Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forgot password backend logic + frontend design #73

Merged
merged 11 commits into from
Nov 11, 2024
1 change: 0 additions & 1 deletion backend/rest/authRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { generate } from "generate-password";
import {
getAccessToken,
isAuthorizedByEmail,

Check failure on line 6 in backend/rest/authRoutes.ts

View workflow job for this annotation

GitHub Actions / run-lint

'isAuthorizedByEmail' is defined but never used
isAuthorizedByUserId,
isAuthorizedByRole,
isFirstTimeInvitedUser,
Expand Down Expand Up @@ -144,17 +144,16 @@
);

/* Emails a password reset link to the user with the specified email */
authRouter.post(

Check failure on line 147 in backend/rest/authRoutes.ts

View workflow job for this annotation

GitHub Actions / run-lint

Replace `⏎··"/resetPassword/:email",⏎·` with `"/resetPassword/:email",`
"/resetPassword/:email",
isAuthorizedByEmail("email"),
async (req, res) => {
try {

Check failure on line 150 in backend/rest/authRoutes.ts

View workflow job for this annotation

GitHub Actions / run-lint

Replace `····` with `··`
await authService.resetPassword(req.params.email);

Check failure on line 151 in backend/rest/authRoutes.ts

View workflow job for this annotation

GitHub Actions / run-lint

Delete `··`
res.status(204).send();

Check failure on line 152 in backend/rest/authRoutes.ts

View workflow job for this annotation

GitHub Actions / run-lint

Delete `··`
} catch (error: unknown) {

Check failure on line 153 in backend/rest/authRoutes.ts

View workflow job for this annotation

GitHub Actions / run-lint

Delete `··`
res.status(500).json({ error: getErrorMessage(error) });

Check failure on line 154 in backend/rest/authRoutes.ts

View workflow job for this annotation

GitHub Actions / run-lint

Delete `··`
}

Check failure on line 155 in backend/rest/authRoutes.ts

View workflow job for this annotation

GitHub Actions / run-lint

Delete `··`
},

Check failure on line 156 in backend/rest/authRoutes.ts

View workflow job for this annotation

GitHub Actions / run-lint

Replace `··},⏎` with `}`
);

