fix: forgotPassword set expiration time (#9871)

The logic for creating a timestamp for use in resetPassword was not
correctly returning a valid date.

---------

Co-authored-by: Patrik Kozak <patrik@payloadcms.com>
This commit is contained in:
Dan Ribbens
2024-12-11 08:43:22 -05:00
committed by GitHub
parent ca52a50dd9
commit 306b5d2300
4 changed files with 77 additions and 2 deletions

View File

@@ -137,8 +137,8 @@ export const forgotPasswordOperation = async <TSlug extends CollectionSlug>(
user.resetPasswordToken = token
user.resetPasswordExpiration = new Date(
collectionConfig.auth?.forgotPassword?.expiration || expiration || Date.now() + 3600000,
).toISOString() // 1 hour
Date.now() + (collectionConfig.auth?.forgotPassword?.expiration ?? expiration ?? 3600000),
).toISOString()
user = await payload.update({
id: user.id,

View File

@@ -605,6 +605,43 @@ describe('Access Control', () => {
expect(res).toBeTruthy()
})
})
describe('Auth - Local API', () => {
it('should not allow reset password if forgotPassword expiration token is expired', async () => {
// Mock Date.now() to simulate the forgotPassword call happening 1 hour ago (default is 1 hour)
const originalDateNow = Date.now
const mockDateNow = jest.spyOn(Date, 'now').mockImplementation(() => {
// Move the current time back by 1 hour
return originalDateNow() - 60 * 60 * 1000
})
let forgot
try {
// Call forgotPassword while the mocked Date.now() is active
forgot = await payload.forgotPassword({
collection: 'users',
data: {
email: 'dev@payloadcms.com',
},
})
} finally {
// Restore the original Date.now() after the forgotPassword call
mockDateNow.mockRestore()
}
// Attempt to reset password, which should fail because the token is expired
await expect(
payload.resetPassword({
collection: 'users',
data: {
password: 'test',
token: forgot,
},
overrideAccess: true,
}),
).rejects.toThrow('Token is either invalid or has expired.')
})
})
})
async function createDoc<TSlug extends CollectionSlug = 'posts'>(

View File

@@ -44,6 +44,9 @@ export default buildConfigWithDefaults({
tokenExpiration: 7200, // 2 hours
useAPIKey: true,
verify: false,
forgotPassword: {
expiration: 300000, // 5 minutes
},
},
fields: [
{

View File

@@ -932,5 +932,40 @@ describe('Auth', () => {
expect(reset.user.email).toStrictEqual('dev@payloadcms.com')
})
it('should not allow reset password if forgotPassword expiration token is expired', async () => {
// Mock Date.now() to simulate the forgotPassword call happening 6 minutes ago (current expiration is set to 5 minutes)
const originalDateNow = Date.now
const mockDateNow = jest.spyOn(Date, 'now').mockImplementation(() => {
// Move the current time back by 6 minutes (360,000 ms)
return originalDateNow() - 6 * 60 * 1000
})
let forgot
try {
// Call forgotPassword while the mocked Date.now() is active
forgot = await payload.forgotPassword({
collection: 'users',
data: {
email: 'dev@payloadcms.com',
},
})
} finally {
// Restore the original Date.now() after the forgotPassword call
mockDateNow.mockRestore()
}
// Attempt to reset password, which should fail because the token is expired
await expect(
payload.resetPassword({
collection: 'users',
data: {
password: 'test',
token: forgot,
},
overrideAccess: true,
}),
).rejects.toThrow('Token is either invalid or has expired.')
})
})
})