diff --git a/packages/password/package.json b/packages/password/package.json index f21250a70..44303ff8d 100644 --- a/packages/password/package.json +++ b/packages/password/package.json @@ -24,8 +24,8 @@ "dependencies": { "@accounts/two-factor": "^0.32.4", "bcryptjs": "2.4.3", - "tslib": "2.6.2", - "validator": "^13.11.0" + "express-validator": "^7.0.1", + "tslib": "2.6.2" }, "devDependencies": { "@accounts/server": "^0.33.1", @@ -33,7 +33,6 @@ "@types/bcryptjs": "2.4.6", "@types/express": "^4.17.21", "@types/lodash.set": "4.3.9", - "@types/validator": "^13", "graphql": "16.8.1", "graphql-modules": "3.0.0-alpha-20231106133212-0b04b56e", "lodash.set": "4.3.2", diff --git a/packages/password/src/endpoints/express.ts b/packages/password/src/endpoints/express.ts index e3a1797f5..518b1a1b5 100644 --- a/packages/password/src/endpoints/express.ts +++ b/packages/password/src/endpoints/express.ts @@ -1,7 +1,16 @@ import { type Injector } from 'graphql-modules'; import type { Request, Response, NextFunction } from 'express'; -import validator from 'validator'; import AccountsPassword from '../accounts-password'; +import { body, matchedData, param, validationResult } from 'express-validator'; + +function matchOrThrow = Record>( + ...args: Parameters +): T { + if (!validationResult(args[0]).isEmpty()) { + throw new Error('Validation error'); + } + return matchedData(...args) as T; +} function getHtml(title: string, body: string) { return ` @@ -30,62 +39,93 @@ export const infosMiddleware = (req: Request, _res: Response, next: NextFunction next(); }; -export const verifyEmail = +export const verifyEmail = [ + param('token').isString().notEmpty(), (accountsPasswordOrInjector: Injector | AccountsPassword) => - async (req: Request, res: Response) => { - try { - const { token } = req.params; - if (token == null) { - throw new Error('Token is missing'); - } - const accountsPassword = - accountsPasswordOrInjector instanceof AccountsPassword - ? accountsPasswordOrInjector - : accountsPasswordOrInjector.get(AccountsPassword); - await accountsPassword.verifyEmail(token); - res.send( - getHtml( - 'Email successfully verified', - ` + async (req: Request, res: Response) => { + try { + const { token } = matchOrThrow<{ token: string }>(req); + const accountsPassword = + accountsPasswordOrInjector instanceof AccountsPassword + ? accountsPasswordOrInjector + : accountsPasswordOrInjector.get(AccountsPassword); + await accountsPassword.verifyEmail(token); + res.send( + getHtml( + 'Email successfully verified', + `

The email address has been successfully verified.

` - ) - ); - } catch (err: any) { - res.send( - //codeql[js/xss-through-exception] - getHtml( - 'Email verification error', - ` + ) + ); + } catch (err: any) { + res.send( + //codeql[js/xss-through-exception] + getHtml( + 'Email verification error', + `

The email address couldn't be verified: ${err.message ?? 'unknown error'}

` - ) - ); - } - }; + ) + ); + } + }, +]; -export const resetPassword = +export const resetPassword = [ + body('token').isString().notEmpty(), + body('newPassword').isString().notEmpty(), (accountsPasswordOrInjector: Injector | AccountsPassword) => - async (req: Request, res: Response) => { - try { - const { token, newPassword } = req.body; - if (token == null) { - throw new Error('Token is missing'); - } - if (newPassword == null) { - throw new Error('New password is missing'); + async (req: Request, res: Response) => { + try { + const { token, newPassword } = matchOrThrow<{ token: string; newPassword: string }>(req); + const accountsPassword = + accountsPasswordOrInjector instanceof AccountsPassword + ? accountsPasswordOrInjector + : accountsPasswordOrInjector.get(AccountsPassword); + await accountsPassword.resetPassword(token, newPassword, req.infos); + res.send( + getHtml( + 'Password successfully changed', + ` +

The password has been successfully changed.

+ ` + ) + ); + } catch (err: any) { + //codeql[js/xss-through-exception] + res.send( + getHtml( + 'Password reset error', + ` +

The password couldn't be changed: ${err.message ?? 'unknown error'}

+ ` + ) + ); } - const accountsPassword = - accountsPasswordOrInjector instanceof AccountsPassword - ? accountsPasswordOrInjector - : accountsPasswordOrInjector.get(AccountsPassword); - await accountsPassword.resetPassword(token, newPassword, req.infos); + }, +]; + +export const resetPasswordForm = [ + param('token').isString().notEmpty().escape(), + (req: Request, res: Response) => { + try { + const { token } = matchOrThrow<{ token: string }>(req); res.send( getHtml( - 'Password successfully changed', + 'Reset password', ` -

The password has been successfully changed.

- ` +
+

Reset your password

+
+ +
+ + +
+ +
+ ` ) ); } catch (err: any) { @@ -99,23 +139,5 @@ export const resetPassword = ) ); } - }; - -export const resetPasswordForm = (req: Request, res: Response): Response => - res.send( - getHtml( - 'Reset password', - ` -
-

Reset your password

-
- -
- - -
- -
- ` - ) - ); + }, +]; diff --git a/yarn.lock b/yarn.lock index e79729069..8275c5187 100644 --- a/yarn.lock +++ b/yarn.lock @@ -538,14 +538,13 @@ __metadata: "@types/bcryptjs": "npm:2.4.6" "@types/express": "npm:^4.17.21" "@types/lodash.set": "npm:4.3.9" - "@types/validator": "npm:^13" bcryptjs: "npm:2.4.3" + express-validator: "npm:^7.0.1" graphql: "npm:16.8.1" graphql-modules: "npm:3.0.0-alpha-20231106133212-0b04b56e" lodash.set: "npm:4.3.2" reflect-metadata: "npm:0.1.13" tslib: "npm:2.6.2" - validator: "npm:^13.11.0" peerDependencies: "@accounts/server": ^0.32.0 || ^0.33.0 graphql: ^16.0.0 @@ -8542,13 +8541,6 @@ __metadata: languageName: node linkType: hard -"@types/validator@npm:^13": - version: 13.11.6 - resolution: "@types/validator@npm:13.11.6" - checksum: 3201902a8e5d4784d1c67f5a5a796d1500bae10fe5413ed75fdbdf5d6b5572952445f3482ffe64908531b20171d4c5cfe94934de3fd401781bb6cf9f95766b02 - languageName: node - linkType: hard - "@types/webidl-conversions@npm:*": version: 7.0.3 resolution: "@types/webidl-conversions@npm:7.0.3" @@ -28921,7 +28913,7 @@ __metadata: languageName: node linkType: hard -"validator@npm:^13.11.0, validator@npm:^13.9.0": +"validator@npm:^13.9.0": version: 13.11.0 resolution: "validator@npm:13.11.0" checksum: 0107da3add5a4ebc6391dac103c55f6d8ed055bbcc29a4c9cbf89eacfc39ba102a5618c470bdc33c6487d30847771a892134a8c791f06ef0962dd4b7a60ae0f5