diff --git a/public_html/invalid_link.html b/public_html/invalid_link.html
index 66bdc788..b19044e5 100644
--- a/public_html/invalid_link.html
+++ b/public_html/invalid_link.html
@@ -35,6 +35,8 @@
padding: 0 0 0 0;
}
+
+
Invalid Link
diff --git a/public_html/invalid_verification_link.html b/public_html/invalid_verification_link.html
new file mode 100644
index 00000000..fe6914fc
--- /dev/null
+++ b/public_html/invalid_verification_link.html
@@ -0,0 +1,68 @@
+
+
+
+
+
Invalid Link
+
+
+
+
+
+
+
Invalid Verification Link
+
+
+
+
diff --git a/public_html/link_send_fail.html b/public_html/link_send_fail.html
new file mode 100644
index 00000000..7f817a2c
--- /dev/null
+++ b/public_html/link_send_fail.html
@@ -0,0 +1,45 @@
+
+
+
+
+
Invalid Link
+
+
+
+
+
+
No link sent. User not found or email already verified
+
+
+
diff --git a/public_html/link_send_success.html b/public_html/link_send_success.html
new file mode 100644
index 00000000..55d9cad6
--- /dev/null
+++ b/public_html/link_send_success.html
@@ -0,0 +1,45 @@
+
+
+
+
+
Invalid Link
+
+
+
+
+
+
Link Sent! Check your email.
+
+
+
diff --git a/spec/EmailVerificationToken.spec.js b/spec/EmailVerificationToken.spec.js
index 7e27239c..fad7f4ef 100644
--- a/spec/EmailVerificationToken.spec.js
+++ b/spec/EmailVerificationToken.spec.js
@@ -6,7 +6,7 @@ const Config = require('../src/Config');
describe("Email Verification Token Expiration: ", () => {
- it('show the invalid link page, if the user clicks on the verify email link after the email verify token expires', done => {
+ it('show the invalid verification link page, if the user clicks on the verify email link after the email verify token expires', done => {
var user = new Parse.User();
var sendEmailOptions;
var emailAdapter = {
@@ -37,7 +37,7 @@ describe("Email Verification Token Expiration: ", () => {
followRedirect: false,
}, (error, response) => {
expect(response.statusCode).toEqual(302);
- expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html');
+ expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?username=testEmailVerifyTokenValidity&appId=test');
done();
});
}, 1000);
@@ -313,7 +313,7 @@ describe("Email Verification Token Expiration: ", () => {
});
});
- it('clicking on the email verify link by an email VERIFIED user that was setup before enabling the expire email verify token should show an invalid link', done => {
+ 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', done => {
var user = new Parse.User();
var sendEmailOptions;
var emailAdapter = {
@@ -359,7 +359,7 @@ describe("Email Verification Token Expiration: ", () => {
followRedirect: false,
}, (error, response) => {
expect(response.statusCode).toEqual(302);
- expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html');
+ expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html?username=testEmailVerifyTokenValidity');
done();
});
})
@@ -369,7 +369,7 @@ describe("Email Verification Token Expiration: ", () => {
});
});
- it('clicking on the email verify link by an email UNVERIFIED user that was setup before enabling the expire email verify token should show an invalid link', done => {
+ 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', done => {
var user = new Parse.User();
var sendEmailOptions;
var emailAdapter = {
@@ -409,7 +409,7 @@ describe("Email Verification Token Expiration: ", () => {
followRedirect: false,
}, (error, response) => {
expect(response.statusCode).toEqual(302);
- expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html');
+ expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?username=testEmailVerifyTokenValidity&appId=test');
done();
});
})
diff --git a/spec/ValidationAndPasswordsReset.spec.js b/spec/ValidationAndPasswordsReset.spec.js
index e127fd03..d1d1007b 100644
--- a/spec/ValidationAndPasswordsReset.spec.js
+++ b/spec/ValidationAndPasswordsReset.spec.js
@@ -655,7 +655,7 @@ describe("Custom Pages, Email Verification, Password Reset", () => {
});
});
- it('redirects you to invalid link if you try to validate a nonexistant users email', done => {
+ it('redirects you to invalid verification link page if you try to validate a nonexistant users email', done => {
reconfigureServer({
appName: 'emailing app',
verifyUserEmails: true,
@@ -671,7 +671,32 @@ describe("Custom Pages, Email Verification, Password Reset", () => {
followRedirect: false,
}, (error, response) => {
expect(response.statusCode).toEqual(302);
- expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html');
+ expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?username=sadfasga&appId=test');
+ done();
+ });
+ });
+ });
+
+ it('redirects you to link send fail page if you try to resend a link for a nonexistant user', done => {
+ reconfigureServer({
+ appName: 'emailing app',
+ verifyUserEmails: true,
+ emailAdapter: {
+ sendVerificationEmail: () => Promise.resolve(),
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {}
+ },
+ publicServerURL: "http://localhost:8378/1"
+ })
+ .then(() => {
+ request.post('http://localhost:8378/1/apps/test/resend_verification_email', {
+ followRedirect: false,
+ form: {
+ username: "sadfasga"
+ }
+ }, (error, response) => {
+ expect(response.statusCode).toEqual(302);
+ expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/link_send_fail.html');
done();
});
});
@@ -685,7 +710,7 @@ describe("Custom Pages, Email Verification, Password Reset", () => {
followRedirect: false,
}, (error, response) => {
expect(response.statusCode).toEqual(302);
- expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html');
+ expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?username=zxcv&appId=test');
user.fetch()
.then(() => {
expect(user.get('emailVerified')).toEqual(false);
diff --git a/src/Config.js b/src/Config.js
index 8958a079..52d1bb81 100644
--- a/src/Config.js
+++ b/src/Config.js
@@ -234,6 +234,18 @@ export class Config {
return this.customPages.invalidLink || `${this.publicServerURL}/apps/invalid_link.html`;
}
+ get invalidVerificationLinkURL() {
+ return this.customPages.invalidVerificationLink || `${this.publicServerURL}/apps/invalid_verification_link.html`;
+ }
+
+ get linkSendSuccessURL() {
+ return this.customPages.linkSendSuccess || `${this.publicServerURL}/apps/link_send_success.html`
+ }
+
+ get linkSendFailURL() {
+ return this.customPages.linkSendFail || `${this.publicServerURL}/apps/link_send_fail.html`
+ }
+
get verifyEmailSuccessURL() {
return this.customPages.verifyEmailSuccess || `${this.publicServerURL}/apps/verify_email_success.html`;
}
diff --git a/src/Controllers/UserController.js b/src/Controllers/UserController.js
index b6ca02a5..305fdccf 100644
--- a/src/Controllers/UserController.js
+++ b/src/Controllers/UserController.js
@@ -60,11 +60,17 @@ export class UserController extends AdaptableController {
updateFields._email_verify_token_expires_at = {__op: 'Delete'};
}
- return this.config.database.update('_User', query, updateFields).then((document) => {
- if (!document) {
- throw undefined;
+ var checkIfAlreadyVerified = new RestQuery(this.config, Auth.master(this.config), '_User', {username: username, emailVerified: true});
+ return checkIfAlreadyVerified.execute().then(result => {
+ if (result.results.length) {
+ return Promise.resolve(result.results.length[0]);
}
- return Promise.resolve(document);
+ return this.config.database.update('_User', query, updateFields).then((document) => {
+ if (!document) {
+ throw undefined
+ }
+ return Promise.resolve(document);
+ })
});
}
@@ -134,6 +140,18 @@ export class UserController extends AdaptableController {
});
}
+ resendVerificationEmail(username) {
+ return this.getUserIfNeeded({username: username}).then((aUser) => {
+ if (!aUser || aUser.emailVerified) {
+ throw undefined;
+ }
+ this.setEmailVerifyToken(aUser);
+ return this.config.database.update('_User', {username}, aUser).then(() => {
+ this.sendVerificationEmail(aUser);
+ });
+ });
+ }
+
setPasswordResetToken(email) {
const token = { _perishable_token: randomString(25) };
diff --git a/src/Routers/PublicAPIRouter.js b/src/Routers/PublicAPIRouter.js
index e3a569ff..012c13b3 100644
--- a/src/Routers/PublicAPIRouter.js
+++ b/src/Routers/PublicAPIRouter.js
@@ -31,7 +31,35 @@ export class PublicAPIRouter extends PromiseRouter {
location: `${config.verifyEmailSuccessURL}?${params}`
});
}, ()=> {
+ return this.invalidVerificationLink(req);
+ })
+ }
+
+ resendVerificationEmail(req) {
+ const username = req.body.username;
+ const appId = req.params.appId;
+ const config = new Config(appId);
+
+ if (!config.publicServerURL) {
+ return this.missingPublicServerURL();
+ }
+
+ if (!username) {
return this.invalidLink(req);
+ }
+
+ const userController = config.userController;
+
+ return userController.resendVerificationEmail(username).then(() => {
+ return Promise.resolve({
+ status: 302,
+ location: `${config.linkSendSuccessURL}`
+ });
+ }, ()=> {
+ return Promise.resolve({
+ status: 302,
+ location: `${config.linkSendFailURL}`
+ });
})
}
@@ -123,6 +151,19 @@ export class PublicAPIRouter extends PromiseRouter {
});
}
+ invalidVerificationLink(req) {
+ const config = req.config;
+ if (req.query.username && req.params.appId) {
+ const params = qs.stringify({username: req.query.username, appId: req.params.appId});
+ return Promise.resolve({
+ status: 302,
+ location: `${config.invalidVerificationLinkURL}?${params}`
+ });
+ } else {
+ return this.invalidLink(req);
+ }
+ }
+
missingPublicServerURL() {
return Promise.resolve({
text: 'Not found.',
@@ -140,6 +181,10 @@ export class PublicAPIRouter extends PromiseRouter {
req => { this.setConfig(req) },
req => { return this.verifyEmail(req); });
+ this.route('POST', '/apps/:appId/resend_verification_email',
+ req => { this.setConfig(req); },
+ req => { return this.resendVerificationEmail(req); });
+
this.route('GET','/apps/choose_password',
req => { return this.changePassword(req); });