Separate redis keys to check user email

This commit is contained in:
Chocobozzz
2026-07-24 11:00:16 +02:00
parent e46fb4d864
commit 5971c37a11
4 changed files with 50 additions and 8 deletions
@@ -124,6 +124,48 @@ describe('Test users email verification', function () {
}
})
it('Should not verify a pending email using a verification string minted for the main email', async function () {
this.timeout(30000)
let mainEmailVerificationString: string
{
await server.users.updateMe({
token: userAccessToken,
email: 'attacker-target@example.com',
currentPassword: user1.password
})
await waitJobs(server)
expectedEmailsLength++
expect(emails).to.have.lengthOf(expectedEmailsLength)
}
{
await server.users.askSendVerifyEmail({ email: 'updated@example.com' })
await waitJobs(server)
expectedEmailsLength++
expect(emails).to.have.lengthOf(expectedEmailsLength)
const email = emails[expectedEmailsLength - 1]
const verificationStringMatches = /verificationString=([a-z0-9]+)/.exec(email['text'])
mainEmailVerificationString = verificationStringMatches[1]
}
await server.users.verifyEmail({
userId,
verificationString: mainEmailVerificationString,
isPendingEmail: true,
expectedStatus: HttpStatusCode.FORBIDDEN_403
})
const me = await server.users.getMyInfo({ token: userAccessToken })
expect(me.email).to.equal('updated@example.com')
expect(me.pendingEmail).to.equal('attacker-target@example.com')
})
it('Should register user not requiring email verification if setting not enabled', async function () {
this.timeout(5000)
await server.config.updateExistingConfig({
+6 -6
View File
@@ -192,16 +192,16 @@ class Redis {
/* ************ Email verification ************ */
async setUserVerifyEmailVerificationString (userId: number) {
async setUserVerifyEmailVerificationString (userId: number, isPendingEmail: boolean) {
const generatedString = await generateRandomString(32)
await this.setValue(this.generateUserVerifyEmailKey(userId), generatedString, EMAIL_VERIFY_LIFETIME)
await this.setValue(this.generateUserVerifyEmailKey(userId, isPendingEmail), generatedString, EMAIL_VERIFY_LIFETIME)
return generatedString
}
async getUserVerifyEmailLink (userId: number) {
return this.getValue(this.generateUserVerifyEmailKey(userId))
async getUserVerifyEmailLink (userId: number, isPendingEmail: boolean) {
return this.getValue(this.generateUserVerifyEmailKey(userId, isPendingEmail))
}
async setRegistrationVerifyEmailVerificationString (registrationId: number) {
@@ -479,8 +479,8 @@ class Redis {
return 'two-factor-request-' + userId + '-' + token
}
private generateUserVerifyEmailKey (userId: number) {
return 'verify-email-user-' + userId
private generateUserVerifyEmailKey (userId: number, isPendingEmail: boolean) {
return 'verify-email-user-' + userId + (isPendingEmail ? '-pending' : '')
}
private generateRegistrationVerifyEmailKey (registrationId: number) {
+1 -1
View File
@@ -172,7 +172,7 @@ export async function createApplicationActor (applicationId: number) {
// ---------------------------------------------------------------------------
export async function buildUserVerifyEmail (user: MUser, isPendingEmail: boolean) {
const verificationString = await Redis.Instance.setUserVerifyEmailVerificationString(user.id)
const verificationString = await Redis.Instance.setUserVerifyEmailVerificationString(user.id, isPendingEmail)
const verifyEmailUrl = `${WEBSERVER.URL}/verify-account/email?userId=${user.id}&verificationString=${verificationString}`
@@ -93,7 +93,7 @@ export const usersVerifyEmailValidator = [
if (!await checkUserIdExist(req.params.id, res)) return
const user = res.locals.user
const redisVerificationString = await Redis.Instance.getUserVerifyEmailLink(user.id)
const redisVerificationString = await Redis.Instance.getUserVerifyEmailLink(user.id, req.body.isPendingEmail === true)
if (redisVerificationString !== req.body.verificationString) {
return res.fail({ status: HttpStatusCode.FORBIDDEN_403, message: 'Invalid verification string.' })