Adds ability to prevent login with unverified emails (#2175)
This commit is contained in:
committed by
Florent Vilmart
parent
b641712d4d
commit
08c63f324a
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user