Adds ability to prevent login with unverified emails (#2175)

This commit is contained in:
Diwakar Cherukumilli
2016-07-04 12:56:35 -05:00
committed by Florent Vilmart
parent b641712d4d
commit 08c63f324a
6 changed files with 141 additions and 0 deletions

View File

@@ -209,6 +209,11 @@ var server = ParseServer({
...otherOptions, ...otherOptions,
// Enable email verification // Enable email verification
verifyUserEmails: true, verifyUserEmails: true,
// set preventLoginWithUnverifiedEmail to false to allow user to login without verifying their email
// set preventLoginWithUnverifiedEmail to true to prevent user from login if their email is not verified
preventLoginWithUnverifiedEmail: false, // defaults to false
// The public URL of your app. // The public URL of your app.
// This will appear in the link that is used to verify email addresses and reset passwords. // This will appear in the link that is used to verify email addresses and reset passwords.
// Set the mount path as it is in serverURL // Set the mount path as it is in serverURL

View File

@@ -238,6 +238,129 @@ describe("Custom Pages, Email Verification, Password Reset", () => {
}); });
}); });
it_exclude_dbs(['postgres'])('prevents user from login if email is not verified but preventLoginWithUnverifiedEmail is set to true', done => {
reconfigureServer({
appName: 'test',
publicServerURL: 'http://localhost:1337/1',
verifyUserEmails: true,
preventLoginWithUnverifiedEmail: true,
emailAdapter: MockEmailAdapterWithOptions({
fromAddress: 'parse@example.com',
apiKey: 'k',
domain: 'd',
}),
})
.then(() => {
let user = new Parse.User();
user.setPassword("asdf");
user.setUsername("zxcv");
user.set("email", "testInvalidConfig@parse.com");
user.signUp(null)
.then(user => Parse.User.logIn("zxcv", "asdf"))
.then(result => {
fail('login should have failed');
done();
}, error => {
expect(error.message).toEqual('User email is not verified.')
done();
});
})
.catch(error => {
fail(JSON.stringify(error));
done();
});
});
it_exclude_dbs(['postgres'])('allows user to login only after user clicks on the link to confirm email address if preventLoginWithUnverifiedEmail is set to true', done => {
var user = new Parse.User();
var sendEmailOptions;
var emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
},
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {}
}
reconfigureServer({
appName: 'emailing app',
verifyUserEmails: true,
preventLoginWithUnverifiedEmail: true,
emailAdapter: emailAdapter,
publicServerURL: "http://localhost:8378/1"
})
.then(() => {
user.setPassword("other-password");
user.setUsername("user");
user.set('email', 'user@parse.com');
return user.signUp();
}).then(() => {
expect(sendEmailOptions).not.toBeUndefined();
request.get(sendEmailOptions.link, {
followRedirect: false,
}, (error, response, body) => {
expect(response.statusCode).toEqual(302);
expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html?username=user');
user.fetch()
.then(() => {
expect(user.get('emailVerified')).toEqual(true);
Parse.User.logIn("user", "other-password")
.then(user => {
expect(typeof user).toBe('object');
expect(user.get('emailVerified')).toBe(true);
done();
}, error => {
fail('login should have succeeded');
done();
});
}, (err) => {
console.error(err);
fail("this should not fail");
done();
}).catch((err) =>
{
console.error(err);
fail(err);
done();
})
});
});
});
it_exclude_dbs(['postgres'])('allows user to login if email is not verified but preventLoginWithUnverifiedEmail is set to false', done => {
reconfigureServer({
appName: 'test',
publicServerURL: 'http://localhost:1337/1',
verifyUserEmails: true,
preventLoginWithUnverifiedEmail: false,
emailAdapter: MockEmailAdapterWithOptions({
fromAddress: 'parse@example.com',
apiKey: 'k',
domain: 'd',
}),
})
.then(() => {
let user = new Parse.User();
user.setPassword("asdf");
user.setUsername("zxcv");
user.set("email", "testInvalidConfig@parse.com");
user.signUp(null)
.then(user => Parse.User.logIn("zxcv", "asdf"))
.then(user => {
expect(typeof user).toBe('object');
expect(user.get('emailVerified')).toBe(false);
done();
}, error => {
fail('login should have succeeded');
done();
});
})
.catch(error => {
fail(JSON.stringify(error));
done();
});
});
it_exclude_dbs(['postgres'])('fails if you include an emailAdapter, set a publicServerURL, but have no appName and send a password reset email', done => { it_exclude_dbs(['postgres'])('fails if you include an emailAdapter, set a publicServerURL, but have no appName and send a password reset email', done => {
reconfigureServer({ reconfigureServer({
appName: undefined, appName: undefined,

View File

@@ -36,6 +36,7 @@ export class Config {
this.serverURL = cacheInfo.serverURL; this.serverURL = cacheInfo.serverURL;
this.publicServerURL = removeTrailingSlash(cacheInfo.publicServerURL); this.publicServerURL = removeTrailingSlash(cacheInfo.publicServerURL);
this.verifyUserEmails = cacheInfo.verifyUserEmails; this.verifyUserEmails = cacheInfo.verifyUserEmails;
this.preventLoginWithUnverifiedEmail = cacheInfo.preventLoginWithUnverifiedEmail;
this.appName = cacheInfo.appName; this.appName = cacheInfo.appName;
this.cacheController = cacheInfo.cacheController; this.cacheController = cacheInfo.cacheController;

View File

@@ -117,6 +117,7 @@ class ParseServer {
serverURL = requiredParameter('You must provide a serverURL!'), serverURL = requiredParameter('You must provide a serverURL!'),
maxUploadSize = '20mb', maxUploadSize = '20mb',
verifyUserEmails = false, verifyUserEmails = false,
preventLoginWithUnverifiedEmail = false,
cacheAdapter, cacheAdapter,
emailAdapter, emailAdapter,
publicServerURL, publicServerURL,
@@ -231,6 +232,7 @@ class ParseServer {
hooksController: hooksController, hooksController: hooksController,
userController: userController, userController: userController,
verifyUserEmails: verifyUserEmails, verifyUserEmails: verifyUserEmails,
preventLoginWithUnverifiedEmail: preventLoginWithUnverifiedEmail,
allowClientClassCreation: allowClientClassCreation, allowClientClassCreation: allowClientClassCreation,
authDataManager: authDataManager(oauth, enableAnonymousUsers), authDataManager: authDataManager(oauth, enableAnonymousUsers),
appName: appName, appName: appName,

View File

@@ -83,6 +83,11 @@ export class UsersRouter extends ClassesRouter {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.');
} }
user = results[0]; user = results[0];
if (req.config.verifyUserEmails && req.config.preventLoginWithUnverifiedEmail && !user.emailVerified) {
throw new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.');
}
return passwordCrypto.compare(req.body.password, user.password); return passwordCrypto.compare(req.body.password, user.password);
}).then((correct) => { }).then((correct) => {

View File

@@ -146,6 +146,11 @@ export default {
help: "Enable (or disable) user email validation, defaults to false", help: "Enable (or disable) user email validation, defaults to false",
action: booleanParser action: booleanParser
}, },
"preventLoginWithUnverifiedEmail": {
env: "PARSE_SERVER_PREVENT_LOGIN_WITH_UNVERIFIED_EMAIL",
help: "Prevent user from login if email is not verified and PARSE_SERVER_VERIFY_USER_EMAILS is true, defaults to false",
action: booleanParser
},
"appName": { "appName": {
env: "PARSE_SERVER_APP_NAME", env: "PARSE_SERVER_APP_NAME",
help: "Sets the app name" help: "Sets the app name"