authRouter.post("/isUserVerified/:email", async (req, res) => {
Expand Down
12 changes: 2 additions & 10 deletions frontend/src/APIClients/AuthAPIClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,9 @@ const signup = async (
}
};

const resetPassword = async (email: string | undefined): Promise<boolean> => {
const bearerToken = `Bearer ${getLocalStorageObjProperty(
AUTHENTICATED_USER_KEY,
"accessToken",
)}`;
const resetPassword = async (email: string): Promise<boolean> => {
try {
await baseAPIClient.post(
`/auth/resetPassword/${email}`,
{},
{ headers: { Authorization: bearerToken } },
);
await baseAPIClient.post(`/auth/resetPassword/${email}`, {});
return true;
} catch (error) {
return false;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/APIClients/BaseAPIClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ baseAPIClient.interceptors.request.use(async (config: AxiosRequestConfig) => {
decodedToken.exp <= Math.round(new Date().getTime() / 1000))
) {
const { data } = await axios.post(
`${process.env.REACT_APP_BACKEND_URL}/auth/refresh`,
`${process.env.REACT_APP_BACKEND_URL}auth/refresh`,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmmm this might break things 🤔
I have REACT_APP_BACKEND_URL="http://localhost:8080" as my env variable, which wouldn't pair well without the /

{},
{ withCredentials: true },
);
Expand Down
164 changes: 93 additions & 71 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import "bootstrap/dist/css/bootstrap.min.css";
import { CssBaseline } from "@mui/material";
import { createTheme, ThemeProvider } from "@mui/material/styles";
import React, { useState, useReducer, useEffect } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Welcome from "./components/pages/Welcome";
Expand All @@ -22,12 +24,26 @@
import { AuthenticatedUser } from "./types/AuthTypes";
import authAPIClient from "./APIClients/AuthAPIClient";
import * as Routes from "./constants/Routes";
import ManageUserPage from "./components/pages/ManageUserPage";
import { SocketProvider } from "./contexts/SocketContext";

import ManageUserPage from "./components/pages/ManageUserPage";
import MakeHelpRequestPage from "./components/pages/MakeHelpRequestPage";
import ViewHelpRequestsPage from "./components/pages/ViewHelpRequestsPage";
import HelpRequestPage from "./components/pages/HelpRequestPage";
import CreatePasswordPage from "./components/pages/CreatePasswordPage";
import ForgotPasswordPage from "./components/auth/forgot_password/ForgotPasswordPage";

// Add Lexend Deca font globally
const link = document.createElement("link");
link.href = "https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@400;600&display=swap";

Check warning on line 38 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / run-lint

Insert `⏎·`
link.rel = "stylesheet";
document.head.appendChild(link);

const theme = createTheme({
typography: {
fontFamily: "'Lexend Deca', sans-serif",
},
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Design question - is Lexend Deca the only font used throughout our app?
If yes, I think it would be better to plop this theming into frontend/src/theme/theme.ts :)
If no... then we might need to get more creative lol


const App = (): React.ReactElement => {
const currentUser: AuthenticatedUser | null =
Expand All @@ -35,7 +51,8 @@

const [authenticatedUser, setAuthenticatedUser] =
useState<AuthenticatedUser | null>(currentUser);

Check warning on line 54 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / run-lint

Delete `⏎··`

// Some sort of global state. Context API replaces redux.
// Split related states into different contexts as necessary.
// Split dispatcher and state into separate contexts as necessary.
Expand All @@ -60,77 +77,82 @@

return (
<SampleContext.Provider value={sampleContext}>
<SampleContextDispatcherContext.Provider
value={dispatchSampleContextUpdate}
>
<AuthContext.Provider
value={{ authenticatedUser, setAuthenticatedUser }}
<ThemeProvider theme={theme}>
<CssBaseline />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have ThemeProvider in index.tsx that's grabbing the theme from frontend/src/theme/theme.ts already. It'd be best to only have this in one place, so adding onto my comment above - if we can put the new font in our existing theme, then we can use our existing theming!

<SampleContextDispatcherContext.Provider
value={dispatchSampleContextUpdate}
>
<SocketProvider id={authenticatedUser?.id}>
<Router>
<Switch>
<Route exact path={Routes.WELCOME_PAGE} component={Welcome} />
<Route exact path={Routes.LOGIN_PAGE} component={Login} />
<Route exact path={Routes.SIGNUP_PAGE} component={Signup} />
<PrivateRoute
exact
path={Routes.HOME_PAGE}
component={Default}
allowedRoles={["Administrator", "Facilitator", "Learner"]}
/>
<PrivateRoute
exact
path={Routes.MY_ACCOUNT_PAGE}
component={MyAccount}
allowedRoles={["Administrator", "Facilitator", "Learner"]}
/>
<PrivateRoute
exact
path={Routes.CREATE_MODULE_PAGE}
component={CreateModulePage}
allowedRoles={["Administrator"]}
/>
<Route
exact
path={Routes.NOT_AUTHORIZED_PAGE}
component={NotAuthorized}
/>
<PrivateRoute
exact
path={Routes.CREATE_PASSWORD_PAGE}
component={CreatePasswordPage}
allowedRoles={["Administrator", "Learner"]}
/>
<PrivateRoute
exact
path={Routes.MANAGE_USERS_PAGE}
component={ManageUserPage}
allowedRoles={["Administrator"]}
/>
<PrivateRoute
exact
path={Routes.MAKE_HELP_REQUEST_PAGE}
component={MakeHelpRequestPage}
allowedRoles={["Learner"]}
/>
<PrivateRoute
exact
path={Routes.VIEW_HELP_REQUESTS_PAGE}
component={ViewHelpRequestsPage}
allowedRoles={["Facilitator"]}
/>
<PrivateRoute
exact
path={`${Routes.VIEW_HELP_REQUESTS_PAGE}/:id`}
component={HelpRequestPage}
allowedRoles={["Facilitator"]}
/>
<Route exact path="*" component={NotFound} />
</Switch>
</Router>
</SocketProvider>
</AuthContext.Provider>
</SampleContextDispatcherContext.Provider>
<AuthContext.Provider
value={{ authenticatedUser, setAuthenticatedUser }}
>
<SocketProvider id={authenticatedUser?.id}>
<Router>
<Switch>
<Route exact path={Routes.WELCOME_PAGE} component={Welcome} />
<Route exact path={Routes.LOGIN_PAGE} component={Login} />
<Route exact path={Routes.SIGNUP_PAGE} component={Signup} />
<Route exact path={Routes.FORGOT_PASSWORD_PAGE} component={ForgotPasswordPage} />

Check warning on line 94 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / run-lint

Replace `·exact·path={Routes.FORGOT_PASSWORD_PAGE}·component={ForgotPasswordPage}` with `⏎····················exact⏎····················path={Routes.FORGOT_PASSWORD_PAGE}⏎····················component={ForgotPasswordPage}⏎·················`

<PrivateRoute
exact
path={Routes.HOME_PAGE}
component={Default}
allowedRoles={["Administrator", "Facilitator", "Learner"]}
/>
<PrivateRoute
exact
path={Routes.MY_ACCOUNT_PAGE}
component={MyAccount}
allowedRoles={["Administrator", "Facilitator", "Learner"]}
/>
<PrivateRoute
exact
path={Routes.CREATE_MODULE_PAGE}
component={CreateModulePage}
allowedRoles={["Administrator"]}
/>
<Route
exact
path={Routes.NOT_AUTHORIZED_PAGE}
component={NotAuthorized}
/>
<PrivateRoute
exact
path={Routes.CREATE_PASSWORD_PAGE}
component={CreatePasswordPage}
allowedRoles={["Administrator", "Learner"]}
/>
<PrivateRoute
exact
path={Routes.MANAGE_USERS_PAGE}
component={ManageUserPage}
allowedRoles={["Administrator"]}
/>
<PrivateRoute
exact
path={Routes.MAKE_HELP_REQUEST_PAGE}
component={MakeHelpRequestPage}
allowedRoles={["Learner"]}
/>
<PrivateRoute
exact
path={Routes.VIEW_HELP_REQUESTS_PAGE}
component={ViewHelpRequestsPage}
allowedRoles={["Facilitator"]}
/>
<PrivateRoute
exact
path={`${Routes.VIEW_HELP_REQUESTS_PAGE}/:id`}
component={HelpRequestPage}
allowedRoles={["Facilitator"]}
/>
<Route exact path="*" component={NotFound} />
</Switch>
</Router>
</SocketProvider>
</AuthContext.Provider>
</SampleContextDispatcherContext.Provider>
</ThemeProvider>
</SampleContext.Provider>
);
};
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/auth/ResetPassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
const { authenticatedUser } = useContext(AuthContext);

const onResetPasswordClick = async () => {
await authAPIClient.resetPassword(authenticatedUser?.email);
if (!authenticatedUser?.email) {
console.error("Email is undefined. Cannot reset password.");

Check warning on line 11 in frontend/src/components/auth/ResetPassword.tsx

View workflow job for this annotation

GitHub Actions / run-lint

Unexpected console statement
return;
}
await authAPIClient.resetPassword(authenticatedUser.email);
};

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React from "react";
import { Container, Typography, Button } from "@mui/material";

interface ForgotPasswordConfirmationProps {
email: string;
onBackToEmail: () => void;
}

const ForgotPasswordConfirmation: React.FC<ForgotPasswordConfirmationProps> = ({
email,
onBackToEmail,
}) => {
return (
<Container
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
width: "100vw",
}}
>
<Container
sx={{
display: "flex",
width: "500px",
flexDirection: "column",
alignItems: "flex-start",
gap: "20px",
flexShrink: 0,
}}
>
<Container
sx={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
gap: "8px",
alignSelf: "stretch",
marginLeft: "-24px",
}}
>
<Typography
variant="h4"
gutterBottom
sx={{
color: "#000",
fontFamily: "Lexend Deca",
fontSize: "28px",
fontWeight: 600,
lineHeight: "120%",
marginBottom: 0,
}}
>
Email sent
</Typography>
<Typography
variant="body1"
sx={{
color: "#000",
fontFamily: "Lexend Deca",
fontSize: "16px",
fontWeight: 400,
lineHeight: "140%",
letterSpacing: "0.2px",
}}
>
Check your email ({email}) and open the link we sent to continue.
</Typography>
</Container>
<Button
variant="text"
color="primary"
onClick={onBackToEmail}
sx={{
color: "#006877",
fontSize: "12.5px",
fontWeight: 300,
lineHeight: "120%",
letterSpacing: "0.625px",
textTransform: "uppercase",
padding: 0,
}}
>
NOT YOUR EMAIL? Go back to change your email
</Button>
</Container>
</Container>
);
};

export default ForgotPasswordConfirmation;
Loading
Loading