Use a rendered html email template for identity check

pull/7/head
Clement Michaud 2017-01-28 02:33:45 +01:00
parent cb98f0454a
commit b205ba6a0d
6 changed files with 281 additions and 16 deletions

View File

@ -4,9 +4,13 @@ var randomstring = require('randomstring');
var Promise = require('bluebird'); var Promise = require('bluebird');
var util = require('util'); var util = require('util');
var exceptions = require('./exceptions'); var exceptions = require('./exceptions');
var fs = require('fs');
var ejs = require('ejs');
module.exports = identity_check; module.exports = identity_check;
var filePath = __dirname + '/../resources/email-template.ejs';
var email_template = fs.readFileSync(filePath, 'utf8');
// IdentityCheck class // IdentityCheck class
@ -24,15 +28,8 @@ IdentityCheck.prototype.issue_token = function(userid, email, content, logger) {
this._logger.debug('identity_check: issue identity token %s for 5 minutes', token); this._logger.debug('identity_check: issue identity token %s for 5 minutes', token);
return this._user_data_store.issue_identity_check_token(userid, token, content, five_minutes) return this._user_data_store.issue_identity_check_token(userid, token, content, five_minutes)
.then(function() { .then(function() {
that._logger.debug('identity_check: send email to %s', email); return Promise.resolve(token);
return that._send_identity_check_email(email, token); });
})
}
IdentityCheck.prototype._send_identity_check_email = function(email, token) {
var url = util.format('%s?identity_token=%s', email.hook_url, token);
var email_content = util.format('<a href="%s">Register</a>', url);
return this._email_sender.send(email.to, email.subject, email_content);
} }
IdentityCheck.prototype.consume_token = function(token, logger) { IdentityCheck.prototype.consume_token = function(token, logger) {
@ -107,14 +104,27 @@ function identity_check_post(endpoint, icheck_interface) {
throw new exceptions.IdentityError('Missing user id or email address'); throw new exceptions.IdentityError('Missing user id or email address');
} }
var email = {}; return identity_check.issue_token(userid, undefined, logger);
email.to = email_address;
email.subject = 'Identity Verification';
email.hook_url = util.format('https://%s%s', req.headers.host, req.headers['x-original-uri']);
return identity_check.issue_token(userid, email, undefined, logger);
}, function(err) { }, function(err) {
throw new exceptions.AccessDeniedError(); throw new exceptions.AccessDeniedError();
}) })
.then(function(token) {
var original_url = util.format('https://%s%s', req.headers.host, req.headers['x-original-uri']);
var link_url = util.format('%s?identity_token=%s', original_url, token);
var email = {};
var d = {};
d.url = link_url;
d.button_title = 'Continue';
d.title = icheck_interface.email_subject;
email.to = email_address;
email.subject = icheck_interface.email_subject;
email.content = ejs.render(email_template, d);
logger.info('POST identity_check: send email to %s', email.to);
return email_sender.send(email.to, email.subject, email.content);
})
.then(function() { .then(function() {
res.status(204); res.status(204);
res.send(); res.send();

View File

@ -9,6 +9,7 @@ var icheck_interface = {
challenge: CHALLENGE, challenge: CHALLENGE,
render_template: 'reset-password', render_template: 'reset-password',
pre_check_callback: pre_check, pre_check_callback: pre_check,
email_subject: 'Reset your password',
} }
module.exports = { module.exports = {

View File

@ -8,6 +8,7 @@ var icheck_interface = {
challenge: CHALLENGE, challenge: CHALLENGE,
render_template: 'u2f-register', render_template: 'u2f-register',
pre_check_callback: pre_check, pre_check_callback: pre_check,
email_subject: 'Register your U2F device',
} }
module.exports = { module.exports = {

View File

@ -23,7 +23,7 @@ function onLoginButtonClicked() {
validateFirstFactor(username, password, function(err) { validateFirstFactor(username, password, function(err) {
if(err) { if(err) {
onFirstFactorFailure(err); onFirstFactorFailure(err.responseText);
return; return;
} }
onFirstFactorSuccess(); onFirstFactorSuccess();

View File

@ -0,0 +1,254 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Simples-Minimalistic Responsive Template</title>
<style type="text/css">
/* Client-specific Styles */
#outlook a {padding:0;} /* Force Outlook to provide a "view in browser" menu link. */
body{width:100% !important; -webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; margin:0; padding:0;}
/* Prevent Webkit and Windows Mobile platforms from changing default font sizes, while not breaking desktop design. */
.ExternalClass {width:100%;} /* Force Hotmail to display emails at full width */
.ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height: 100%;} /* Force Hotmail to display normal line spacing.*/
#backgroundTable {margin:0; padding:0; width:100% !important; line-height: 100% !important;}
img {outline:none; text-decoration:none;border:none; -ms-interpolation-mode: bicubic;}
a img {border:none;}
.image_fix {display:block;}
p {margin: 0px 0px !important;}
table td {border-collapse: collapse;}
table { border-collapse:collapse; mso-table-lspace:0pt; mso-table-rspace:0pt; }
a {color: #0a8cce;text-decoration: none;text-decoration:none!important;}
h1 { line-height: 30px; }
.button {padding: 15px 30px; border-radius: 10px; background: rgb(204, 204, 255); text-decoration:none; }
/*STYLES*/
table[class=full] { width: 100%; clear: both; }
/*IPAD STYLES*/
@media only screen and (max-width: 640px) {
a[href^="tel"], a[href^="sms"] {
text-decoration: none;
color: #0a8cce; /* or whatever your want */
pointer-events: none;
cursor: default;
}
.mobile_link a[href^="tel"], .mobile_link a[href^="sms"] {
text-decoration: default;
color: #0a8cce !important;
pointer-events: auto;
cursor: default;
}
table[class=devicewidth] {width: 440px!important;text-align:center!important;}
table[class=devicewidthinner] {width: 420px!important;text-align:center!important;}
img[class=banner] {width: 440px!important;height:220px!important;}
img[class=colimg2] {width: 440px!important;height:220px!important;}
}
/*IPHONE STYLES*/
@media only screen and (max-width: 480px) {
a[href^="tel"], a[href^="sms"] {
text-decoration: none;
color: #0a8cce; /* or whatever your want */
pointer-events: none;
cursor: default;
}
.mobile_link a[href^="tel"], .mobile_link a[href^="sms"] {
text-decoration: default;
color: #0a8cce !important;
pointer-events: auto;
cursor: default;
}
table[class=devicewidth] {width: 280px!important;text-align:center!important;}
table[class=devicewidthinner] {width: 260px!important;text-align:center!important;}
img[class=banner] {width: 280px!important;height:140px!important;}
img[class=colimg2] {width: 280px!important;height:140px!important;}
td[class=mobile-hide]{display:none!important;}
td[class="padding-bottom25"]{padding-bottom:25px!important;}
}
</style>
</head>
<body>
<!-- Start of header -->
<table width="100%" bgcolor="#ffffff" cellpadding="0" cellspacing="0" border="0" id="backgroundTable" st-sortable="header">
<tbody>
<tr>
<td>
<table width="600" cellpadding="0" cellspacing="0" border="0" align="center" class="devicewidth">
<tbody>
<tr>
<td width="100%">
<table width="600" cellpadding="0" cellspacing="0" border="0" align="center" class="devicewidth">
<tbody>
<!-- Spacing -->
<tr>
<td height="20" style="font-size:1px; line-height:1px; mso-line-height-rule: exactly;">&nbsp;</td>
</tr>
<!-- Spacing -->
<tr>
<td>
<!-- logo -->
<table width="140" align="center" border="0" cellpadding="0" cellspacing="0" class="devicewidth">
<tbody>
<tr>
<td width="300" height="50" align="center">
<h1><%= title %></h1>
</td>
</tr>
</tbody>
</table>
<!-- end of logo -->
</td>
</tr>
<!-- Spacing -->
<tr>
<td height="20" style="font-size:1px; line-height:1px; mso-line-height-rule: exactly;">&nbsp;</td>
</tr>
<!-- Spacing -->
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<!-- End of Header -->
<!-- Start of seperator -->
<table width="100%" bgcolor="#ffffff" cellpadding="0" cellspacing="0" border="0" id="backgroundTable" st-sortable="seperator">
<tbody>
<tr>
<td>
<table width="600" align="center" cellspacing="0" cellpadding="0" border="0" class="devicewidth">
<tbody>
<tr>
<td align="center" height="20" style="font-size:1px; line-height:1px;">&nbsp;</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<!-- End of seperator -->
<!-- Start Full Text -->
<table width="100%" bgcolor="#ffffff" cellpadding="0" cellspacing="0" border="0" id="backgroundTable" st-sortable="full-text">
<tbody>
<tr>
<td>
<table width="600" cellpadding="0" cellspacing="0" border="0" align="center" class="devicewidth">
<tbody>
<tr>
<td width="100%">
<table width="600" cellpadding="0" cellspacing="0" border="0" align="center" class="devicewidth">
<tbody>
<!-- Spacing -->
<tr>
<td height="20" style="font-size:1px; line-height:1px; mso-line-height-rule: exactly;">&nbsp;</td>
</tr>
<!-- Spacing -->
<tr>
<td>
<table width="560" align="center" cellpadding="0" cellspacing="0" border="0" class="devicewidthinner">
<tbody>
<!-- Title -->
<tr>
<td style="font-family: Helvetica, arial, sans-serif; font-size: 16px; color: #333333; text-align:center; line-height: 30px;" st-title="fulltext-content">
This email has been sent to you in order to validate your identity. Please ignore it if you do not know why you received it.
</td>
</tr>
<!-- End of Title -->
<!-- spacing -->
<tr>
<td width="100%" height="20" style="font-size:1px; line-height:1px; mso-line-height-rule: exactly;">&nbsp;</td>
</tr>
<!-- End of spacing -->
<!-- content -->
<tr>
<td style="font-family: Helvetica, arial, sans-serif; font-size: 16px; color: #666666; text-align:center; line-height: 30px;" st-content="fulltext-content">
<a href="<%= url %>" class="button"><%= button_title %></a>
</td>
</tr>
<!-- End of content -->
</tbody>
</table>
</td>
</tr>
<!-- Spacing -->
<tr>
<td height="20" style="font-size:1px; line-height:1px; mso-line-height-rule: exactly;">&nbsp;</td>
</tr>
<!-- Spacing -->
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<!-- end of full text -->
<!-- Start of seperator -->
<table width="100%" bgcolor="#ffffff" cellpadding="0" cellspacing="0" border="0" id="backgroundTable" st-sortable="seperator">
<tbody>
<tr>
<td>
<table width="600" align="center" cellspacing="0" cellpadding="0" border="0" class="devicewidth">
<tbody>
<tr>
<td align="center" height="30" style="font-size:1px; line-height:1px;">&nbsp;</td>
</tr>
<tr>
<td width="550" align="center" height="1" bgcolor="#d1d1d1" style="font-size:1px; line-height:1px;">&nbsp;</td>
</tr>
<tr>
<td align="center" height="30" style="font-size:1px; line-height:1px;">&nbsp;</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<!-- End of seperator -->
<!-- Start of Postfooter -->
<table width="100%" bgcolor="#ffffff" cellpadding="0" cellspacing="0" border="0" id="backgroundTable" st-sortable="postfooter" >
<tbody>
<tr>
<td>
<table width="600" cellpadding="0" cellspacing="0" border="0" align="center" class="devicewidth">
<tbody>
<tr>
<td width="100%">
<table width="600" cellpadding="0" cellspacing="0" border="0" align="center" class="devicewidth">
<tbody>
<tr>
<td align="center" valign="middle" style="font-family: Helvetica, arial, sans-serif; font-size: 14px;color: #666666" st-content="postfooter">
Please ignore this email if you did not initiate the process.
</td>
</tr>
<!-- Spacing -->
<tr>
<td width="100%" height="20"></td>
</tr>
<!-- Spacing -->
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<!-- End of postfooter -->
</body>
</html>

View File

@ -130,7 +130,6 @@ describe('test identity check process', function() {
assert(user_data_store.issue_identity_check_token.calledOnce); assert(user_data_store.issue_identity_check_token.calledOnce);
assert.equal(user_data_store.issue_identity_check_token.getCall(0).args[0], 'user'); assert.equal(user_data_store.issue_identity_check_token.getCall(0).args[0], 'user');
assert.equal(user_data_store.issue_identity_check_token.getCall(0).args[3], 240000); assert.equal(user_data_store.issue_identity_check_token.getCall(0).args[3], 240000);
assert(email_sender.send.getCall(0).args[2].startsWith('<a href="https://localhost/auth/test?identity_token='));
done(); done();
}); });
var handler = app.post.getCall(0).args[1]; var handler = app.post.getCall(0).args[1];