Skip to content

Commit

Permalink
feat: use express-validator in password endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
darkbasic committed Nov 20, 2023
1 parent 289600d commit acef284
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 79 deletions.
5 changes: 2 additions & 3 deletions packages/password/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,15 @@
"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",
"@accounts/types": "^0.33.1",
"@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",
Expand Down
154 changes: 88 additions & 66 deletions packages/password/src/endpoints/express.ts
Original file line number Diff line number Diff line change
@@ -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<T extends Record<string, any> = Record<string, any>>(
...args: Parameters<typeof matchedData>
): T {
if (!validationResult(args[0]).isEmpty()) {
throw new Error('Validation error');
}
return matchedData(...args) as T;
}

function getHtml(title: string, body: string) {
return `
Expand Down Expand Up @@ -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',
`
<h3>The email address has been successfully verified.</h3>
`
)
);
} 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',
`
<h3>The email address couldn't be verified: ${err.message ?? 'unknown error'}</h3>
`
)
);
}
};
)
);
}
},
];

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',
`
<h3>The password has been successfully changed.</h3>
`
)
);
} catch (err: any) {
//codeql[js/xss-through-exception]
res.send(
getHtml(
'Password reset error',
`
<h3>The password couldn't be changed: ${err.message ?? 'unknown error'}</h3>
`
)
);
}
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',
`
<h3>The password has been successfully changed.</h3>
`
<div class="container">
<h1>Reset your password</h1>
<form action="/resetPassword" method="POST">
<input type="hidden" name="token" value=${token} />
<div class="form-group">
<label for="newPassword">New password</label>
<input type="text" class="form-control" id="newPassword" value="" placeholder="Enter your new password" name="newPassword">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
`
)
);
} catch (err: any) {
Expand All @@ -99,23 +139,5 @@ export const resetPassword =
)
);
}
};

export const resetPasswordForm = (req: Request, res: Response): Response =>
res.send(
getHtml(
'Reset password',
`
<div class="container">
<h1>Reset your password</h1>
<form action="/resetPassword" method="POST">
<input type="hidden" name="token" value=${validator.escape(req.params.token)} />
<div class="form-group">
<label for="newPassword">New password</label>
<input type="text" class="form-control" id="newPassword" value="" placeholder="Enter your new password" name="newPassword">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
`
)
);
},
];
12 changes: 2 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit acef284

Please sign in to comment.