diff --git a/backend/models/user.py b/backend/models/user.py
index 676c185..4ea37f5 100644
--- a/backend/models/user.py
+++ b/backend/models/user.py
@@ -114,6 +114,7 @@ class UserOut(BaseModel):
organization_id: Optional[int]
role: Optional[str]
requires_password_update: bool
+ email_verified: bool
class UserUpdate(BaseModel):
diff --git a/backend/routes/user_routes.py b/backend/routes/user_routes.py
index e303162..d501594 100644
--- a/backend/routes/user_routes.py
+++ b/backend/routes/user_routes.py
@@ -58,6 +58,7 @@ async def read_users_me(current_user: User = Depends(get_current_user)):
organization_id=current_user.organization_id,
role=current_user.role,
requires_password_update=current_user.requires_password_update,
+ email_verified=current_user.email_verified,
)
return user_out
diff --git a/backend/utils/email.py b/backend/utils/email.py
index 5ebe617..d693979 100644
--- a/backend/utils/email.py
+++ b/backend/utils/email.py
@@ -34,10 +34,9 @@ async def send_verification_email_with_sendgrid(email: List[str], token: str):
html_content=f'Click on the link to verify your email: https://{APP_HOST}/verify-email',
)
# Disable click tracking in development
- # if APP_ENV == "dev":
- print("Disabling click tracking")
- message.tracking_settings = TrackingSettings()
- message.tracking_settings.click_tracking = ClickTracking(False, False)
+ if APP_ENV == "dev":
+ message.tracking_settings = TrackingSettings()
+ message.tracking_settings.click_tracking = ClickTracking(False, False)
try:
sg = SendGridAPIClient(SENDGRID_API_KEY)
response = await sg.send(message)
diff --git a/frontend/src/components/auth/RequireAuth.jsx b/frontend/src/components/auth/RequireAuth.jsx
index b8a410e..1590012 100644
--- a/frontend/src/components/auth/RequireAuth.jsx
+++ b/frontend/src/components/auth/RequireAuth.jsx
@@ -2,8 +2,10 @@ import { Navigate } from "react-router-dom";
import { useAuth } from "../../contexts/AuthContext";
function RequireAuth({ children }) {
- const { isAuthenticated, isLoading, isEmailVerified } = useAuth();
+ const { isAuthenticated, isLoading, isEmailVerified, loginProcessCompleted } =
+ useAuth();
+ // if (isLoading || !loginProcessCompleted) {
if (isLoading) {
return
Loading...
; // Or your preferred loading indicator/component
}
@@ -13,8 +15,13 @@ function RequireAuth({ children }) {
return ;
}
- if (!isEmailVerified) {
- console.log("from require auth");
+ if (isAuthenticated && !isEmailVerified) {
+ // Redirect to the verify-email page if email is not verified
+ console.log("triggered");
+ return ;
+ }
+
+ if (loginProcessCompleted && !isEmailVerified) {
// Redirect to the verify-email page if email is not verified
return ;
}
diff --git a/frontend/src/contexts/AuthContext.jsx b/frontend/src/contexts/AuthContext.jsx
index 9f4aa22..feca594 100644
--- a/frontend/src/contexts/AuthContext.jsx
+++ b/frontend/src/contexts/AuthContext.jsx
@@ -17,11 +17,11 @@ export const AuthProvider = ({ children }) => {
const [isEmailVerified, setIsEmailVerified] = useState(false); // Add a state for email verification
const [userRole, setUserRole] = useState(null);
const [isLoading, setIsLoading] = useState(true); // Add a loading state
+ const [loginProcessCompleted, setLoginProcessCompleted] = useState(false);
useEffect(() => {
const verifyToken = async () => {
try {
- // Replace '/api/verifyToken' with your actual API endpoint
const response = await axios.get(`${API_URL}verify-token/`);
// Update based on the response message
if (response.data.message === "User is authenticated") {
@@ -29,11 +29,7 @@ export const AuthProvider = ({ children }) => {
const userResponse = await axios.get(`${API_URL}users/me/`);
setUserRole(userResponse.data.role);
-
- const emailResponse = await axios.get(
- `${API_URL}users/is-email-verified/`,
- );
- setIsEmailVerified(emailResponse.data.email_verified);
+ setIsEmailVerified(userResponse.data.email_verified);
} else {
setIsAuthenticated(false);
}
@@ -57,14 +53,21 @@ export const AuthProvider = ({ children }) => {
setIsAuthenticated(newAuthState);
};
- const updateEmailVerification = (newEmailVerificationState) => {
+ const updateEmailVerification = (newEmailVerificationState, callback) => {
setIsEmailVerified(newEmailVerificationState);
+ if (callback) {
+ callback();
+ }
};
const updateUserRole = (newUserRole) => {
setUserRole(newUserRole);
};
+ const markLoginProcessCompleted = (newState) => {
+ setLoginProcessCompleted(newState);
+ };
+
// The Provider component from our created context is used here.
// It makes the `isAuthenticated` state and `updateAuth` function available to any descendants of this component
return (
@@ -77,6 +80,8 @@ export const AuthProvider = ({ children }) => {
updateUserRole,
userRole,
isLoading,
+ loginProcessCompleted,
+ markLoginProcessCompleted,
}}
>
{children}
diff --git a/frontend/src/pages/login/LoginPage.jsx b/frontend/src/pages/login/LoginPage.jsx
index 086e5f7..6de3887 100644
--- a/frontend/src/pages/login/LoginPage.jsx
+++ b/frontend/src/pages/login/LoginPage.jsx
@@ -27,16 +27,19 @@ function LoginPage({ onLogin }) {
updateEmailVerification,
updateUserRole,
isAuthenticated,
+ isEmailVerified,
+ markLoginProcessCompleted,
} = useAuth();
const [errorMessage, setErrorMessage] = useState("");
const location = useLocation();
const [emailVerifiedMessage, setEmailVerifiedMessage] = useState("");
useEffect(() => {
- if (isAuthenticated) {
+ if (isAuthenticated && isEmailVerified) {
+ console.log("Navigating to dashboards");
navigate("/dashboards");
}
- }, [isAuthenticated, navigate]);
+ }, [isAuthenticated, isEmailVerified, navigate]);
useEffect(() => {
if (location.state?.emailVerified) {
@@ -70,24 +73,24 @@ function LoginPage({ onLogin }) {
);
if (response.status === 200) {
- updateAuth(true);
+ markLoginProcessCompleted(true);
const userResponse = await axios.get(`${API_URL}users/me/`, {
headers: {
Authorization: `Bearer ${response.data.access_token}`,
},
});
- console.log(userResponse.data.role);
updateUserRole(userResponse.data.role);
+ updateAuth(true);
if (userResponse.data.requires_password_update) {
navigate("/change-password");
- } else if (userResponse.data.email_verified == false) {
- console.log("email_verified is false");
- navigate("/verify-email");
} else {
- console.log("email_verified is true");
- updateEmailVerification(true);
- navigate("/dashboards");
+ updateEmailVerification(userResponse.data.email_verified);
+ if (userResponse.data.email_verified) {
+ navigate("/dashboards");
+ } else {
+ navigate("/verify-email");
+ }
}
}
} catch (error) {
diff --git a/frontend/src/pages/logout/LogoutPage.jsx b/frontend/src/pages/logout/LogoutPage.jsx
index 40fcd6d..b4feab4 100644
--- a/frontend/src/pages/logout/LogoutPage.jsx
+++ b/frontend/src/pages/logout/LogoutPage.jsx
@@ -6,7 +6,12 @@ import { useAuth } from "../../contexts/AuthContext";
function Logout() {
const navigate = useNavigate();
- const { updateAuth } = useAuth();
+ const {
+ updateAuth,
+ updateEmailVerification,
+ updateUserRole,
+ markLoginProcessCompleted,
+ } = useAuth();
useEffect(() => {
// Clear the authentication cookie
@@ -15,10 +20,19 @@ function Logout() {
// Update authentication state
updateAuth(false);
+ updateEmailVerification(false);
+ updateUserRole(null);
+ markLoginProcessCompleted(false);
// Redirect to the login page
navigate("/");
- }, [navigate, updateAuth]);
+ }, [
+ navigate,
+ updateAuth,
+ updateEmailVerification,
+ updateUserRole,
+ markLoginProcessCompleted,
+ ]);
// Optionally, you can render a message or a spinner here
return Logging out...
;
diff --git a/frontend/src/pages/register/RegisterPage.jsx b/frontend/src/pages/register/RegisterPage.jsx
index a8ec5a1..b91eb8e 100644
--- a/frontend/src/pages/register/RegisterPage.jsx
+++ b/frontend/src/pages/register/RegisterPage.jsx
@@ -40,7 +40,7 @@ function RegisterPage() {
});
// Navigate to the verify-email page instead of login
- navigate("/verify-email", { state: { email } });
+ navigate("/verify-email");
}
} catch (error) {
if (error.response) {
diff --git a/frontend/src/pages/verify-email/VerifyEmailPage.jsx b/frontend/src/pages/verify-email/VerifyEmailPage.jsx
index e1ef0cc..6f21850 100644
--- a/frontend/src/pages/verify-email/VerifyEmailPage.jsx
+++ b/frontend/src/pages/verify-email/VerifyEmailPage.jsx
@@ -1,16 +1,26 @@
import React, { useState, useEffect } from "react";
import { useLocation, useNavigate } from "react-router-dom";
-import { Box, Button, Container, Typography } from "@mui/material";
+import {
+ Alert,
+ Box,
+ Button,
+ Container,
+ Snackbar,
+ Typography,
+} from "@mui/material";
import { useAuth } from "../../contexts/AuthContext";
import { API_URL } from "../../utils/constants";
import axios from "axios";
+import { set } from "date-fns";
const VerifyEmailPage = () => {
const location = useLocation();
const navigate = useNavigate();
- const email = location.state?.email || null;
+ const [userResponse, setUserResponse] = useState(null);
const [token, setToken] = useState(null);
- const { updateAuth, updateEmailVerification } = useAuth();
+ const { updateAuth, updateEmailVerification, loginProcessCompleted } =
+ useAuth();
+ const [openAlert, setOpenAlert] = useState(false);
useEffect(() => {
const params = new URLSearchParams(location.search);
@@ -18,6 +28,25 @@ const VerifyEmailPage = () => {
setToken(token);
}, [location]);
+ useEffect(() => {
+ if (!token) {
+ axios
+ .get(`${API_URL}users/me/`)
+ .then((response) => {
+ if (response.data) {
+ setUserResponse(response.data);
+ } else {
+ // If no user data is returned, navigate to the login page
+ navigate("/login");
+ }
+ })
+ .catch((error) => {
+ // If an error occurs, navigate to the login page
+ navigate("/login");
+ });
+ }
+ }, [token]);
+
useEffect(() => {
if (token) {
console.log("token", token);
@@ -38,7 +67,7 @@ const VerifyEmailPage = () => {
.catch((error) => {
// Handle failed token verification
console.log("Failed to verify token");
- navigate("/login", { state: { emailVerified: true } });
+ navigate("/login");
});
})
.catch((error) => {
@@ -51,10 +80,13 @@ const VerifyEmailPage = () => {
const handleResendEmail = async (event) => {
event.preventDefault();
axios
- .post(`${API_URL}users/send-verification-email/`, { email })
+ .post(`${API_URL}users/send-verification-email/`, {
+ email: userResponse.email,
+ })
.then((response) => {
// Handle successful email resend
console.log("Verification email sent successfully");
+ setOpenAlert(true);
})
.catch((error) => {
// Handle failed email resend
@@ -62,6 +94,13 @@ const VerifyEmailPage = () => {
});
};
+ const handleCloseAlert = (event, reason) => {
+ if (reason === "clickaway") {
+ return;
+ }
+ setOpenAlert(false);
+ };
+
return (
{
>
Resend Verification Email
+
+
+ Verification email has been resent!
+
+
);