From 8a8d5503f55662023916d30695fc52a53a092d3a Mon Sep 17 00:00:00 2001 From: ctc-devops <90984711+ctc-devops@users.noreply.github.com> Date: Mon, 26 Feb 2024 12:05:41 -0800 Subject: [PATCH 1/3] Create a pull trequest for branch 43-login-match-high-fidelity From 5b2217395c75087fc645641fc382c079b19f3360 Mon Sep 17 00:00:00 2001 From: Cheryl Chen Date: Mon, 26 Feb 2024 13:38:48 -0800 Subject: [PATCH 2/3] finished login and forgot password pages --- .../Authentication/ForgotPassword.jsx | 96 +++++++-- src/components/Authentication/Login.jsx | 92 ++++----- src/components/Authentication/SignUp.jsx | 186 ++++++++++++------ 3 files changed, 259 insertions(+), 115 deletions(-) diff --git a/src/components/Authentication/ForgotPassword.jsx b/src/components/Authentication/ForgotPassword.jsx index 1da3c5c..51e5cba 100644 --- a/src/components/Authentication/ForgotPassword.jsx +++ b/src/components/Authentication/ForgotPassword.jsx @@ -1,5 +1,7 @@ import { useState } from 'react'; import { sendPasswordReset } from '../../utils/auth_utils'; +import { FormControl, Input, Button, Center, Link } from '@chakra-ui/react'; + const ForgotPassword = () => { const [email, setEmail] = useState(); @@ -21,22 +23,94 @@ const ForgotPassword = () => { }; return ( -
-

Send Reset Email

+
+
+

Reset Password

+

Enter email address associated with account

{errorMessage &&

{errorMessage}

}
- setEmail(target.value)} - placeholder="Email" - /> -
- + +
+ setEmail(target.value)} + placeholder="Email Address" + borderColor={"#CBD5E0"} + borderRadius= '3px' + /> +
+ + +
+ + + + + + +
+
{confirmationMessage &&

{confirmationMessage}

} - Back to Login
+
); }; diff --git a/src/components/Authentication/Login.jsx b/src/components/Authentication/Login.jsx index 7fb2d1b..6cace22 100644 --- a/src/components/Authentication/Login.jsx +++ b/src/components/Authentication/Login.jsx @@ -3,7 +3,7 @@ import { instanceOf } from 'prop-types'; import { Cookies, withCookies } from '../../utils/cookie_utils'; import { logInWithEmailAndPassword, useNavigate } from '../../utils/auth_utils'; // import { logInWithEmailAndPassword , signInWithGoogle, useNavigate } from '../utils/auth_utils'; -import { FormControl, Input, Button, Center } from '@chakra-ui/react'; +import { FormControl, Input, Button, Center, Link } from '@chakra-ui/react'; const Login = ({ cookies }) => { const navigate = useNavigate(); const [email, setEmail] = useState(); @@ -22,7 +22,7 @@ const Login = ({ cookies }) => { }; return ( -
+
{ gap: '25px', }} > -

Welcome. Please enter login information.

+

Sign In

+

Please enter login information.

