Skip to content

Commit 588b0f3

Browse files
committed
feat: allow the master key to bypass email verification (#8230)
1 parent 8c30c81 commit 588b0f3

File tree

2 files changed

+85
-5
lines changed

2 files changed

+85
-5
lines changed

spec/EmailVerificationToken.spec.js

+76
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,82 @@ describe('Email Verification Token Expiration: ', () => {
521521
});
522522
});
523523

524+
it('master key can create users and change the email of a user without triggering a re-verification', done => {
525+
const user = new Parse.User();
526+
527+
let sendEmailCount = 0;
528+
const emailAdapter = {
529+
sendVerificationEmail: () => {
530+
sendEmailCount += 1;
531+
},
532+
sendPasswordResetEmail: () => {
533+
sendEmailCount += 1;
534+
return Promise.resolve();
535+
},
536+
sendMail: () => {
537+
sendEmailCount += 1;
538+
},
539+
};
540+
const serverConfig = {
541+
appName: 'emailVerifyToken',
542+
verifyUserEmails: true,
543+
preventLoginWithUnverifiedEmail: true,
544+
emailAdapter: emailAdapter,
545+
emailVerifyTokenValidityDuration: 5, // 5 seconds
546+
publicServerURL: 'http://localhost:8378/1',
547+
};
548+
549+
reconfigureServer(serverConfig)
550+
.then(() => {
551+
user.setUsername('masterKeyBypassEmailVerification');
552+
user.setPassword('expiringToken');
553+
user.set('email', '[email protected]');
554+
user.set('emailVerified', true);
555+
return user.save(null, { useMasterKey: true });
556+
})
557+
.then(() => {
558+
const config = Config.get('test');
559+
return config.database
560+
.find('_User', { username: 'masterKeyBypassEmailVerification' })
561+
.then(results => {
562+
return results[0];
563+
});
564+
})
565+
.then(userFromDb => {
566+
expect(typeof userFromDb).toBe('object');
567+
expect(sendEmailCount).toBe(0);
568+
expect(userFromDb['emailVerified']).toBe(true);
569+
expect(userFromDb['email']).toBe('[email protected]');
570+
expect(user.getSessionToken()).toBeDefined();
571+
572+
// change the email
573+
user.set('email', '[email protected]');
574+
user.set('emailVerified', true);
575+
return new Promise(resolve => {
576+
setTimeout(() => resolve(user.save(null, { useMasterKey: true })), 500);
577+
});
578+
})
579+
.then(() => {
580+
const config = Config.get('test');
581+
return config.database
582+
.find('_User', { username: 'masterKeyBypassEmailVerification' })
583+
.then(results => {
584+
return results[0];
585+
});
586+
})
587+
.then(userAfterEmailReset => {
588+
expect(typeof userAfterEmailReset).toBe('object');
589+
expect(sendEmailCount).toBe(0);
590+
expect(userAfterEmailReset['emailVerified']).toBe(true);
591+
expect(userAfterEmailReset['email']).toBe('[email protected]');
592+
done();
593+
})
594+
.catch(error => {
595+
jfail(error);
596+
done();
597+
});
598+
});
599+
524600
it('should send a new verification email when a resend is requested and the user is UNVERIFIED', done => {
525601
const user = new Parse.User();
526602
let sendEmailOptions;

src/RestWrite.js

+9-5
Original file line numberDiff line numberDiff line change
@@ -732,9 +732,11 @@ RestWrite.prototype._validateEmail = function () {
732732
(Object.keys(this.data.authData).length === 1 &&
733733
Object.keys(this.data.authData)[0] === 'anonymous')
734734
) {
735-
// We updated the email, send a new validation
736-
this.storage['sendVerificationEmail'] = true;
737-
this.config.userController.setEmailVerifyToken(this.data);
735+
// We updated the email, send a new verification unless the master key explicitly suppresses it
736+
if (!this.auth.isMaster || !this.data['emailVerified']) {
737+
this.storage['sendVerificationEmail'] = true;
738+
this.config.userController.setEmailVerifyToken(this.data);
739+
}
738740
}
739741
});
740742
};
@@ -862,8 +864,10 @@ RestWrite.prototype.createSessionTokenIfNeeded = function () {
862864
this.config.preventLoginWithUnverifiedEmail && // no login without verification
863865
this.config.verifyUserEmails
864866
) {
865-
// verification is on
866-
return; // do not create the session token in that case!
867+
// verification is on and the master key has not explicitly suppressed verification
868+
if (!this.auth.isMaster || !this.data['emailVerified']) {
869+
return; // do not create the session token in that case!
870+
}
867871
}
868872
return this.createSessionToken();
869873
};

0 commit comments

Comments
 (0)