Skip to content

Commit

Permalink
implemented basic auth (#5)
Browse files Browse the repository at this point in the history
* implemented auth (register, login, reset password, dashboard). need to figure out correct verfiy email link

* Yarn packages

* adjusted auth component import

---------

Co-authored-by: Kristen Yee <[email protected]>
Co-authored-by: maithyy <[email protected]>
Co-authored-by: michellelin1 <[email protected]>
  • Loading branch information
4 people authored Dec 30, 2023
1 parent 062f12a commit edf2832
Show file tree
Hide file tree
Showing 14 changed files with 837 additions and 6 deletions.
10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@
"prepare": "husky install"
},
"dependencies": {
"firebase": "^10.7.0",
"@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"axios": "^1.6.2",
"firebase": "^10.7.0",
"framer-motion": "^10.16.5",
"lint-staged": "^14.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-cookie": "^6.1.1",
"react-dom": "^18.2.0",
"react-router-dom": "^6.20.0",
"react-script": "^2.0.5"
},
"lint-staged": {
"*.{js,jsx}": "yarn run eslint"
Expand Down Expand Up @@ -64,6 +68,8 @@
"eslint-plugin-react-refresh": "^0.4.3",
"husky": "^8.0.3",
"prettier": "^3.0.3",
"react-app-env": "^1.2.3",
"react-error-overlay": "6.0.9",
"vite": "^4.4.5"
}
}
42 changes: 38 additions & 4 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,45 @@
import './App.css';
import { ChakraProvider, Text } from '@chakra-ui/react'
// import from 'react';
import { ChakraProvider } from '@chakra-ui/react'
import { Route, Routes, BrowserRouter as Router } from 'react-router-dom';
import { CookiesProvider } from 'react-cookie';
import Login from './components/Authentication/Login';
import Logout from './components/Authentication/Logout';
import Register from './components/Authentication/register';
import Dashboard from './pages/Dashboard/Dashboard';
import ForgotPassword from './components/Authentication/ForgotPassword';
import EmailAction from './components/Authentication/EmailAction';
import AUTH_ROLES from './utils/auth_config';
import ProtectedRoute from './utils/ProtectedRoute';

const { ADMIN_ROLE, USER_ROLE } = AUTH_ROLES.AUTH_ROLES;

const App = () => {
return (
<ChakraProvider>
<Text>Hello World</Text>
</ChakraProvider>
<ChakraProvider>
<CookiesProvider>
<Router>
<Routes>
<Route exact path="/" element={<Login />} />
<Route exact path="/logout" element={<Logout />} />
<Route exact path="/forgotpassword" element={<ForgotPassword />} />
<Route exact path="/register" element={<Register />} />
<Route exact path="/emailAction" element={<EmailAction redirectPath="/" />} />
<Route
exact
path="/dashboard"
element={
<ProtectedRoute
Component={Dashboard}
redirectPath="/"
roles={[ADMIN_ROLE, USER_ROLE]}
/>
}
/>
</Routes>
</Router>
</CookiesProvider>
</ChakraProvider>
);
};

Expand Down
22 changes: 22 additions & 0 deletions src/components/Authentication/EmailAction.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// import React from 'react';
import { useLocation, Navigate } from 'react-router-dom';
import PropTypes from 'prop-types';
import ResetPassword from './ResetPassword';
import VerifyEmail from './VerifyEmail';

const EmailAction = ({ redirectPath }) => {
const { search } = useLocation();
const mode = new URLSearchParams(search).get('mode');
const code = new URLSearchParams(search).get('oobCode');

if (code === null) {
return <Navigate to={redirectPath} />;
}
return mode === 'resetPassword' ? <ResetPassword code={code} /> : <VerifyEmail code={code} />;
};

EmailAction.propTypes = {
redirectPath: PropTypes.string.isRequired,
};

export default EmailAction;
43 changes: 43 additions & 0 deletions src/components/Authentication/ForgotPassword.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useState } from 'react';
import { sendPasswordReset } from '../../utils/auth_utils';

const ForgotPassword = () => {
const [email, setEmail] = useState();
const [errorMessage, setErrorMessage] = useState();
const [confirmationMessage, setConfirmationMessage] = useState();

const handleForgotPassword = async e => {
try {
e.preventDefault();
await sendPasswordReset(email);
setConfirmationMessage(
'If the email entered is associated with an account, you should receive an email to reset your password shortly.',
);
setErrorMessage('');
setEmail('');
} catch (err) {
setErrorMessage(err.message);
}
};

return (
<div>
<h2>Send Reset Email</h2>
{errorMessage && <p>{errorMessage}</p>}
<form onSubmit={handleForgotPassword}>
<input
type="text"
value={email}
onChange={({ target }) => setEmail(target.value)}
placeholder="Email"
/>
<br />
<button type="submit">Send Email</button>
</form>
{confirmationMessage && <p>{confirmationMessage}</p>}
<a href="/">Back to Login</a>
</div>
);
};

