Skip to content

Commit

Permalink
new API route to reset password
Browse files Browse the repository at this point in the history
  • Loading branch information
javtran committed Aug 23, 2024
1 parent bdc2eaa commit 4d2b976
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 0 deletions.
59 changes: 59 additions & 0 deletions server/routes/api/v1/users/password-reset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { StatusCodes } from 'http-status-codes';
import User from '../../../../models/user.js';

export default async function (fastify, _opts) {
fastify.post(
'/password-reset',
{
schema: {
body: {
type: 'object',
required: ['passwordResetToken', 'password'],
properties: {
passwordResetToken: { type: 'string' },
password: { type: 'string' },
},
},
response: {
[StatusCodes.OK]: {
type: 'null',
},
[StatusCodes.UNAUTHORIZED]: {
type: 'object',
properties: {
message: { type: 'string' },
},
},
},
},
},
async (request, reply) => {
const { passwordResetToken, password } = request.body;

const data = await fastify.prisma.user.findUnique({
where: { passwordResetToken },
});

if (!data) {
return reply.unauthorized(
'Password Reset Link is expired or not valid',
);
}

const user = new User(data);

await user.setPassword(password);

await fastify.prisma.user.update({
where: { passwordResetToken },
data: {
passwordResetToken: null,
passwordResetExpires: null,
hashedPassword: user.hashedPassword,
},
});

reply.code(StatusCodes.OK);
},
);
}
74 changes: 74 additions & 0 deletions server/test/routes/api/v1/users.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -796,4 +796,78 @@ describe('/api/v1/users', () => {
);
});
});

describe('POST /password-reset', () => {
it('should return status OK on successful password reset', async (t) => {
const app = await build(t);
await t.loadFixtures();

const res = await app
.inject()
.post('/api/v1/users/password-reset')
.payload({
passwordResetToken: '4ae4a190-005e-4222-aac3-7dd5ff2c477f',
password: 'NewPassword123!',
});

assert.deepStrictEqual(res.statusCode, StatusCodes.OK);

const user = await t.prisma.user.findUnique({
where: { id: '5a848b86-e418-4f7f-9973-cbda82aaaba5' },
});

bcrypt.compare(
'NewPassword123!',
user.hashedPassword,
function (err, result) {
assert.deepStrictEqual(result, true);
},
);

assert.deepStrictEqual(user.passwordResetToken, null);
assert.deepStrictEqual(user.passwordResetExpires, null);
});

it('should return status UNAUTHORIZED on invalid attempt', async (t) => {
const app = await build(t);
await t.loadFixtures();

let res = await app
.inject()
.post('/api/v1/users/password-reset')
.payload({
passwordResetToken: '4ae4a190-005e-4222-aac3-7dd5ff2c477f',
password: 'NewPassword123!',
});

assert.deepStrictEqual(res.statusCode, StatusCodes.OK);

res = await app.inject().post('/api/v1/users/password-reset').payload({
passwordResetToken: '4ae4a190-005e-4222-aac3-7dd5ff2c477f',
password: 'AnotherPassword123!',
});

const user = await t.prisma.user.findUnique({
where: { id: '5a848b86-e418-4f7f-9973-cbda82aaaba5' },
});

assert.deepStrictEqual(res.statusCode, StatusCodes.UNAUTHORIZED);
const { message } = JSON.parse(res.body);
assert.deepStrictEqual(
message,
'Password Reset Link is expired or not valid',
);

bcrypt.compare(
'NewPassword123!',
user.hashedPassword,
function (err, result) {
assert.deepStrictEqual(result, true);
},
);

assert.deepStrictEqual(user.passwordResetToken, null);
assert.deepStrictEqual(user.passwordResetExpires, null);
});
});
});

0 comments on commit 4d2b976

Please sign in to comment.