{errorMessage &&

{errorMessage}

}
setEmail(target.value)} placeholder="Email" + borderColor={"#CBD5E0"} + borderRadius= '3px' /> setPassword(target.value)} placeholder="Password" + borderColor={"#CBD5E0"} + borderRadius= '3px' />
@@ -58,62 +63,61 @@ const Login = ({ cookies }) => { marginBottom: '25px', }} > + + + -
- + Forgot Password
- - {/*

Welcome. Please enter login information.

- {errorMessage &&

{errorMessage}

} */} - {/*
- setEmail(target.value)} placeholder="Email" /> -
- setPassword(target.value)} - placeholder="Password" - /> -
- Forgot Password -
- -
*/} - - {/*
-
- -
-
*/}
); diff --git a/src/components/Authentication/SignUp.jsx b/src/components/Authentication/SignUp.jsx index ba62321..3ab0579 100644 --- a/src/components/Authentication/SignUp.jsx +++ b/src/components/Authentication/SignUp.jsx @@ -1,73 +1,139 @@ import { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { registerWithEmailAndPassword } from '../../utils/auth_utils'; -import { sendEmail } from '../EmailTemplates/EmailSending'; -import emailtemplate from '../EmailTemplates/emailtemplate'; +// import { useNavigate } from 'react-router-dom'; +// import { registerWithEmailAndPassword } from '../../utils/auth_utils'; +// import { sendEmail } from '../EmailTemplates/EmailSending'; +// import emailtemplate from '../EmailTemplates/emailtemplate'; // import { renderEmail } from 'react-html-email'; +import { FormControl, Button, Center } from '@chakra-ui/react'; + const SignUp = () => { - const [email, setEmail] = useState(); - const [password, setPassword] = useState(); - const [checkPassword, setCheckPassword] = useState(); - const [errorMessage, setErrorMessage] = useState(); + // const [email, setEmail] = useState(); + // const [password, setPassword] = useState(); + // const [checkPassword, setCheckPassword] = useState(); + // const [errorMessage, setErrorMessage] = useState(); + + const [userType, setUserType] = useState(null); + //const [role, setRole] = useState(); - const navigate = useNavigate(); + // const navigate = useNavigate(); - const handleSubmit = async (e) => { - e.preventDefault(); - try { - if (password !== checkPassword) { - throw new Error("Passwords don't match"); - } + // const handleSubmit = async (e) => { + // e.preventDefault(); + // try { + // if (password !== checkPassword) { + // throw new Error("Passwords don't match"); + // } - // register email and password - await registerWithEmailAndPassword(email, password, 'admin', navigate, '/'); + // // register email and password + // await registerWithEmailAndPassword(email, password, 'admin', navigate, '/'); - // send email to Debbie - const subject = "placeholder"; - const newEmail = email; - await sendEmail(subject, newEmail, emailtemplate); - - } catch (error) { - setErrorMessage(error.message); - } - }; + // // send email to Debbie + // const subject = "placeholder"; + // const newEmail = email; + // await sendEmail(subject, newEmail, emailtemplate); + + + // // setUserType("nut null") + // } catch (error) { + // setErrorMessage(error.message); + // } + // }; + if (userType === null) { + return ( +
+
+

Create Account

+

Select account type:

+ {/* {errorMessage &&

{errorMessage}

} */} +
+ + +
+ + + +
+ + + - return ( -
-

Sign Up

- - setEmail(target.value)} - placeholder="Email" - /> -
- {/* setRole(target.value)} placeholder="Role" /> -
*/} - setPassword(target.value)} - placeholder="Password" - type="password" - /> -
- setCheckPassword(target.value)} - placeholder="Re-enter Password" - type="password" - /> -
- - {/*
- -
*/} - -

{errorMessage}