export default ForgotPassword;
62 changes: 62 additions & 0 deletions src/components/Authentication/Login.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useState } from 'react';
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';

const Login = ({ cookies }) => {
const navigate = useNavigate();
const [email, setEmail] = useState();
const [password, setPassword] = useState();
const [errorMessage, setErrorMessage] = useState();

const handleStdLogin = async e => {
try {
e.preventDefault();
await logInWithEmailAndPassword(email, password, '/dashboard', navigate, cookies);
} catch (err) {
setErrorMessage(err.message);
}
};

// const handleGoogleLogin = async e => {
// try {
// e.preventDefault();
// await signInWithGoogle('/new-user', '/dashboard', navigate, cookies);
// } catch (err) {
// setErrorMessage(err.message);
// }
// };

return (
<div>
<h2>Login</h2>
{errorMessage && <p>{errorMessage}</p>}
<form onSubmit={handleStdLogin}>
<input type="text" onChange={({ target }) => setEmail(target.value)} placeholder="Email" />
<br />
<input
type="password"
onChange={({ target }) => setPassword(target.value)}
placeholder="Password"
/>
<br />
<a href="/forgotpassword">Forgot Password</a>
<br />
<button type="submit">Sign In</button>
</form>

{/* <br />
<form onSubmit={handleGoogleLogin}>
<button type="submit">Sign In with Google</button>
</form>
<br /> */}
</div>
);
};

Login.propTypes = {
cookies: instanceOf(Cookies).isRequired,
};

export default withCookies(Login);
33 changes: 33 additions & 0 deletions src/components/Authentication/Logout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useState } from 'react';
import { instanceOf } from 'prop-types';
import { logout, useNavigate } from '../../utils/auth_utils';
import { Cookies, withCookies } from '../../utils/cookie_utils';

const Logout = ({ cookies }) => {
const navigate = useNavigate();
const [errorMessage, setErrorMessage] = useState();

const handleLogout = async () => {
try {
await logout('/', navigate, cookies);
} catch (err) {
setErrorMessage(err.message);
}
};

return (
<div>
<h2>Logout</h2>
{errorMessage && <p>{errorMessage}</p>}
<button type="submit" onClick={handleLogout}>
Logout
</button>
</div>
);
};

Logout.propTypes = {
cookies: instanceOf(Cookies).isRequired,
};

export default withCookies(Logout);
59 changes: 59 additions & 0 deletions src/components/Authentication/ResetPassword.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useState } from 'react';
import PropTypes from 'prop-types';
import { confirmNewPassword } from '../../utils/auth_utils';

const ResetPassword = ({ code }) => {
const [password, setPassword] = useState();
const [checkPassword, setCheckPassword] = useState();
const [errorMessage, setErrorMessage] = useState();
const [confirmationMessage, setConfirmationMessage] = useState();
const handleResetPassword = async e => {
try {
e.preventDefault();
if (password !== checkPassword) {
throw new Error("Passwords don't match");
}
await confirmNewPassword(code, password);
setConfirmationMessage('Password changed. You can now sign in with your new password.');
setErrorMessage('');
setPassword('');
} catch (err) {
setErrorMessage(err.message);
}
};
return (
<div>
<h2>Reset Password</h2>
{errorMessage && <p>{errorMessage}</p>}
{!confirmationMessage && (
<form onSubmit={handleResetPassword}>
<input
type="password"
onChange={({ target }) => setPassword(target.value)}
placeholder="New Password"
/>
<br />
<input
type="password"
onChange={({ target }) => setCheckPassword(target.value)}
placeholder="Re-enter Password"
/>
<br />
<button type="submit">Reset Password</button>
</form>
)}
{confirmationMessage && (
<div>
<p>{confirmationMessage}</p>
<a href="/">Back to Login</a>
</div>
)}
</div>
);
};

ResetPassword.propTypes = {
code: PropTypes.string.isRequired,
};

export default ResetPassword;
43 changes: 43 additions & 0 deletions src/components/Authentication/VerifyEmail.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { confirmVerifyEmail } from '../../utils/auth_utils';

const VerifyEmail = ({ code }) => {
const [errorMessage, setErrorMessage] = useState();
const [confirmationMessage, setConfirmationMessage] = useState();

useEffect(() => {
const verifyEmail = async () => {
try {
await confirmVerifyEmail(code);
setConfirmationMessage(
'Your email has been verified. You can now sign in with your new account.',
);
setErrorMessage('');
} catch (err) {
setErrorMessage(err.message);
}
};

verifyEmail();
}, [code]);

return (
<div>
<h2>Verify Email</h2>
{errorMessage && <p>{errorMessage}</p>}
{confirmationMessage && (
<div>
<p>{confirmationMessage}</p>
<a href="/">Back to Login</a>
</div>
)}
</div>
);
};

VerifyEmail.propTypes = {
code: PropTypes.string.isRequired,
};

export default VerifyEmail;
Loading

0 comments on commit edf2832

Please sign in to comment.