1
0
Fork 0
mirror of https://github.com/overleaf/overleaf.git synced 2025-04-22 19:47:33 +00:00

Confirm email new routes ()

* confirm email routes

* Style the email confirmation template ()

* error handling

* prettier

* error message

* rename variables

* message codes change

* v1 redirect

* fix assigning to session

* rename rate limitter

* rate limitter per email

* add try/catch

* added stub

* prettier

* confirm email acceptance test

* confirm when created

* tests

* added rate limit tests

* new email text

* subscribe to newsletter

* beforeEach/afterEach test both variants

* move tests to OverleafAuthenticationTests

* Revert "move tests to OverleafAuthenticationTests"

This reverts commit 3c745382815da1594044a811882ba3daa24a7a3a.

* cacheflow reset after each

* remove test archive request

* use crypto for random code

* rate limit in userEmailsConfirmationHandler

* ratelimiter per type

* req.session.pendingUserRegistration

* spy in before/after each

* without deleteMany

* delete staffUser in afterEach

* stub response, format

* rate limiter outside userEmailConfirmationHandler

* mock ratelimitter

* fix subscribe promise

* add email to logger

* logger calls

* using tsscmp

* fix lint

* resendConfirmationCode rate limiter in router

* remove redirect

---------

Co-authored-by: Rebeka Dekany <50901361+rebekadekany@users.noreply.github.com>
GitOrigin-RevId: 786c477966cf2c5f6e28417fe486146ee5c10884
This commit is contained in:
Domagoj Kriskovic 2023-11-01 12:46:35 +01:00 committed by Copybot
parent 79b4f1ec7f
commit af4b22fab5
4 changed files with 70 additions and 3 deletions

View file

@ -29,9 +29,14 @@ module.exports = _.template(`\
<%= paragraph %>
</p>
<% }) %>
</td>
<% if (highlightedText) { %>
<div style="text-align: center; color: #1B222C; font-size: 20px; margin: 16px 0; padding: 16px 8px; border-radius: 8px; background: #F4F5F6;">
<b><%= highlightedText %></b>
</div>
<% } %>
</td>
</tr>
</tr>
</tr>
</table>
</th>
</tr>

View file

@ -112,6 +112,10 @@ The ${settings.appName} Team - ${settings.siteUrl}\
title:
typeof content.title === 'function' ? content.title(opts) : undefined,
greeting: content.greeting(opts),
highlightedText:
typeof content.highlightedText === 'function'
? content.highlightedText(opts)
: undefined,
message: content.message(opts),
StringHelper,
})
@ -242,6 +246,32 @@ templates.confirmEmail = ctaTemplate({
},
})
templates.confirmCode = NoCTAEmailTemplate({
greeting(opts) {
return ''
},
subject(opts) {
return `Confirm your email address on Overleaf (${opts.confirmCode})`
},
title(opts) {
return 'Confirm your email address'
},
message(opts, isPlainText) {
const msg = [
`Welcome to Overleaf! We're so glad you joined us.`,
'Use this 6-digit confirmation code to finish your setup.',
]
if (isPlainText && opts.confirmCode) {
msg.push(opts.confirmCode)
}
return msg
},
highlightedText(opts) {
return opts.confirmCode
},
})
templates.projectInvite = ctaTemplate({
subject(opts) {
const safeName = SpamSafe.isSafeProjectName(opts.project.name)

View file

@ -94,13 +94,19 @@ async function createNewUser(attributes, options = {}) {
emailData.samlProviderId = attributes.samlIdentifiers[0].providerId
}
const affiliationOptions = options.affiliationOptions || {}
if (options.confirmedAt) {
emailData.confirmedAt = options.confirmedAt
affiliationOptions.confirmedAt = options.confirmedAt
}
user.emails = [emailData]
user = await user.save()
if (Features.hasFeature('affiliations')) {
try {
user = await _addAffiliation(user, options.affiliationOptions || {})
user = await _addAffiliation(user, affiliationOptions)
} catch (error) {
if (options.requireAffiliation) {
await UserDeleter.promises.deleteMongoUser(user._id)

View file

@ -6,10 +6,12 @@ const Errors = require('../Errors/Errors')
const UserUpdater = require('./UserUpdater')
const UserGetter = require('./UserGetter')
const { callbackify, promisify } = require('util')
const crypto = require('crypto')
// Reject email confirmation tokens after 90 days
const TOKEN_EXPIRY_IN_S = 90 * 24 * 60 * 60
const TOKEN_USE = 'email_confirmation'
const CONFIRMATION_CODE_EXPIRY_IN_S = 10 * 60
function sendConfirmationEmail(userId, email, emailTemplate, callback) {
if (arguments.length === 3) {
@ -46,6 +48,26 @@ function sendConfirmationEmail(userId, email, emailTemplate, callback) {
)
}
async function sendConfirmationCode(email) {
if (!EmailHelper.parseEmail(email)) {
throw new Error('invalid email')
}
const confirmCode = crypto.randomInt(0, 1_000_000).toString().padStart(6, '0')
const confirmCodeExpiresTimestamp =
Date.now() + CONFIRMATION_CODE_EXPIRY_IN_S * 1000
await EmailHandler.promises.sendEmail('confirmCode', {
to: email,
confirmCode,
})
return {
confirmCode,
confirmCodeExpiresTimestamp,
}
}
async function sendReconfirmationEmail(userId, email) {
email = EmailHelper.parseEmail(email)
if (!email) {
@ -112,6 +134,10 @@ const UserEmailsConfirmationHandler = {
UserEmailsConfirmationHandler.promises = {
sendConfirmationEmail: promisify(sendConfirmationEmail),
confirmEmailFromToken: promisify(
UserEmailsConfirmationHandler.confirmEmailFromToken
),
sendConfirmationCode,
}
module.exports = UserEmailsConfirmationHandler