-
- ); +
+ +
+
+ ); + } + else { + return ( +
+
+

admin

+
+
+ ); + } }; export default SignUp; From dcb23f7b8eeea7067404bdc62b8c093554040c5a Mon Sep 17 00:00:00 2001 From: Cheryl Chen Date: Mon, 4 Mar 2024 12:04:05 -0800 Subject: [PATCH 3/3] matched login high-fidelity, added first and last name, protect pages when users not approved --- src/App.jsx | 2 + .../Authentication/AwaitConfirmation.jsx | 34 +++ .../Authentication/ForgotPassword.jsx | 34 +-- src/components/Authentication/Login.jsx | 187 ++++++++------ src/components/Authentication/SignUp.jsx | 243 ++++++++++++++---- src/utils/ProtectedRoute.jsx | 3 +- src/utils/auth_utils.js | 30 ++- src/utils/cookie_utils.js | 1 + 8 files changed, 360 insertions(+), 174 deletions(-) create mode 100644 src/components/Authentication/AwaitConfirmation.jsx diff --git a/src/App.jsx b/src/App.jsx index ed55008..3321d4a 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -7,6 +7,7 @@ import Logout from './components/Authentication/Logout'; import SignUp from './components/Authentication/SignUp'; import ForgotPassword from './components/Authentication/ForgotPassword'; import EmailAction from './components/Authentication/EmailAction'; +import AwaitConfirmation from './components/Authentication/AwaitConfirmation'; import AUTH_ROLES from './utils/auth_config'; import ProtectedRoute from './utils/ProtectedRoute'; import Catalog from './pages/Catalog/Catalog'; @@ -50,6 +51,7 @@ const App = () => { } /> } /> } /> + } /> { + + + return ( +
+ + Account Pending Approval +
+ + + +
+
+
+ ) +}; + +export default AwaitConfirmation; diff --git a/src/components/Authentication/ForgotPassword.jsx b/src/components/Authentication/ForgotPassword.jsx index 51e5cba..a368cdf 100644 --- a/src/components/Authentication/ForgotPassword.jsx +++ b/src/components/Authentication/ForgotPassword.jsx @@ -1,6 +1,6 @@ import { useState } from 'react'; import { sendPasswordReset } from '../../utils/auth_utils'; -import { FormControl, Input, Button, Center, Link } from '@chakra-ui/react'; +import { FormControl, Input, Button, Center, Link, Box, Heading, Text} from '@chakra-ui/react'; const ForgotPassword = () => { @@ -24,22 +24,20 @@ const ForgotPassword = () => { return (
-
-

Reset Password

-

Enter email address associated with account

+ Reset Password + Enter email address associated with account {errorMessage &&

{errorMessage}

}
-
+ { borderColor={"#CBD5E0"} borderRadius= '3px' /> -
+ -
{ backgroundColor={'#FFFFFF'} color={'#155696'} variant='outline' - onMouseOver={(e) => { - e.target.style.backgroundColor = '#E0E0E0'; - }} - onMouseOut={(e) => { - e.target.style.backgroundColor = '#FFFFFF'; - }} + _hover={{backgroundColor: '#E0E0E0'}} > Cancel @@ -96,20 +89,15 @@ const ForgotPassword = () => { }} backgroundColor={'#243268'} color={'#ffffff'} - onMouseOver={(e) => { - e.target.style.backgroundColor = '#1A2559'; - }} - onMouseOut={(e) => { - e.target.style.backgroundColor = '#243268'; - }} + _hover={{backgroundColor: '#1A2559'}} > Send Instructions -
+
{confirmationMessage &&

{confirmationMessage}

} -
+
); }; diff --git a/src/components/Authentication/Login.jsx b/src/components/Authentication/Login.jsx index 6cace22..54d1f60 100644 --- a/src/components/Authentication/Login.jsx +++ b/src/components/Authentication/Login.jsx @@ -3,123 +3,142 @@ import { instanceOf } from 'prop-types'; import { Cookies, withCookies } from '../../utils/cookie_utils'; import { logInWithEmailAndPassword, useNavigate } from '../../utils/auth_utils'; // import { logInWithEmailAndPassword , signInWithGoogle, useNavigate } from '../utils/auth_utils'; -import { FormControl, Input, Button, Center, Link } from '@chakra-ui/react'; +import { Box, Heading, Text, FormControl, Input, Button, Center, Link, Alert, AlertDescription } from '@chakra-ui/react'; +// import styles from + const Login = ({ cookies }) => { const navigate = useNavigate(); const [email, setEmail] = useState(); const [password, setPassword] = useState(); + const [hasError, setHasError] = useState(false); const [errorMessage, setErrorMessage] = useState(); const handleStdLogin = async e => { try { - console.log('logging in...'); e.preventDefault(); await logInWithEmailAndPassword(email, password, '/publishedSchedule', navigate, cookies); window.location.reload(true); } catch (err) { + setHasError(true); setErrorMessage(err.message); } }; return ( -
-
-

Sign In

-

Please enter login information.

- {errorMessage &&

{errorMessage}

} -
- -
- setEmail(target.value)} - placeholder="Email" - borderColor={"#CBD5E0"} - borderRadius= '3px' - /> - setPassword(target.value)} - placeholder="Password" - borderColor={"#CBD5E0"} - borderRadius= '3px' - /> -
+ + + { hasError && + + { errorMessage } + + } + +
+ + Sign In + Please enter login information. + + + + setEmail(target.value)} + placeholder="Email" + borderColor={"#CBD5E0"} + borderRadius= '3px' + /> + setPassword(target.value)} + placeholder="Password" + borderColor={"#CBD5E0"} + borderRadius= '3px' + /> + -
- + + + + - - -
+
- - - -
-
+ + + Forgot Password + + + + + + + ); }; diff --git a/src/components/Authentication/SignUp.jsx b/src/components/Authentication/SignUp.jsx index 3ab0579..5d68d11 100644 --- a/src/components/Authentication/SignUp.jsx +++ b/src/components/Authentication/SignUp.jsx @@ -1,49 +1,70 @@ import { useState } from 'react'; -// import { useNavigate } from 'react-router-dom'; -// import { registerWithEmailAndPassword } from '../../utils/auth_utils'; -// import { sendEmail } from '../EmailTemplates/EmailSending'; -// import emailtemplate from '../EmailTemplates/emailtemplate'; -// import { renderEmail } from 'react-html-email'; -import { FormControl, Button, Center } from '@chakra-ui/react'; +import { useNavigate } from 'react-router-dom'; +import { registerWithEmailAndPassword } from '../../utils/auth_utils'; +import { sendEmail } from '../EmailTemplates/EmailSending'; +import emailtemplate from '../EmailTemplates/emailtemplate'; +import { Box, Heading, Text, FormControl, Button, Center, Link, Input, Alert, AlertDescription } from '@chakra-ui/react'; +import AUTH_ROLES from '../../utils/auth_config'; +const { USER_ROLE } = AUTH_ROLES.AUTH_ROLES; const SignUp = () => { - // const [email, setEmail] = useState(); - // const [password, setPassword] = useState(); - // const [checkPassword, setCheckPassword] = useState(); - // const [errorMessage, setErrorMessage] = useState(); + const [firstName, setFirstName] = useState(); + const [lastName, setLastName] = useState(); + const [email, setEmail] = useState(); + const [password, setPassword] = useState(); + const [checkPassword, setCheckPassword] = useState(); + const [errorMessage, setErrorMessage] = useState(); + const [hasError, setHasError] = useState(false); const [userType, setUserType] = useState(null); + const [submitted, setSubmitted] = useState(false); - //const [role, setRole] = useState(); - // const navigate = useNavigate(); + const navigate = useNavigate(); - // const handleSubmit = async (e) => { - // e.preventDefault(); - // try { - // if (password !== checkPassword) { - // throw new Error("Passwords don't match"); - // } - - // // register email and password - // await registerWithEmailAndPassword(email, password, 'admin', navigate, '/'); + const handleSubmit = async (e) => { + e.preventDefault(); + try { + if (firstName === undefined) { + throw new Error("Must enter a first name."); + } + if (lastName === undefined) { + throw new Error("Must enter a last name."); + } + if (email === undefined) { + throw new Error("Must enter an email"); + } + if (password === undefined) { + throw new Error("Must enter a password."); + } + if (password !== checkPassword) { + throw new Error("Passwords don't match."); + } + if (password.length < 6) { + throw new Error("Password must be at least 6 characters."); + } - // // send email to Debbie - // const subject = "placeholder"; - // const newEmail = email; - // await sendEmail(subject, newEmail, emailtemplate); + // register email and password + await registerWithEmailAndPassword(email, password, USER_ROLE, navigate, '/awaitConfirmation', firstName, lastName); + // send email to Debbie + const subject = "placeholder"; + const newEmail = email; + await sendEmail(subject, newEmail, emailtemplate); + + setSubmitted(true); - // // setUserType("nut null") - // } catch (error) { - // setErrorMessage(error.message); - // } - // }; + } catch (error) { + console.log(error); + setHasError(true) + setErrorMessage(error.message); + } + }; if (userType === null) { return (
-
{ gap: '25px', }} > -

