server: enhance reset-password endpoint

- Add a rate limit analogous to request-reset-password.
  See also a0ef32f4f6.
- Delete an expired reset request if found.
- Return a proper error.
- Use time constants.

Changelog: Changed
This commit is contained in:
Johann150 2022-10-26 22:12:38 +02:00
parent 384e8c49b7
commit 4dc97d5b65
Signed by untrusted user: Johann150
GPG key ID: 9EE6577A2A06F8F1

View file

@ -1,5 +1,6 @@
import bcrypt from 'bcryptjs';
import { UserProfiles, PasswordResetRequests } from '@/models/index.js';
import { MINUTE } from '@/const.js';
import define from '../define.js';
export const meta = {
@ -9,8 +10,17 @@ export const meta = {
description: 'Complete the password reset that was previously requested.',
errors: {
limit: {
duration: HOUR,
max: 3,
},
errors: {
noSuchResetRequest: {
message: 'No such reset request.',
code: 'NO_SUCH_RESET_REQUEST',
id: '6382759d-294c-43de-89b3-4e825006ca43',
},
},
} as const;
@ -25,13 +35,18 @@ export const paramDef = {
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async (ps, user) => {
const req = await PasswordResetRequests.findOneByOrFail({
const req = await PasswordResetRequests.findOneBy({
token: ps.token,
});
if (req == null) throw new ApiError(meta.errors.noSuchResetRequest);
// 発行してから30分以上経過していたら無効
if (Date.now() - req.createdAt.getTime() > 1000 * 60 * 30) {
throw new Error(); // TODO
// expires after 30 minutes
// This is a secondary check just in case the expiry task is broken,
// the expiry task is badly aligned with this expiration or something
// else strange is going on.
if (Date.now() - req.createdAt.getTime() > 30 * MINUTE) {
await PasswordResetRequests.delete(req.id);
throw new ApiError(meta.errors.noSuchResetRequest);
}
// Generate hash of password
@ -42,5 +57,5 @@ export default define(meta, paramDef, async (ps, user) => {
password: hash,
});
PasswordResetRequests.delete(req.id);
await PasswordResetRequests.delete(req.id);
});