2018-09-01 13:58:06 -04:00
'use strict' ;
2016-07-19 01:10:36 -05:00
2023-01-09 08:02:12 +11:00
const Auth = require ( '../lib/Auth' ) ;
2018-07-02 23:30:14 -04:00
const Config = require ( '../lib/Config' ) ;
2018-09-24 17:07:51 -04:00
const request = require ( '../lib/request' ) ;
2025-04-15 11:25:42 -05:00
const { resolvingPromise , sleep } = require ( '../lib/TestUtils' ) ;
2024-01-06 16:41:13 +01:00
const MockEmailAdapterWithOptions = require ( './support/MockEmailAdapterWithOptions' ) ;
2016-07-19 01:10:36 -05:00
2025-04-15 11:25:42 -05:00
describe ( 'Email Verification Token Expiration:' , ( ) => {
it ( 'show the invalid verification link page, if the user clicks on the verify email link after the email verify token expires' , async ( ) => {
2018-02-17 09:55:30 -05:00
const user = new Parse . User ( ) ;
let sendEmailOptions ;
2025-04-15 11:25:42 -05:00
const sendPromise = resolvingPromise ( ) ;
2018-02-17 09:55:30 -05:00
const emailAdapter = {
2016-07-19 01:10:36 -05:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
2025-04-15 11:25:42 -05:00
sendPromise . resolve ( ) ;
2016-07-19 01:10:36 -05:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
} ;
2025-04-15 11:25:42 -05:00
await reconfigureServer ( {
2016-07-19 01:10:36 -05:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 0.5 , // 0.5 second
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2025-04-15 11:25:42 -05:00
} ) ;
user . setUsername ( 'testEmailVerifyTokenValidity' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@parse.com' ) ;
await user . signUp ( ) ;
await sendPromise ;
// wait for 1 second - simulate user behavior to some extent
await sleep ( 1000 ) ;
2016-07-19 01:10:36 -05:00
2025-04-15 11:25:42 -05:00
expect ( sendEmailOptions ) . not . toBeUndefined ( ) ;
const response = await request ( {
url : sendEmailOptions . link ,
followRedirects : false ,
} ) ;
2025-12-12 20:55:39 +01:00
expect ( response . status ) . toEqual ( 200 ) ;
expect ( response . text ) . toContain ( 'Invalid verification link!' ) ;
2016-07-19 01:10:36 -05:00
} ) ;
2025-04-15 11:25:42 -05:00
it ( 'emailVerified should set to false, if the user does not verify their email before the email verify token expires' , async ( ) => {
2018-02-17 09:55:30 -05:00
const user = new Parse . User ( ) ;
let sendEmailOptions ;
2025-04-15 11:25:42 -05:00
const sendPromise = resolvingPromise ( ) ;
2018-02-17 09:55:30 -05:00
const emailAdapter = {
2016-07-19 01:10:36 -05:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
2025-04-15 11:25:42 -05:00
sendPromise . resolve ( ) ;
2016-07-19 01:10:36 -05:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
} ;
2025-04-15 11:25:42 -05:00
await reconfigureServer ( {
2016-07-19 01:10:36 -05:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 0.5 , // 0.5 second
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2025-04-15 11:25:42 -05:00
} ) ;
user . setUsername ( 'testEmailVerifyTokenValidity' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@parse.com' ) ;
await user . signUp ( ) ;
await sendPromise ;
// wait for 1 second - simulate user behavior to some extent
await sleep ( 1000 ) ;
2016-07-19 01:10:36 -05:00
2025-04-15 11:25:42 -05:00
expect ( sendEmailOptions ) . not . toBeUndefined ( ) ;
const response = await request ( {
url : sendEmailOptions . link ,
followRedirects : false ,
} ) ;
2025-12-12 20:55:39 +01:00
expect ( response . status ) . toEqual ( 200 ) ;
2025-04-15 11:25:42 -05:00
await user . fetch ( ) ;
expect ( user . get ( 'emailVerified' ) ) . toEqual ( false ) ;
2016-07-19 01:10:36 -05:00
} ) ;
2025-04-15 11:25:42 -05:00
it _id ( 'f20dd3c2-87d9-4bc6-a51d-4ea2834acbcc' ) ( it ) ( 'if user clicks on the email verify link before email verification token expiration then show the verify email success page' , async ( ) => {
2018-02-17 09:55:30 -05:00
const user = new Parse . User ( ) ;
let sendEmailOptions ;
2025-04-15 11:25:42 -05:00
const sendPromise = resolvingPromise ( ) ;
2018-02-17 09:55:30 -05:00
const emailAdapter = {
2016-07-19 01:10:36 -05:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
2025-04-15 11:25:42 -05:00
sendPromise . resolve ( ) ;
2016-07-19 01:10:36 -05:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
} ;
2025-04-15 11:25:42 -05:00
await reconfigureServer ( {
2016-07-19 01:10:36 -05:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2025-04-15 11:25:42 -05:00
} ) ;
user . setUsername ( 'testEmailVerifyTokenValidity' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@parse.com' ) ;
await user . signUp ( ) ;
await sendPromise ;
const response = await request ( {
url : sendEmailOptions . link ,
followRedirects : false ,
} ) ;
2025-12-12 20:55:39 +01:00
expect ( response . status ) . toEqual ( 200 ) ;
expect ( response . text ) . toContain ( 'Email verified!' ) ;
2016-07-19 01:10:36 -05:00
} ) ;
2025-04-15 11:25:42 -05:00
it _id ( '94956799-c85e-4297-b879-e2d1f985394c' ) ( it ) ( 'if user clicks on the email verify link before email verification token expiration then emailVerified should be true' , async ( ) => {
2018-02-17 09:55:30 -05:00
const user = new Parse . User ( ) ;
let sendEmailOptions ;
2025-04-15 11:25:42 -05:00
const sendPromise = resolvingPromise ( ) ;
2018-02-17 09:55:30 -05:00
const emailAdapter = {
2016-07-19 01:10:36 -05:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
2025-04-15 11:25:42 -05:00
sendPromise . resolve ( ) ;
2016-07-19 01:10:36 -05:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
} ;
2025-04-15 11:25:42 -05:00
await reconfigureServer ( {
2016-07-19 01:10:36 -05:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2025-04-15 11:25:42 -05:00
} ) ;
user . setUsername ( 'testEmailVerifyTokenValidity' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@parse.com' ) ;
await user . signUp ( ) ;
await sendPromise ;
const response = await request ( {
url : sendEmailOptions . link ,
followRedirects : false ,
} ) ;
2025-12-12 20:55:39 +01:00
expect ( response . status ) . toEqual ( 200 ) ;
2025-04-15 11:25:42 -05:00
await user . fetch ( ) ;
expect ( user . get ( 'emailVerified' ) ) . toEqual ( true ) ;
2016-07-19 01:10:36 -05:00
} ) ;
2025-04-15 11:25:42 -05:00
it _id ( '25f3f895-c987-431c-9841-17cb6aaf18b5' ) ( it ) ( 'if user clicks on the email verify link before email verification token expiration then user should be able to login' , async ( ) => {
2018-02-17 09:55:30 -05:00
const user = new Parse . User ( ) ;
let sendEmailOptions ;
2025-04-15 11:25:42 -05:00
const sendPromise = resolvingPromise ( ) ;
2018-02-17 09:55:30 -05:00
const emailAdapter = {
2016-07-19 01:10:36 -05:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
2025-04-15 11:25:42 -05:00
sendPromise . resolve ( ) ;
2016-07-19 01:10:36 -05:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
} ;
2025-04-15 11:25:42 -05:00
await reconfigureServer ( {
2016-07-19 01:10:36 -05:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2025-04-15 11:25:42 -05:00
} ) ;
user . setUsername ( 'testEmailVerifyTokenValidity' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@parse.com' ) ;
await user . signUp ( ) ;
await sendPromise ;
const response = await request ( {
url : sendEmailOptions . link ,
followRedirects : false ,
} ) ;
2025-12-12 20:55:39 +01:00
expect ( response . status ) . toEqual ( 200 ) ;
2025-04-15 11:25:42 -05:00
const verifiedUser = await Parse . User . logIn ( 'testEmailVerifyTokenValidity' , 'expiringToken' ) ;
expect ( typeof verifiedUser ) . toBe ( 'object' ) ;
expect ( verifiedUser . get ( 'emailVerified' ) ) . toBe ( true ) ;
2016-07-19 01:10:36 -05:00
} ) ;
2025-04-15 11:25:42 -05:00
it _id ( 'c6a3e188-9065-4f50-842d-454d1e82f289' ) ( it ) ( 'sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp' , async ( ) => {
2018-02-17 09:55:30 -05:00
const user = new Parse . User ( ) ;
let sendEmailOptions ;
2025-04-15 11:25:42 -05:00
const sendPromise = resolvingPromise ( ) ;
2018-02-17 09:55:30 -05:00
const emailAdapter = {
2016-07-19 01:10:36 -05:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
2025-04-15 11:25:42 -05:00
sendPromise . resolve ( ) ;
2016-07-19 01:10:36 -05:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
} ;
2025-04-15 11:25:42 -05:00
await reconfigureServer ( {
2016-07-19 01:10:36 -05:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2025-04-15 11:25:42 -05:00
} ) ;
user . setUsername ( 'sets_email_verify_token_expires_at' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@parse.com' ) ;
await user . signUp ( ) ;
await sendPromise ;
const config = Config . get ( 'test' ) ;
const results = await config . database . find (
'_User' ,
{
username : 'sets_email_verify_token_expires_at' ,
} ,
{ } ,
Auth . maintenance ( config )
) ;
expect ( results . length ) . toBe ( 1 ) ;
const verifiedUser = results [ 0 ] ;
expect ( typeof verifiedUser ) . toBe ( 'object' ) ;
expect ( verifiedUser . emailVerified ) . toEqual ( false ) ;
expect ( typeof verifiedUser . _email _verify _token ) . toBe ( 'string' ) ;
expect ( typeof verifiedUser . _email _verify _token _expires _at ) . toBe ( 'object' ) ;
expect ( sendEmailOptions ) . toBeDefined ( ) ;
2016-07-19 01:10:36 -05:00
} ) ;
2025-03-02 12:32:43 +11:00
it ( 'can resend email using an expired token' , async ( ) => {
const user = new Parse . User ( ) ;
const emailAdapter = {
sendVerificationEmail : ( ) => { } ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
sendMail : ( ) => { } ,
} ;
await reconfigureServer ( {
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
publicServerURL : 'http://localhost:8378/1' ,
} ) ;
user . setUsername ( 'test' ) ;
user . setPassword ( 'password' ) ;
user . set ( 'email' , 'user@example.com' ) ;
await user . signUp ( ) ;
await Parse . Server . database . update (
'_User' ,
{ objectId : user . id } ,
{
_email _verify _token _expires _at : Parse . _encode ( new Date ( '2000' ) ) ,
}
) ;
const obj = await Parse . Server . database . find (
'_User' ,
{ objectId : user . id } ,
{ } ,
Auth . maintenance ( Parse . Server )
) ;
const token = obj [ 0 ] . _email _verify _token ;
const res = await request ( {
url : ` http://localhost:8378/1/apps/test/verify_email?token= ${ token } ` ,
method : 'GET' ,
} ) ;
2025-12-12 20:55:39 +01:00
expect ( res . text ) . toContain ( 'Invalid verification link!' ) ;
2025-03-02 12:32:43 +11:00
const formUrl = ` http://localhost:8378/1/apps/test/resend_verification_email ` ;
const formResponse = await request ( {
url : formUrl ,
method : 'POST' ,
body : {
token : token ,
} ,
headers : { 'Content-Type' : 'application/x-www-form-urlencoded' } ,
followRedirects : false ,
} ) ;
2025-12-12 20:55:39 +01:00
expect ( formResponse . text ) . toContain ( 'email_verification_send_success.html' ) ;
2025-03-02 12:32:43 +11:00
} ) ;
2024-08-13 22:13:19 +02:00
it _id ( '9365c53c-b8b4-41f7-a3c1-77882f76a89c' ) ( it ) ( 'can conditionally send emails' , async ( ) => {
2023-06-23 16:29:32 +02:00
let sendEmailOptions ;
const emailAdapter = {
sendVerificationEmail : options => {
sendEmailOptions = options ;
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
sendMail : ( ) => { } ,
} ;
const verifyUserEmails = {
method ( req ) {
2026-02-06 04:48:35 +01:00
expect ( Object . keys ( req ) ) . toEqual ( [
'original' ,
'object' ,
'master' ,
'ip' ,
'installationId' ,
'createdWith' ,
] ) ;
expect ( req . createdWith ) . toEqual ( { action : 'signup' , authProvider : 'password' } ) ;
2023-06-23 16:29:32 +02:00
return false ;
} ,
} ;
const verifySpy = spyOn ( verifyUserEmails , 'method' ) . and . callThrough ( ) ;
await reconfigureServer ( {
appName : 'emailVerifyToken' ,
verifyUserEmails : verifyUserEmails . method ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
publicServerURL : 'http://localhost:8378/1' ,
} ) ;
const beforeSave = {
method ( req ) {
req . object . set ( 'emailVerified' , true ) ;
} ,
} ;
const saveSpy = spyOn ( beforeSave , 'method' ) . and . callThrough ( ) ;
const emailSpy = spyOn ( emailAdapter , 'sendVerificationEmail' ) . and . callThrough ( ) ;
Parse . Cloud . beforeSave ( Parse . User , beforeSave . method ) ;
const user = new Parse . User ( ) ;
user . setUsername ( 'sets_email_verify_token_expires_at' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@example.com' ) ;
await user . signUp ( ) ;
const config = Config . get ( 'test' ) ;
const results = await config . database . find (
'_User' ,
{
username : 'sets_email_verify_token_expires_at' ,
} ,
{ } ,
Auth . maintenance ( config )
) ;
expect ( results . length ) . toBe ( 1 ) ;
const user _data = results [ 0 ] ;
expect ( typeof user _data ) . toBe ( 'object' ) ;
expect ( user _data . emailVerified ) . toEqual ( true ) ;
expect ( user _data . _email _verify _token ) . toBeUndefined ( ) ;
expect ( user _data . _email _verify _token _expires _at ) . toBeUndefined ( ) ;
expect ( emailSpy ) . not . toHaveBeenCalled ( ) ;
expect ( saveSpy ) . toHaveBeenCalled ( ) ;
expect ( sendEmailOptions ) . toBeUndefined ( ) ;
expect ( verifySpy ) . toHaveBeenCalled ( ) ;
} ) ;
2024-08-13 22:13:19 +02:00
it _id ( 'b3549300-bed7-4a5e-bed5-792dbfead960' ) ( it ) ( 'can conditionally send emails and allow conditional login' , async ( ) => {
2023-06-23 16:29:32 +02:00
let sendEmailOptions ;
2025-04-15 11:25:42 -05:00
const sendPromise = resolvingPromise ( ) ;
2023-06-23 16:29:32 +02:00
const emailAdapter = {
sendVerificationEmail : options => {
sendEmailOptions = options ;
2025-04-15 11:25:42 -05:00
sendPromise . resolve ( ) ;
2023-06-23 16:29:32 +02:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
sendMail : ( ) => { } ,
} ;
const verifyUserEmails = {
method ( req ) {
2026-02-06 04:48:35 +01:00
expect ( Object . keys ( req ) ) . toEqual ( [
'original' ,
'object' ,
'master' ,
'ip' ,
'installationId' ,
'createdWith' ,
] ) ;
expect ( req . createdWith ) . toEqual ( { action : 'signup' , authProvider : 'password' } ) ;
2023-06-23 16:29:32 +02:00
if ( req . object . get ( 'username' ) === 'no_email' ) {
return false ;
}
return true ;
} ,
} ;
const verifySpy = spyOn ( verifyUserEmails , 'method' ) . and . callThrough ( ) ;
await reconfigureServer ( {
appName : 'emailVerifyToken' ,
verifyUserEmails : verifyUserEmails . method ,
preventLoginWithUnverifiedEmail : verifyUserEmails . method ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
publicServerURL : 'http://localhost:8378/1' ,
} ) ;
const user = new Parse . User ( ) ;
user . setUsername ( 'no_email' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@example.com' ) ;
await user . signUp ( ) ;
expect ( sendEmailOptions ) . toBeUndefined ( ) ;
expect ( user . getSessionToken ( ) ) . toBeDefined ( ) ;
expect ( verifySpy ) . toHaveBeenCalledTimes ( 2 ) ;
const user2 = new Parse . User ( ) ;
user2 . setUsername ( 'email' ) ;
user2 . setPassword ( 'expiringToken' ) ;
user2 . set ( 'email' , 'user2@example.com' ) ;
await user2 . signUp ( ) ;
2025-04-15 11:25:42 -05:00
await sendPromise ;
2023-06-23 16:29:32 +02:00
expect ( user2 . getSessionToken ( ) ) . toBeUndefined ( ) ;
expect ( sendEmailOptions ) . toBeDefined ( ) ;
2023-12-26 21:01:27 +01:00
expect ( verifySpy ) . toHaveBeenCalledTimes ( 5 ) ;
2023-06-23 16:29:32 +02:00
} ) ;
2026-02-06 04:48:35 +01:00
it ( 'provides createdWith on signup when verification blocks session creation' , async ( ) => {
const verifyUserEmails = {
method : params => {
expect ( params . object ) . toBeInstanceOf ( Parse . User ) ;
expect ( params . createdWith ) . toEqual ( { action : 'signup' , authProvider : 'password' } ) ;
return true ;
} ,
} ;
const verifySpy = spyOn ( verifyUserEmails , 'method' ) . and . callThrough ( ) ;
await reconfigureServer ( {
appName : 'emailVerifyToken' ,
verifyUserEmails : verifyUserEmails . method ,
preventLoginWithUnverifiedEmail : true ,
preventSignupWithUnverifiedEmail : true ,
emailAdapter : MockEmailAdapterWithOptions ( {
fromAddress : 'parse@example.com' ,
apiKey : 'k' ,
domain : 'd' ,
} ) ,
publicServerURL : 'http://localhost:8378/1' ,
} ) ;
const user = new Parse . User ( ) ;
user . setUsername ( 'signup_created_with' ) ;
user . setPassword ( 'pass' ) ;
user . setEmail ( 'signup@example.com' ) ;
const res = await user . signUp ( ) . catch ( e => e ) ;
expect ( res . message ) . toBe ( 'User email is not verified.' ) ;
expect ( user . getSessionToken ( ) ) . toBeUndefined ( ) ;
expect ( verifySpy ) . toHaveBeenCalledTimes ( 2 ) ; // before signup completion and on preventLoginWithUnverifiedEmail
} ) ;
it ( 'provides createdWith with auth provider on login verification' , async ( ) => {
const user = new Parse . User ( ) ;
user . setUsername ( 'user_created_with_login' ) ;
user . setPassword ( 'pass' ) ;
user . set ( 'email' , 'login@example.com' ) ;
await user . signUp ( ) ;
const verifyUserEmails = {
method : async params => {
expect ( params . object ) . toBeInstanceOf ( Parse . User ) ;
expect ( params . createdWith ) . toEqual ( { action : 'login' , authProvider : 'password' } ) ;
return true ;
} ,
} ;
const verifyUserEmailsSpy = spyOn ( verifyUserEmails , 'method' ) . and . callThrough ( ) ;
await reconfigureServer ( {
appName : 'emailVerifyToken' ,
publicServerURL : 'http://localhost:8378/1' ,
verifyUserEmails : verifyUserEmails . method ,
preventLoginWithUnverifiedEmail : verifyUserEmails . method ,
preventSignupWithUnverifiedEmail : true ,
emailAdapter : MockEmailAdapterWithOptions ( {
fromAddress : 'parse@example.com' ,
apiKey : 'k' ,
domain : 'd' ,
} ) ,
} ) ;
const res = await Parse . User . logIn ( 'user_created_with_login' , 'pass' ) . catch ( e => e ) ;
expect ( res . code ) . toBe ( 205 ) ;
expect ( verifyUserEmailsSpy ) . toHaveBeenCalledTimes ( 2 ) ; // before login completion and on preventLoginWithUnverifiedEmail
} ) ;
it ( 'provides createdWith with auth provider on signup verification' , async ( ) => {
const createdWithValues = [ ] ;
const verifyUserEmails = {
method : params => {
createdWithValues . push ( params . createdWith ) ;
return true ;
} ,
} ;
const verifySpy = spyOn ( verifyUserEmails , 'method' ) . and . callThrough ( ) ;
await reconfigureServer ( {
appName : 'emailVerifyToken' ,
verifyUserEmails : verifyUserEmails . method ,
preventLoginWithUnverifiedEmail : true ,
preventSignupWithUnverifiedEmail : true ,
emailAdapter : MockEmailAdapterWithOptions ( {
fromAddress : 'parse@example.com' ,
apiKey : 'k' ,
domain : 'd' ,
} ) ,
publicServerURL : 'http://localhost:8378/1' ,
} ) ;
const provider = {
authData : { id : '8675309' , access _token : 'jenny' } ,
shouldError : false ,
authenticate ( options ) {
options . success ( this , this . authData ) ;
} ,
restoreAuthentication ( ) {
return true ;
} ,
getAuthType ( ) {
return 'facebook' ;
} ,
deauthenticate ( ) { } ,
} ;
Parse . User . _registerAuthenticationProvider ( provider ) ;
const res = await Parse . User . _logInWith ( 'facebook' ) . catch ( e => e ) ;
expect ( res . message ) . toBe ( 'User email is not verified.' ) ;
// Called once in createSessionTokenIfNeeded (no email set, so _validateEmail skips)
expect ( verifySpy ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( createdWithValues [ 0 ] ) . toEqual ( { action : 'signup' , authProvider : 'facebook' } ) ;
} ) ;
it ( 'provides createdWith for preventLoginWithUnverifiedEmail function' , async ( ) => {
const user = new Parse . User ( ) ;
user . setUsername ( 'user_prevent_login_fn' ) ;
user . setPassword ( 'pass' ) ;
user . set ( 'email' , 'preventlogin@example.com' ) ;
await user . signUp ( ) ;
const preventLoginCreatedWith = [ ] ;
await reconfigureServer ( {
appName : 'emailVerifyToken' ,
publicServerURL : 'http://localhost:8378/1' ,
verifyUserEmails : true ,
preventLoginWithUnverifiedEmail : params => {
preventLoginCreatedWith . push ( params . createdWith ) ;
return true ;
} ,
emailAdapter : MockEmailAdapterWithOptions ( {
fromAddress : 'parse@example.com' ,
apiKey : 'k' ,
domain : 'd' ,
} ) ,
} ) ;
const res = await Parse . User . logIn ( 'user_prevent_login_fn' , 'pass' ) . catch ( e => e ) ;
expect ( res . code ) . toBe ( 205 ) ;
expect ( preventLoginCreatedWith . length ) . toBe ( 1 ) ;
expect ( preventLoginCreatedWith [ 0 ] ) . toEqual ( { action : 'login' , authProvider : 'password' } ) ;
} ) ;
2024-08-13 22:13:19 +02:00
it _id ( 'd812de87-33d1-495e-a6e8-3485f6dc3589' ) ( it ) ( 'can conditionally send user email verification' , async ( ) => {
2023-06-23 16:29:32 +02:00
const emailAdapter = {
sendVerificationEmail : ( ) => { } ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
sendMail : ( ) => { } ,
} ;
const sendVerificationEmail = {
method ( req ) {
expect ( req . user ) . toBeDefined ( ) ;
expect ( req . master ) . toBeDefined ( ) ;
return false ;
} ,
} ;
const sendSpy = spyOn ( sendVerificationEmail , 'method' ) . and . callThrough ( ) ;
await reconfigureServer ( {
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
publicServerURL : 'http://localhost:8378/1' ,
sendUserEmailVerification : sendVerificationEmail . method ,
} ) ;
const emailSpy = spyOn ( emailAdapter , 'sendVerificationEmail' ) . and . callThrough ( ) ;
const newUser = new Parse . User ( ) ;
newUser . setUsername ( 'unsets_email_verify_token_expires_at' ) ;
newUser . setPassword ( 'expiringToken' ) ;
newUser . set ( 'email' , 'user@example.com' ) ;
await newUser . signUp ( ) ;
await Parse . User . requestEmailVerification ( 'user@example.com' ) ;
2025-04-15 11:25:42 -05:00
await sleep ( 100 ) ;
2023-06-23 16:29:32 +02:00
expect ( sendSpy ) . toHaveBeenCalledTimes ( 2 ) ;
expect ( emailSpy ) . toHaveBeenCalledTimes ( 0 ) ;
} ) ;
2024-08-16 15:21:56 -04:00
it _id ( 'd98babc1-feb8-4b5e-916c-57dc0a6ed9fb' ) ( it ) ( 'provides full user object in email verification function on email and username change' , async ( ) => {
2024-01-15 15:44:49 +01:00
const emailAdapter = {
sendVerificationEmail : ( ) => { } ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
sendMail : ( ) => { } ,
} ;
const sendVerificationEmail = {
method ( req ) {
expect ( req . user ) . toBeDefined ( ) ;
expect ( req . user . id ) . toBeDefined ( ) ;
expect ( req . user . get ( 'createdAt' ) ) . toBeDefined ( ) ;
expect ( req . user . get ( 'updatedAt' ) ) . toBeDefined ( ) ;
expect ( req . master ) . toBeDefined ( ) ;
return false ;
} ,
} ;
await reconfigureServer ( {
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 ,
publicServerURL : 'http://localhost:8378/1' ,
sendUserEmailVerification : sendVerificationEmail . method ,
} ) ;
const user = new Parse . User ( ) ;
user . setPassword ( 'password' ) ;
user . setUsername ( 'new@example.com' ) ;
user . setEmail ( 'user@example.com' ) ;
await user . save ( null , { useMasterKey : true } ) ;
// Update email and username
user . setUsername ( 'new@example.com' ) ;
user . setEmail ( 'new@example.com' ) ;
await user . save ( null , { useMasterKey : true } ) ;
} ) ;
2024-08-13 22:13:19 +02:00
it _id ( 'a8c1f820-822f-4a37-9d08-a968cac8369d' ) ( it ) ( 'beforeSave options do not change existing behaviour' , async ( ) => {
2023-06-23 16:29:32 +02:00
let sendEmailOptions ;
2025-04-15 11:25:42 -05:00
const sendPromise = resolvingPromise ( ) ;
2023-06-23 16:29:32 +02:00
const emailAdapter = {
sendVerificationEmail : options => {
sendEmailOptions = options ;
2025-04-15 11:25:42 -05:00
sendPromise . resolve ( ) ;
2023-06-23 16:29:32 +02:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
sendMail : ( ) => { } ,
} ;
await reconfigureServer ( {
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
publicServerURL : 'http://localhost:8378/1' ,
} ) ;
const emailSpy = spyOn ( emailAdapter , 'sendVerificationEmail' ) . and . callThrough ( ) ;
const newUser = new Parse . User ( ) ;
newUser . setUsername ( 'unsets_email_verify_token_expires_at' ) ;
newUser . setPassword ( 'expiringToken' ) ;
newUser . set ( 'email' , 'user@parse.com' ) ;
await newUser . signUp ( ) ;
2025-04-15 11:25:42 -05:00
await sendPromise ;
2023-06-23 16:29:32 +02:00
const response = await request ( {
url : sendEmailOptions . link ,
followRedirects : false ,
} ) ;
2025-12-12 20:55:39 +01:00
expect ( response . status ) . toEqual ( 200 ) ;
2023-06-23 16:29:32 +02:00
const config = Config . get ( 'test' ) ;
const results = await config . database . find ( '_User' , {
username : 'unsets_email_verify_token_expires_at' ,
} ) ;
expect ( results . length ) . toBe ( 1 ) ;
const user = results [ 0 ] ;
expect ( typeof user ) . toBe ( 'object' ) ;
expect ( user . emailVerified ) . toEqual ( true ) ;
expect ( typeof user . _email _verify _token ) . toBe ( 'undefined' ) ;
expect ( typeof user . _email _verify _token _expires _at ) . toBe ( 'undefined' ) ;
expect ( emailSpy ) . toHaveBeenCalled ( ) ;
} ) ;
2025-04-15 11:25:42 -05:00
it _id ( '36d277eb-ec7c-4a39-9108-435b68228741' ) ( it ) ( 'unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful' , async ( ) => {
2018-02-17 09:55:30 -05:00
const user = new Parse . User ( ) ;
let sendEmailOptions ;
2025-04-15 11:25:42 -05:00
const sendPromise = resolvingPromise ( ) ;
2018-02-17 09:55:30 -05:00
const emailAdapter = {
2016-07-19 01:10:36 -05:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
2025-04-15 11:25:42 -05:00
sendPromise . resolve ( ) ;
2016-07-19 01:10:36 -05:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
} ;
2025-04-15 11:25:42 -05:00
await reconfigureServer ( {
2016-07-19 01:10:36 -05:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2025-04-15 11:25:42 -05:00
} ) ;
user . setUsername ( 'unsets_email_verify_token_expires_at' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@parse.com' ) ;
await user . signUp ( ) ;
await sendPromise ;
const response = await request ( {
url : sendEmailOptions . link ,
followRedirects : false ,
} ) ;
2025-12-12 20:55:39 +01:00
expect ( response . status ) . toEqual ( 200 ) ;
2025-04-15 11:25:42 -05:00
const config = Config . get ( 'test' ) ;
const results = await config . database . find ( '_User' , {
username : 'unsets_email_verify_token_expires_at' ,
} ) ;
expect ( results . length ) . toBe ( 1 ) ;
const verifiedUser = results [ 0 ] ;
expect ( typeof verifiedUser ) . toBe ( 'object' ) ;
expect ( verifiedUser . emailVerified ) . toEqual ( true ) ;
expect ( typeof verifiedUser . _email _verify _token ) . toBe ( 'undefined' ) ;
expect ( typeof verifiedUser . _email _verify _token _expires _at ) . toBe ( 'undefined' ) ;
2016-07-19 01:10:36 -05:00
} ) ;
2025-04-15 11:25:42 -05:00
it _id ( '4f444704-ec4b-4dff-b947-614b1c6971c4' ) ( it ) ( 'clicking on the email verify link by an email VERIFIED user that was setup before enabling the expire email verify token should show email verify email success' , async ( ) => {
2018-02-17 09:55:30 -05:00
const user = new Parse . User ( ) ;
let sendEmailOptions ;
2025-04-15 11:25:42 -05:00
const sendPromise = resolvingPromise ( ) ;
2018-02-17 09:55:30 -05:00
const emailAdapter = {
2016-07-19 01:10:36 -05:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
2025-04-15 11:25:42 -05:00
sendPromise . resolve ( ) ;
2016-07-19 01:10:36 -05:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
} ;
2018-02-17 09:55:30 -05:00
const serverConfig = {
2016-07-19 01:10:36 -05:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2016-07-19 01:10:36 -05:00
} ;
// setup server WITHOUT enabling the expire email verify token flag
2025-04-15 11:25:42 -05:00
await reconfigureServer ( serverConfig ) ;
user . setUsername ( 'testEmailVerifyTokenValidity' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@parse.com' ) ;
await user . signUp ( ) ;
await sendPromise ;
let response = await request ( {
url : sendEmailOptions . link ,
followRedirects : false ,
} ) ;
2025-12-12 20:55:39 +01:00
expect ( response . status ) . toEqual ( 200 ) ;
2025-04-15 11:25:42 -05:00
await user . fetch ( ) ;
expect ( user . get ( 'emailVerified' ) ) . toEqual ( true ) ;
// RECONFIGURE the server i.e., ENABLE the expire email verify token flag
serverConfig . emailVerifyTokenValidityDuration = 5 ; // 5 seconds
await reconfigureServer ( serverConfig ) ;
response = await request ( {
url : sendEmailOptions . link ,
followRedirects : false ,
} ) ;
2025-12-12 20:55:39 +01:00
expect ( response . status ) . toEqual ( 200 ) ;
expect ( response . text ) . toContain ( 'Invalid verification link!' ) ;
2016-07-19 01:10:36 -05:00
} ) ;
2025-04-15 11:25:42 -05:00
it ( 'clicking on the email verify link by an email UNVERIFIED user that was setup before enabling the expire email verify token should show invalid verficiation link page' , async ( ) => {
2018-02-17 09:55:30 -05:00
const user = new Parse . User ( ) ;
let sendEmailOptions ;
2025-04-15 11:25:42 -05:00
const sendPromise = resolvingPromise ( ) ;
2018-02-17 09:55:30 -05:00
const emailAdapter = {
2016-07-19 01:10:36 -05:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
2025-04-15 11:25:42 -05:00
sendPromise . resolve ( ) ;
2016-07-19 01:10:36 -05:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
} ;
2018-02-17 09:55:30 -05:00
const serverConfig = {
2016-07-19 01:10:36 -05:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2016-07-19 01:10:36 -05:00
} ;
// setup server WITHOUT enabling the expire email verify token flag
2025-04-15 11:25:42 -05:00
await reconfigureServer ( serverConfig ) ;
user . setUsername ( 'testEmailVerifyTokenValidity' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@parse.com' ) ;
await user . signUp ( ) ;
await sendPromise ;
// just get the user again - DO NOT email verify the user
await user . fetch ( ) ;
expect ( user . get ( 'emailVerified' ) ) . toEqual ( false ) ;
// RECONFIGURE the server i.e., ENABLE the expire email verify token flag
serverConfig . emailVerifyTokenValidityDuration = 5 ; // 5 seconds
await reconfigureServer ( serverConfig ) ;
const response = await request ( {
url : sendEmailOptions . link ,
followRedirects : false ,
} ) ;
2025-12-12 20:55:39 +01:00
expect ( response . status ) . toEqual ( 200 ) ;
expect ( response . text ) . toContain ( 'Invalid verification link!' ) ;
2016-07-19 01:10:36 -05:00
} ) ;
2025-04-15 11:25:42 -05:00
it _id ( 'b6c87f35-d887-477d-bc86-a9217a424f53' ) ( it ) ( 'setting the email on the user should set a new email verification token and new expiration date for the token when expire email verify token flag is set' , async ( ) => {
2016-12-07 15:17:05 -08:00
const user = new Parse . User ( ) ;
2016-07-19 01:10:36 -05:00
let sendEmailOptions ;
2025-04-15 11:25:42 -05:00
const sendPromise = resolvingPromise ( ) ;
2016-12-07 15:17:05 -08:00
const emailAdapter = {
2016-07-19 01:10:36 -05:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
2025-04-15 11:25:42 -05:00
sendPromise . resolve ( ) ;
2016-07-19 01:10:36 -05:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
2016-07-19 01:10:36 -05:00
} ;
2016-12-07 15:17:05 -08:00
const serverConfig = {
2016-07-19 01:10:36 -05:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2016-07-19 01:10:36 -05:00
} ;
2025-04-15 11:25:42 -05:00
await reconfigureServer ( serverConfig ) ;
user . setUsername ( 'newEmailVerifyTokenOnEmailReset' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@parse.com' ) ;
await user . signUp ( ) ;
await sendPromise ;
const config = Config . get ( 'test' ) ;
const userFromDb = await config . database
. find ( '_User' , { username : 'newEmailVerifyTokenOnEmailReset' } )
. then ( results => {
return results [ 0 ] ;
} ) ;
expect ( typeof userFromDb ) . toBe ( 'object' ) ;
2025-07-04 20:24:08 +02:00
const userBeforeEmailReset = userFromDb ;
2016-07-19 01:10:36 -05:00
2025-04-15 11:25:42 -05:00
// trigger another token generation by setting the email
user . set ( 'email' , 'user@parse.com' ) ;
await new Promise ( resolve => {
// wait for half a sec to get a new expiration time
setTimeout ( ( ) => resolve ( user . save ( ) ) , 500 ) ;
} ) ;
const userAfterEmailReset = await config . database
. find (
'_User' ,
{ username : 'newEmailVerifyTokenOnEmailReset' } ,
{ } ,
Auth . maintenance ( config )
)
. then ( results => {
return results [ 0 ] ;
2016-08-15 16:48:39 -04:00
} ) ;
2025-04-15 11:25:42 -05:00
expect ( typeof userAfterEmailReset ) . toBe ( 'object' ) ;
expect ( userBeforeEmailReset . _email _verify _token ) . not . toEqual (
userAfterEmailReset . _email _verify _token
) ;
expect ( userBeforeEmailReset . _email _verify _token _expires _at ) . not . toEqual (
userAfterEmailReset . _email _verify _token _expires _at
) ;
expect ( sendEmailOptions ) . toBeDefined ( ) ;
2016-07-19 01:10:36 -05:00
} ) ;
2025-04-15 11:25:42 -05:00
it _id ( '28f2140d-48bd-44ac-a141-ba60ea8d9713' ) ( it ) ( 'should send a new verification email when a resend is requested and the user is UNVERIFIED' , async ( ) => {
2018-02-17 09:55:30 -05:00
const user = new Parse . User ( ) ;
let sendEmailOptions ;
let sendVerificationEmailCallCount = 0 ;
2025-04-15 11:25:42 -05:00
const promise1 = resolvingPromise ( ) ;
const promise2 = resolvingPromise ( ) ;
2018-02-17 09:55:30 -05:00
const emailAdapter = {
2017-03-04 13:30:52 -08:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
sendVerificationEmailCallCount ++ ;
2025-04-15 11:25:42 -05:00
if ( sendVerificationEmailCallCount === 1 ) {
promise1 . resolve ( ) ;
} else {
promise2 . resolve ( ) ;
}
2017-03-04 13:30:52 -08:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
} ;
2025-04-15 11:25:42 -05:00
await reconfigureServer ( {
2017-03-04 13:30:52 -08:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2025-04-15 11:25:42 -05:00
} ) ;
user . setUsername ( 'resends_verification_token' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@parse.com' ) ;
await user . signUp ( ) ;
await promise1 ;
const config = Config . get ( 'test' ) ;
const newUser = await config . database
. find ( '_User' , { username : 'resends_verification_token' } )
. then ( results => {
return results [ 0 ] ;
} ) ;
// store this user before we make our email request
2025-07-04 20:24:08 +02:00
const userBeforeRequest = newUser ;
2018-02-01 15:35:08 -08:00
2025-04-15 11:25:42 -05:00
expect ( sendVerificationEmailCallCount ) . toBe ( 1 ) ;
2017-03-04 13:30:52 -08:00
2025-04-15 11:25:42 -05:00
const response = await request ( {
url : 'http://localhost:8378/1/verificationEmailRequest' ,
method : 'POST' ,
body : {
email : 'user@parse.com' ,
} ,
headers : {
'X-Parse-Application-Id' : Parse . applicationId ,
'X-Parse-REST-API-Key' : 'rest' ,
'Content-Type' : 'application/json' ,
} ,
} ) ;
expect ( response . status ) . toBe ( 200 ) ;
await promise2 ;
expect ( sendVerificationEmailCallCount ) . toBe ( 2 ) ;
expect ( sendEmailOptions ) . toBeDefined ( ) ;
2018-02-01 15:35:08 -08:00
2025-04-15 11:25:42 -05:00
// query for this user again
const userAfterRequest = await config . database
. find ( '_User' , { username : 'resends_verification_token' } , { } , Auth . maintenance ( config ) )
. then ( results => {
return results [ 0 ] ;
2017-03-04 13:30:52 -08:00
} ) ;
2025-04-15 11:25:42 -05:00
// verify that our token & expiration has been changed for this new request
expect ( typeof userAfterRequest ) . toBe ( 'object' ) ;
expect ( userBeforeRequest . _email _verify _token ) . not . toEqual (
userAfterRequest . _email _verify _token
) ;
expect ( userBeforeRequest . _email _verify _token _expires _at ) . not . toEqual (
userAfterRequest . _email _verify _token _expires _at
) ;
2017-03-04 13:30:52 -08:00
} ) ;
2024-01-06 16:41:13 +01:00
it ( 'provides function arguments in verifyUserEmails on verificationEmailRequest' , async ( ) => {
const user = new Parse . User ( ) ;
user . setUsername ( 'user' ) ;
user . setPassword ( 'pass' ) ;
user . set ( 'email' , 'test@example.com' ) ;
await user . signUp ( ) ;
const verifyUserEmails = {
method : async ( params ) => {
expect ( params . object ) . toBeInstanceOf ( Parse . User ) ;
expect ( params . ip ) . toBeDefined ( ) ;
expect ( params . master ) . toBeDefined ( ) ;
expect ( params . installationId ) . toBeDefined ( ) ;
expect ( params . resendRequest ) . toBeTrue ( ) ;
2026-02-06 04:48:35 +01:00
expect ( params . createdWith ) . toBeUndefined ( ) ;
2024-01-06 16:41:13 +01:00
return true ;
} ,
} ;
const verifyUserEmailsSpy = spyOn ( verifyUserEmails , 'method' ) . and . callThrough ( ) ;
await reconfigureServer ( {
appName : 'test' ,
publicServerURL : 'http://localhost:1337/1' ,
verifyUserEmails : verifyUserEmails . method ,
preventLoginWithUnverifiedEmail : verifyUserEmails . method ,
preventSignupWithUnverifiedEmail : true ,
emailAdapter : MockEmailAdapterWithOptions ( {
fromAddress : 'parse@example.com' ,
apiKey : 'k' ,
domain : 'd' ,
} ) ,
} ) ;
await expectAsync ( Parse . User . requestEmailVerification ( 'test@example.com' ) ) . toBeResolved ( ) ;
expect ( verifyUserEmailsSpy ) . toHaveBeenCalledTimes ( 1 ) ;
} ) ;
2025-04-15 11:25:42 -05:00
it ( 'should throw with invalid emailVerifyTokenReuseIfValid' , async ( ) => {
2020-11-26 04:30:52 +11:00
const sendEmailOptions = [ ] ;
const emailAdapter = {
sendVerificationEmail : ( ) => Promise . resolve ( ) ,
sendPasswordResetEmail : options => {
sendEmailOptions . push ( options ) ;
} ,
sendMail : ( ) => { } ,
} ;
try {
await reconfigureServer ( {
appName : 'passwordPolicy' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 * 60 , // 5 minutes
emailVerifyTokenReuseIfValid : [ ] ,
publicServerURL : 'http://localhost:8378/1' ,
} ) ;
fail ( 'should have thrown.' ) ;
} catch ( e ) {
expect ( e ) . toBe ( 'emailVerifyTokenReuseIfValid must be a boolean value' ) ;
}
try {
await reconfigureServer ( {
appName : 'passwordPolicy' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenReuseIfValid : true ,
publicServerURL : 'http://localhost:8378/1' ,
} ) ;
fail ( 'should have thrown.' ) ;
} catch ( e ) {
expect ( e ) . toBe (
'You cannot use emailVerifyTokenReuseIfValid without emailVerifyTokenValidityDuration'
) ;
}
} ) ;
2025-04-15 11:25:42 -05:00
it _id ( '0e66b7f6-2c07-4117-a8b9-605aa31a1e29' ) ( it ) ( 'should match codes with emailVerifyTokenReuseIfValid' , async ( ) => {
2020-11-26 04:30:52 +11:00
let sendEmailOptions ;
let sendVerificationEmailCallCount = 0 ;
2025-04-15 11:25:42 -05:00
const promise1 = resolvingPromise ( ) ;
const promise2 = resolvingPromise ( ) ;
2020-11-26 04:30:52 +11:00
const emailAdapter = {
sendVerificationEmail : options => {
sendEmailOptions = options ;
sendVerificationEmailCallCount ++ ;
2025-04-15 11:25:42 -05:00
if ( sendVerificationEmailCallCount === 1 ) {
promise1 . resolve ( ) ;
} else {
promise2 . resolve ( ) ;
}
2020-11-26 04:30:52 +11:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
sendMail : ( ) => { } ,
} ;
await reconfigureServer ( {
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 * 60 , // 5 minutes
publicServerURL : 'http://localhost:8378/1' ,
emailVerifyTokenReuseIfValid : true ,
} ) ;
const user = new Parse . User ( ) ;
user . setUsername ( 'resends_verification_token' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@example.com' ) ;
await user . signUp ( ) ;
2025-04-15 11:25:42 -05:00
await promise1 ;
2020-11-26 04:30:52 +11:00
const config = Config . get ( 'test' ) ;
const [ userBeforeRequest ] = await config . database . find ( '_User' , {
username : 'resends_verification_token' ,
2024-01-14 01:37:20 +01:00
} , { } , Auth . maintenance ( config ) ) ;
2020-11-26 04:30:52 +11:00
// store this user before we make our email request
expect ( sendVerificationEmailCallCount ) . toBe ( 1 ) ;
await new Promise ( resolve => {
setTimeout ( ( ) => {
resolve ( ) ;
} , 1000 ) ;
} ) ;
const response = await request ( {
url : 'http://localhost:8378/1/verificationEmailRequest' ,
method : 'POST' ,
body : {
email : 'user@example.com' ,
} ,
headers : {
'X-Parse-Application-Id' : Parse . applicationId ,
'X-Parse-REST-API-Key' : 'rest' ,
'Content-Type' : 'application/json' ,
} ,
} ) ;
2025-04-15 11:25:42 -05:00
await promise2 ;
2020-11-26 04:30:52 +11:00
expect ( response . status ) . toBe ( 200 ) ;
expect ( sendVerificationEmailCallCount ) . toBe ( 2 ) ;
expect ( sendEmailOptions ) . toBeDefined ( ) ;
const [ userAfterRequest ] = await config . database . find ( '_User' , {
username : 'resends_verification_token' ,
2024-01-14 01:37:20 +01:00
} , { } , Auth . maintenance ( config ) ) ;
2020-11-26 04:30:52 +11:00
2024-01-14 01:37:20 +01:00
// Verify that token & expiration haven't been changed for this new request
2020-11-26 04:30:52 +11:00
expect ( typeof userAfterRequest ) . toBe ( 'object' ) ;
2024-01-14 01:37:20 +01:00
expect ( userBeforeRequest . _email _verify _token ) . toBeDefined ( ) ;
2020-11-26 04:30:52 +11:00
expect ( userBeforeRequest . _email _verify _token ) . toEqual ( userAfterRequest . _email _verify _token ) ;
2024-01-14 01:37:20 +01:00
expect ( userBeforeRequest . _email _verify _token _expires _at ) . toBeDefined ( ) ;
expect ( userBeforeRequest . _email _verify _token _expires _at ) . toEqual ( userAfterRequest . _email _verify _token _expires _at ) ;
2020-11-26 04:30:52 +11:00
} ) ;
2025-04-15 11:25:42 -05:00
it _id ( '1ed9a6c2-bebc-4813-af30-4f4a212544c2' ) ( it ) ( 'should not send a new verification email when a resend is requested and the user is VERIFIED' , async ( ) => {
2018-02-17 09:55:30 -05:00
const user = new Parse . User ( ) ;
let sendEmailOptions ;
let sendVerificationEmailCallCount = 0 ;
2025-04-15 11:25:42 -05:00
const sendPromise = resolvingPromise ( ) ;
2018-02-17 09:55:30 -05:00
const emailAdapter = {
2017-03-04 13:30:52 -08:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
sendVerificationEmailCallCount ++ ;
2025-04-15 11:25:42 -05:00
sendPromise . resolve ( ) ;
2017-03-04 13:30:52 -08:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
} ;
2025-04-15 11:25:42 -05:00
await reconfigureServer ( {
2017-03-04 13:30:52 -08:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2025-04-15 11:25:42 -05:00
} ) ;
user . setUsername ( 'no_new_verification_token_once_verified' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@parse.com' ) ;
await user . signUp ( ) ;
await sendPromise ;
let response = await request ( {
url : sendEmailOptions . link ,
followRedirects : false ,
} ) ;
2025-12-12 20:55:39 +01:00
expect ( response . status ) . toEqual ( 200 ) ;
2025-04-15 11:25:42 -05:00
expect ( sendVerificationEmailCallCount ) . toBe ( 1 ) ;
2017-06-20 09:15:26 -07:00
2025-04-15 11:25:42 -05:00
response = await request ( {
url : 'http://localhost:8378/1/verificationEmailRequest' ,
method : 'POST' ,
body : {
email : 'user@parse.com' ,
} ,
headers : {
'X-Parse-Application-Id' : Parse . applicationId ,
'X-Parse-REST-API-Key' : 'rest' ,
'Content-Type' : 'application/json' ,
} ,
} ) . then ( fail , res => res ) ;
expect ( response . status ) . toBe ( 400 ) ;
expect ( sendVerificationEmailCallCount ) . toBe ( 1 ) ;
2017-03-04 13:30:52 -08:00
} ) ;
2025-04-15 11:25:42 -05:00
it ( 'should not send a new verification email if this user does not exist' , async ( ) => {
2018-02-17 09:55:30 -05:00
let sendEmailOptions ;
let sendVerificationEmailCallCount = 0 ;
const emailAdapter = {
2017-03-04 13:30:52 -08:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
sendVerificationEmailCallCount ++ ;
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
} ;
2025-04-15 11:25:42 -05:00
await reconfigureServer ( {
2017-03-04 13:30:52 -08:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2025-04-15 11:25:42 -05:00
} ) ;
const response = await request ( {
url : 'http://localhost:8378/1/verificationEmailRequest' ,
method : 'POST' ,
body : {
email : 'user@parse.com' ,
} ,
headers : {
'X-Parse-Application-Id' : Parse . applicationId ,
'X-Parse-REST-API-Key' : 'rest' ,
'Content-Type' : 'application/json' ,
} ,
2017-03-04 13:30:52 -08:00
} )
2025-04-15 11:25:42 -05:00
. then ( fail )
. catch ( response => response ) ;
2025-07-04 20:24:08 +02:00
2025-04-15 11:25:42 -05:00
expect ( response . status ) . toBe ( 400 ) ;
expect ( sendVerificationEmailCallCount ) . toBe ( 0 ) ;
expect ( sendEmailOptions ) . not . toBeDefined ( ) ;
2017-03-04 13:30:52 -08:00
} ) ;
2025-04-15 11:25:42 -05:00
it ( 'should fail if no email is supplied' , async ( ) => {
2018-02-17 09:55:30 -05:00
let sendEmailOptions ;
let sendVerificationEmailCallCount = 0 ;
const emailAdapter = {
2017-03-04 13:30:52 -08:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
sendVerificationEmailCallCount ++ ;
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
} ;
2025-04-15 11:25:42 -05:00
await reconfigureServer ( {
2017-03-04 13:30:52 -08:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2025-04-15 11:25:42 -05:00
} ) ;
const response = await request ( {
url : 'http://localhost:8378/1/verificationEmailRequest' ,
method : 'POST' ,
body : { } ,
headers : {
'X-Parse-Application-Id' : Parse . applicationId ,
'X-Parse-REST-API-Key' : 'rest' ,
'Content-Type' : 'application/json' ,
} ,
} ) . then ( fail , response => response ) ;
expect ( response . status ) . toBe ( 400 ) ;
expect ( response . data . code ) . toBe ( Parse . Error . EMAIL _MISSING ) ;
expect ( response . data . error ) . toBe ( 'you must provide an email' ) ;
expect ( sendVerificationEmailCallCount ) . toBe ( 0 ) ;
expect ( sendEmailOptions ) . not . toBeDefined ( ) ;
2017-03-04 13:30:52 -08:00
} ) ;
2025-04-15 11:25:42 -05:00
it ( 'should fail if email is not a string' , async ( ) => {
2018-02-17 09:55:30 -05:00
let sendEmailOptions ;
let sendVerificationEmailCallCount = 0 ;
const emailAdapter = {
2017-03-04 13:30:52 -08:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
sendVerificationEmailCallCount ++ ;
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
} ;
2025-04-15 11:25:42 -05:00
await reconfigureServer ( {
2017-03-04 13:30:52 -08:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2025-04-15 11:25:42 -05:00
} ) ;
const response = await request ( {
url : 'http://localhost:8378/1/verificationEmailRequest' ,
method : 'POST' ,
body : { email : 3 } ,
headers : {
'X-Parse-Application-Id' : Parse . applicationId ,
'X-Parse-REST-API-Key' : 'rest' ,
'Content-Type' : 'application/json' ,
} ,
} ) . then ( fail , res => res ) ;
expect ( response . status ) . toBe ( 400 ) ;
expect ( response . data . code ) . toBe ( Parse . Error . INVALID _EMAIL _ADDRESS ) ;
expect ( response . data . error ) . toBe ( 'you must provide a valid email string' ) ;
expect ( sendVerificationEmailCallCount ) . toBe ( 0 ) ;
expect ( sendEmailOptions ) . not . toBeDefined ( ) ;
2017-03-04 13:30:52 -08:00
} ) ;
2025-04-15 11:25:42 -05:00
it ( 'client should not see the _email_verify_token_expires_at field' , async ( ) => {
2018-02-17 09:55:30 -05:00
const user = new Parse . User ( ) ;
let sendEmailOptions ;
2025-04-15 11:25:42 -05:00
const sendPromise = resolvingPromise ( ) ;
2018-02-17 09:55:30 -05:00
const emailAdapter = {
2016-07-19 01:10:36 -05:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
2025-04-15 11:25:42 -05:00
sendPromise . resolve ( ) ;
2016-07-19 01:10:36 -05:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
} ;
2025-04-15 11:25:42 -05:00
await reconfigureServer ( {
2016-07-19 01:10:36 -05:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2025-04-15 11:25:42 -05:00
} ) ;
user . setUsername ( 'testEmailVerifyTokenValidity' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@parse.com' ) ;
await user . signUp ( ) ;
await sendPromise ;
await user . fetch ( ) ;
expect ( user . get ( 'emailVerified' ) ) . toEqual ( false ) ;
expect ( typeof user . get ( '_email_verify_token_expires_at' ) ) . toBe ( 'undefined' ) ;
expect ( sendEmailOptions ) . toBeDefined ( ) ;
2016-07-19 01:10:36 -05:00
} ) ;
2025-04-15 11:25:42 -05:00
it _id ( 'b082d387-4974-4d45-a0d9-0c85ca2d7cbf' ) ( it ) ( 'emailVerified should be set to false after changing from an already verified email' , async ( ) => {
let user = new Parse . User ( ) ;
2018-02-17 09:55:30 -05:00
let sendEmailOptions ;
2025-04-15 11:25:42 -05:00
const sendPromise = resolvingPromise ( ) ;
2018-02-17 09:55:30 -05:00
const emailAdapter = {
2018-02-01 19:39:57 -03:00
sendVerificationEmail : options => {
sendEmailOptions = options ;
2025-04-15 11:25:42 -05:00
sendPromise . resolve ( ) ;
2018-02-01 19:39:57 -03:00
} ,
sendPasswordResetEmail : ( ) => Promise . resolve ( ) ,
2018-09-01 13:58:06 -04:00
sendMail : ( ) => { } ,
} ;
2025-04-15 11:25:42 -05:00
await reconfigureServer ( {
2018-02-01 19:39:57 -03:00
appName : 'emailVerifyToken' ,
verifyUserEmails : true ,
emailAdapter : emailAdapter ,
emailVerifyTokenValidityDuration : 5 , // 5 seconds
2018-09-01 13:58:06 -04:00
publicServerURL : 'http://localhost:8378/1' ,
2025-04-15 11:25:42 -05:00
} ) ;
user . setUsername ( 'testEmailVerifyTokenValidity' ) ;
user . setPassword ( 'expiringToken' ) ;
user . set ( 'email' , 'user@parse.com' ) ;
await user . signUp ( ) ;
await sendPromise ;
let response = await request ( {
url : sendEmailOptions . link ,
followRedirects : false ,
} ) ;
2025-12-12 20:55:39 +01:00
expect ( response . status ) . toEqual ( 200 ) ;
2025-04-15 11:25:42 -05:00
user = await Parse . User . logIn ( 'testEmailVerifyTokenValidity' , 'expiringToken' ) ;
expect ( typeof user ) . toBe ( 'object' ) ;
expect ( user . get ( 'emailVerified' ) ) . toBe ( true ) ;
2018-02-01 19:39:57 -03:00
2025-04-15 11:25:42 -05:00
user . set ( 'email' , 'newEmail@parse.com' ) ;
await user . save ( ) ;
await user . fetch ( ) ;
expect ( typeof user ) . toBe ( 'object' ) ;
expect ( user . get ( 'email' ) ) . toBe ( 'newEmail@parse.com' ) ;
expect ( user . get ( 'emailVerified' ) ) . toBe ( false ) ;
2018-09-01 13:58:06 -04:00
2025-04-15 11:25:42 -05:00
response = await request ( {
url : sendEmailOptions . link ,
followRedirects : false ,
} ) ;
2025-12-12 20:55:39 +01:00
expect ( response . status ) . toEqual ( 200 ) ;
2018-02-01 19:39:57 -03:00
} ) ;
2018-09-01 13:58:06 -04:00
} ) ;