Create Account

-

Select account type:

- {/* {errorMessage &&

{errorMessage}

} */} + Create Account + Select account type:
-
{ }} backgroundColor={'#243268'} color={'#ffffff'} - onMouseOver={(e) => { - e.target.style.backgroundColor = '#1A2559'; - }} - onMouseOut={(e) => { - e.target.style.backgroundColor = '#243268'; - }} + _hover={{backgroundColor: '#1A2559'}} > Admin @@ -101,37 +116,155 @@ const SignUp = () => { }} backgroundColor={'#243268'} color={'#ffffff'} - onMouseOver={(e) => { - e.target.style.backgroundColor = '#1A2559'; - }} - onMouseOut={(e) => { - e.target.style.backgroundColor = '#243268'; - }} + _hover={{backgroundColor: '#1A2559'}} > Student -
+ - +
-
+
); } - else { + else if (userType != null && submitted === false) { return ( + + + { hasError && + + { errorMessage } + + } +
-
-

admin

-
+ + Create Account + Please enter the required information: +
+ + + setFirstName(target.value)} + placeholder="First Name" + borderColor={"#CBD5E0"} + borderRadius= '3px' + /> + setLastName(target.value)} + placeholder="Last Name" + borderColor={"#CBD5E0"} + borderRadius= '3px' + /> + setEmail(target.value)} + placeholder="Email" + borderColor={"#CBD5E0"} + borderRadius= '3px' + /> + setPassword(target.value)} + placeholder="Password" + borderColor={"#CBD5E0"} + borderRadius= '3px' + /> + setCheckPassword(target.value)} + placeholder="Confirm Password" + borderColor={"#CBD5E0"} + borderRadius= '3px' + /> + + + + + + + + + + + + Forgot Password + + + +
+
+
); } }; diff --git a/src/utils/ProtectedRoute.jsx b/src/utils/ProtectedRoute.jsx index 4a08784..3f0fbe9 100644 --- a/src/utils/ProtectedRoute.jsx +++ b/src/utils/ProtectedRoute.jsx @@ -11,7 +11,8 @@ const userIsAuthenticated = async (roles, cookies) => { return false; } const loggedIn = await NPOBackend.get(`/auth/verifyToken/${accessToken}`); - return roles.includes(cookies.get(cookieKeys.ROLE)) && loggedIn.status === 200; + + return roles.includes(cookies.get(cookieKeys.ROLE)) && loggedIn.status === 200 && cookies.get(cookieKeys.APPROVED); // return roles.includes(cookies.get(cookieKeys.ROLE)); } catch (err) { console.log(err); diff --git a/src/utils/auth_utils.js b/src/utils/auth_utils.js index 9c07f82..5f9ba3a 100644 --- a/src/utils/auth_utils.js +++ b/src/utils/auth_utils.js @@ -109,14 +109,14 @@ const refreshToken = async () => { * @param {string} password */ -const createUserInDB = async (email, id, type, signUpWithGoogle, password = null) => { +const createUserInDB = async (email, id, type, signUpWithGoogle, password = null, firstName, lastName) => { try { if (signUpWithGoogle) { - await NPOBackend.post('/users/create', { email, id, type, approved: false }); + await NPOBackend.post('/users/create', { email, id, type, approved: false, approvedOn: (new Date()).getDate(), firstName, lastName }); //old b // await NPOBackend.post('/users/', { email, userId, role, registered: false }); } else { - await NPOBackend.post('/users/create', { email, id, type, approved: false }); + await NPOBackend.post('/users/create', { email, id, type, approved: false, approvedOn: new Date(), firstName, lastName }); // await NPOBackend.post('/users/', { email, userId, role, registered: true }); } } catch (err) { @@ -151,6 +151,8 @@ const signInWithGoogle = async (newUserRedirectPath, defaultRedirectPath, naviga } else { const user = await NPOBackend.get(`/users/${auth.currentUser.uid}`); cookies.set(cookieKeys.ROLE, user.data.user.role, cookieConfig); + cookies.set(cookieKeys.APPROVED, user.data.user.approved, cookieConfig); + if (!user.data.user.registered) { navigate(newUserRedirectPath); } else { @@ -190,12 +192,14 @@ const logInWithEmailAndPassword = async (email, password, redirectPath, navigate //GET req to NPOBackend based on user identity from firebase const user = await NPOBackend.get(`/users/${auth.currentUser.uid}`); - //if approved status is false, tell user to wait for approval if (!user.data[0].approved) { throw new Error('Your account is currently under review. Please wait for approval.'); - } + } + cookies.set(cookieKeys.ROLE, user.data[0].type, cookieConfig); + cookies.set(cookieKeys.APPROVED, user.data[0].approved, cookieConfig); + navigate(redirectPath); }; @@ -217,10 +221,10 @@ const createUserInFirebase = async (email, password) => { * @param {string} role * @returns A UserCredential object from firebase */ -const createUser = async (email, password, role) => { +const createUser = async (email, password, role, firstName, lastName) => { const user = await createUserInFirebase(email, password); //when the user creates an acc add row to the users table where approved = false - await createUserInDB(email, user.uid, role, false, password); + await createUserInDB(email, user.uid, role, false, password, firstName, lastName); sendEmailVerification(user); }; @@ -232,9 +236,13 @@ const createUser = async (email, password, role) => { * @param {hook} navigate An instance of the useNavigate hook from react-router-dom * @param {string} redirectPath path to redirect users once logged in */ -const registerWithEmailAndPassword = async (email, password, role, navigate, redirectPath) => { - await createUser(email, password, role); +const registerWithEmailAndPassword = async (email, password, role, navigate, redirectPath, firstName, lastName) => { + await createUser(email, password, role, firstName, lastName); + // try logging out here + // await signOut(auth); + // clearCookies(document.cookie); navigate(redirectPath); + // window.location.reload(true); }; /** @@ -249,11 +257,11 @@ const sendPasswordReset = async email => { * Sends password reset to new account created with stated email * @param {string} email The email to create an account with */ -const sendInviteLink = async (email, role) => { +const sendInviteLink = async (email, role, firstName, lastName) => { // generate a random password (not going to be used as new account will reset password) const randomPassword = Math.random().toString(36).slice(-8); const user = await createUserInFirebase(email, randomPassword); - createUserInDB(email, user.uid, role, false, randomPassword); + createUserInDB(email, user.uid, role, false, randomPassword, firstName, lastName); sendPasswordReset(email); }; diff --git a/src/utils/cookie_utils.js b/src/utils/cookie_utils.js index efa4560..f266f24 100644 --- a/src/utils/cookie_utils.js +++ b/src/utils/cookie_utils.js @@ -18,6 +18,7 @@ const cookieConfig = { const cookieKeys = { ACCESS_TOKEN: 'accessToken', ROLE: 'role', + APPROVED: 'userID', }; /**