diff --git a/forge/db/controllers/AccessToken.js b/forge/db/controllers/AccessToken.js index 909da9d1e1..46e35301be 100644 --- a/forge/db/controllers/AccessToken.js +++ b/forge/db/controllers/AccessToken.js @@ -51,7 +51,7 @@ module.exports = { await app.db.models.AccessToken.destroy({ where: { ownerType: 'user', - scope: 'password:Reset', + scope: 'password:reset', ownerId: user.hashid } }) diff --git a/forge/db/controllers/User.js b/forge/db/controllers/User.js index 28a28cc1ca..852bd96cfc 100644 --- a/forge/db/controllers/User.js +++ b/forge/db/controllers/User.js @@ -168,6 +168,9 @@ module.exports = { requestingUser.email = decodedToken.change // apply new Email Address requestingUser.email_verified = true await requestingUser.save() + + await app.db.controllers.AccessToken.deleteAllUserPasswordResetTokens(requestingUser) + return requestingUser } catch (err) { if (err.name === 'TokenExpiredError') { diff --git a/test/unit/forge/db/controllers/AccessToken_spec.js b/test/unit/forge/db/controllers/AccessToken_spec.js index 1dd3f637c1..28bec78567 100644 --- a/test/unit/forge/db/controllers/AccessToken_spec.js +++ b/test/unit/forge/db/controllers/AccessToken_spec.js @@ -203,4 +203,27 @@ describe('AccessToken controller', function () { ;(await app.db.models.AccessToken.count()).should.equal(0) }) }) + + describe('passwordReset Tokens', function () { + it('generates a password reset token for a known user', async function () { + ;(await app.db.models.AccessToken.count({ where: { scope: 'password:reset' } })).should.equal(0) + const originalToken = await app.db.controllers.AccessToken.createTokenForPasswordReset(TestObjects.alice) + ;(await app.db.models.AccessToken.count({ where: { scope: 'password:reset' } })).should.equal(1) + const token1 = await app.db.models.AccessToken.findOne({ where: { ownerId: TestObjects.alice.hashid } }) + token1.should.have.property('ownerId', TestObjects.alice.hashid) + token1.should.have.property('ownerType', 'user') + token1.should.have.property('scope', ['password:reset']) + + // Ensure regenerating the token removes old ones + const newToken = await app.db.controllers.AccessToken.createTokenForPasswordReset(TestObjects.alice) + // Should still only be one + ;(await app.db.models.AccessToken.count({ where: { scope: 'password:reset' } })).should.equal(1) + + const oldTokenInDb = await app.db.controllers.AccessToken.getOrExpirePasswordResetToken(originalToken.token) + should.not.exist(oldTokenInDb) + + const newTokenInDb = await app.db.controllers.AccessToken.getOrExpirePasswordResetToken(newToken.token) + should.exist(newTokenInDb) + }) + }) }) diff --git a/test/unit/forge/routes/api/user_spec.js b/test/unit/forge/routes/api/user_spec.js index 032311e5b6..85cd988608 100644 --- a/test/unit/forge/routes/api/user_spec.js +++ b/test/unit/forge/routes/api/user_spec.js @@ -277,6 +277,11 @@ describe('User API', async function () { }) it('user can change password', async function () { + // Create a password reset token so we can verify it gets cleared + ;(await app.db.models.AccessToken.count({ where: { scope: 'password:reset' } })).should.equal(0) + await app.db.controllers.AccessToken.createTokenForPasswordReset(TestObjects.dave) + ;(await app.db.models.AccessToken.count({ where: { scope: 'password:reset' } })).should.equal(1) + await login('dave', 'ddPassword') const secondLoginSession = await app.inject({ @@ -329,6 +334,9 @@ describe('User API', async function () { }) checkSecondToken.statusCode.should.equal(401) + // The password_reset token should no longer exist + ;(await app.db.models.AccessToken.count({ where: { scope: 'password:reset' } })).should.equal(0) + await TestObjects.dave.reload() TestObjects.dave.password = 'ddPassword' await TestObjects.dave.save()