From c56e80f2da632ca2f6bc6726fe8ba277dea85871 Mon Sep 17 00:00:00 2001 From: Daniel Zhang Date: Thu, 28 Nov 2024 19:21:20 -0500 Subject: [PATCH] Implement create password functionality --- backend/typescript/rest/authRoutes.ts | 28 +++ .../services/implementations/authService.ts | 25 +- .../services/interfaces/authService.ts | 10 +- backend/typescript/types.ts | 5 + frontend/package.json | 1 + .../images/humane_society_logo_text.png | Bin 0 -> 9230 bytes frontend/src/APIClients/AuthAPIClient.ts | 33 ++- frontend/src/App.tsx | 7 + frontend/src/components/assets/background.png | Bin 0 -> 111113 bytes .../components/assets/background_mobile.png | Bin 0 -> 43441 bytes .../responsive/ResponsiveAuthContainer.tsx | 34 +++ .../responsive/ResponsiveAuthPageLogo.tsx | 24 ++ .../responsive/ResponsiveModalWindow.tsx | 36 +++ .../responsive/ResponsivePasswordInput.tsx | 58 +++++ .../ResponsivePawprintBackground.tsx | 46 ++++ .../components/pages/CreatePasswordPage.tsx | 226 ++++++++++++++++++ frontend/src/constants/Routes.ts | 2 + frontend/src/theme.tsx | 5 + frontend/src/types/AuthTypes.ts | 5 + 19 files changed, 542 insertions(+), 3 deletions(-) create mode 100644 frontend/public/images/humane_society_logo_text.png create mode 100644 frontend/src/components/assets/background.png create mode 100644 frontend/src/components/assets/background_mobile.png create mode 100644 frontend/src/components/common/responsive/ResponsiveAuthContainer.tsx create mode 100644 frontend/src/components/common/responsive/ResponsiveAuthPageLogo.tsx create mode 100644 frontend/src/components/common/responsive/ResponsiveModalWindow.tsx create mode 100644 frontend/src/components/common/responsive/ResponsivePasswordInput.tsx create mode 100644 frontend/src/components/common/responsive/ResponsivePawprintBackground.tsx create mode 100644 frontend/src/components/pages/CreatePasswordPage.tsx diff --git a/backend/typescript/rest/authRoutes.ts b/backend/typescript/rest/authRoutes.ts index 767183e..945fb4c 100644 --- a/backend/typescript/rest/authRoutes.ts +++ b/backend/typescript/rest/authRoutes.ts @@ -117,6 +117,34 @@ authRouter.post( }, ); +// updates user password and updates status +authRouter.post( + "/setPassword/:email", + isAuthorizedByEmail("email"), + async (req, res) => { + try { + const responseSuccess = await authService.setPassword( + req.params.email, + req.body.newPassword, + ); + if (responseSuccess.success) { + const user = await userService.getUserByEmail(req.params.email); + if (user.status === UserStatus.INVITED) { + userService.updateUserById(user.id, { + ...user, + status: UserStatus.ACTIVE, + }); + } + res.status(200).json(responseSuccess); + } else { + res.status(400).json(responseSuccess); + } + } catch (error) { + res.status(500).json({ error: getErrorMessage(error) }); + } + }, +); + /* Invite a user */ authRouter.post("/invite-user", inviteUserDtoValidator, async (req, res) => { try { diff --git a/backend/typescript/services/implementations/authService.ts b/backend/typescript/services/implementations/authService.ts index e7ab62a..0b69121 100644 --- a/backend/typescript/services/implementations/authService.ts +++ b/backend/typescript/services/implementations/authService.ts @@ -3,7 +3,7 @@ import * as firebaseAdmin from "firebase-admin"; import IAuthService from "../interfaces/authService"; import IEmailService from "../interfaces/emailService"; import IUserService from "../interfaces/userService"; -import { AuthDTO, Role, Token } from "../../types"; +import { AuthDTO, Role, Token, ResponseSuccessDTO } from "../../types"; import { getErrorMessage } from "../../utilities/errorUtils"; import FirebaseRestClient from "../../utilities/firebaseRestClient"; import logger from "../../utilities/logger"; @@ -286,6 +286,29 @@ class AuthService implements IAuthService { return false; } } + + async setPassword( + email: string, + newPassword: string, + ): Promise { + let errorMessage = "An unknown error occured. Please try again later."; + try { + const uid = await (await firebaseAdmin.auth().getUserByEmail(email)).uid; + await firebaseAdmin.auth().updateUser(uid, { + password: newPassword, + }); + return { success: true } as ResponseSuccessDTO; + } catch (error: any) { + Logger.error(`Failed to update password. Error: ${error}`); + if (error.code === "auth/invalid-password") { + errorMessage = + "Password is too weak! Make sure it matches the password policy in Firebase."; + } else if (error.code === "auth/user-not-found") { + errorMessage = "No user found with the provided email!"; + } + return { success: false, errorMessage }; + } + } } export default AuthService; diff --git a/backend/typescript/services/interfaces/authService.ts b/backend/typescript/services/interfaces/authService.ts index c9ddaac..50e4d4e 100644 --- a/backend/typescript/services/interfaces/authService.ts +++ b/backend/typescript/services/interfaces/authService.ts @@ -1,4 +1,4 @@ -import { AuthDTO, Role, Token } from "../../types"; +import { AuthDTO, Role, Token, ResponseSuccessDTO } from "../../types"; interface IAuthService { /** @@ -98,6 +98,14 @@ interface IAuthService { accessToken: string, requestedEmail: string, ): Promise; + + /** + * Set password for the specified email. + * @param accessToken user's access token + * @param requestedEmail email address of requested user + * @returns success (boolean) and errorMessage (string) + */ + setPassword(email: string, newPassword: string): Promise; } export default IAuthService; diff --git a/backend/typescript/types.ts b/backend/typescript/types.ts index 3073b9f..2657ef6 100644 --- a/backend/typescript/types.ts +++ b/backend/typescript/types.ts @@ -31,6 +31,11 @@ export type RegisterUserDTO = Omit; export type AuthDTO = Token & UserDTO; +export type ResponseSuccessDTO = { + success: boolean; + errorMessage?: string; +}; + export type Letters = "A" | "B" | "C" | "D"; const sexValues = ["M", "F"] as const; diff --git a/frontend/package.json b/frontend/package.json index 9239e3b..9e99ff5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,6 +4,7 @@ "private": true, "dependencies": { "@apollo/client": "^3.3.16", + "@chakra-ui/icons": "^2.2.4", "@chakra-ui/react": "^2.8.2", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", diff --git a/frontend/public/images/humane_society_logo_text.png b/frontend/public/images/humane_society_logo_text.png new file mode 100644 index 0000000000000000000000000000000000000000..99861737ba395e7868c42908eb55352de4701112 GIT binary patch literal 9230 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%0A& zo$3d4uJLgIh$q3wDC<9AahpD};e8#r4-G-6S$&kUwK!h_y45oOt5)k$y5MjlQ;mx_ zKIyW;NTU_7I_25Wb|vM_AnhWN-S451AaA*YUD6A_JpS-~Rgwqsf0^v^rCRAKJ5{D_UR&wCjG#S##BqQYfE*tQS-q2X4bpvyG-40lPnt(f z0OEx6aw%!IAdTvOd1UwO2uM44cCuH;0+cJ==YNUql@ZTxD=l^GNBzc|s1|1z{Kv_7<4?1>QLMEat%qpX)1DLel<%hQTC0>2;EYDy9a zeS8^3j>w5C#qb-vxL3PztdBLf?jI4BE1Ks1Kiv4*<0AHq0l3zF7p^gYEE~qe1$wmH zTM+sNG~KC7yA7Maq)j#??zP@?$h_;cxWuqPu3TM@K=wUf8E2RgWwg~a!cO$+GL&%X z5Qi95Tx~)?#8I}u7bx(^xWLAVdka82h}TJ6e-jn)7Z222x~NvxDny-UxM?ur9^QdD zA2K_Cp>a~da~(>ufBOm4*1U_t{XU3xMVya9wMX**XHL5P{J3QuI8H2rj)=o?b%_6=tK+0o+!rS{Sei!2t^F?iPO3V3lsL|l5#@bFWs>gCJlxID zk!Ie*2v=0NXd$;T>$f6?S1PRyT*c>f!lVhqp8_l&r9pLau~@Lo%T z5Z#1h%xC5?AIHhgITevB4XVzluUi%wYJ?F1YI3Vl$tUw}>K9}Z)}8c*_l@U0UlF;# zd)j0Ohjsn%I2pk+JDSATq$`?2NH2HUg8S>6V)V-?cPOLjPNp^tG&Ne{6m||8W^w5I zPNfDy8{4n8pOs|Qq(4ErZFn$AyhcM^7EX5Ylh#Y}^Y58Hbb=2&OJ8{ltJ_fz4$T|! zE$a4~#9#QEX3n9%^^hDoxNLOWHcC7!rfA_>Zaih^C*9s1q8fb$%RY(`;ny71aMG^d z8oD6$Xi1pdpYCAK)8FPixua+nXuB>xhi4~_wrg&qI5A*2*bDPLcFJYglBN!jP7>Ya z&746}+4pm3T44vKVKRLI(1>dm&8x~l+QB^7Q;>F&)W&{))|7#xI<9Qg4t2`X?`^p0 zLj|?xZxI<|n);yk=o*`fUwW%?q@Z?muFJqdYaZ&S&48Mwz{t-CZb`*X56{fc`N#FHN4Tn{&0-IWHxw@Zr?cAW;6554fML;r$t zqO|3X;v-EH2cXu1iVV{4BZ`{-_g+%APSAf9#L=(lpTzmVGy&u-o2x9LiMHHYq8r+v z5Y%Bjb*T7^ixLax(~KPz^_O(*1c)oA)|%>-y1r$Ox{Z5Hnuj5&Jxcv@Hz!=1)yRzJ z#j-r*Hi3L4u-e6@m5&Hnz8r4qgH#YE`hntjk?%Q0L%UX&P2Y-^ib%m{p%P_!O;;qz zYs#JvnwJ;cGjki%VZ2E;U92EZ1V~y$%g=)ETY0=)TaDM%0e3>j*Jq|J`Dw