Skip to content
This repository was archived by the owner on Feb 25, 2019. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 26 additions & 32 deletions boot/mailer.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,38 +66,32 @@ function sendMail (template, locals, options, callback) {
/**
* Get mailer
*/
function setup () {
var fromVerifier = /^(?:\w|\s)+<[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}>$/igm
var transport = settings.mailer &&
nodemailer.createTransport(settings.mailer)

engineName = (settings.mailer && settings.mailer.view_engine) ||
settings.view_engine ||
'hogan'
engine = cons[engineName]

if (transport && (typeof settings.mailer.from !== 'string' ||
!fromVerifier.test(settings.mailer.from))) {
console.error(settings.mailer.from)
throw new Error('From field not provided for mailer. ' +
'Expected "Display Name <email@example.com>"')
}

defaultFrom = settings.mailer && settings.mailer.from

var mailer

exports.getMailer = function () {
if (mailer) {
return mailer
} else {
var fromVerifier = /^(?:\w|\s)+<[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}>$/igm
var transport = settings.mailer &&
nodemailer.createTransport(settings.mailer)

engineName = (settings.mailer && settings.mailer.view_engine) ||
settings.view_engine ||
'hogan'
engine = cons[engineName]

if (transport && (typeof settings.mailer.from !== 'string' ||
!fromVerifier.test(settings.mailer.from))) {
console.error(settings.mailer.from)
throw new Error('From field not provided for mailer. ' +
'Expected "Display Name <email@example.com>"')
}

defaultFrom = settings.mailer && settings.mailer.from

mailer = {
from: defaultFrom,
render: render,
transport: transport,
sendMail: sendMail
}

return mailer
var mailer = {
from: defaultFrom,
render: render,
transport: transport,
sendMail: sendMail
}
return mailer
}

module.exports = setup()
2 changes: 1 addition & 1 deletion boot/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var settings = require('./settings')
var setup = require('./setup')
var client = require('./redis').getClient()
var logger = require('./logger')(settings.logger)
require('./mailer').getMailer()
require('./mailer')
require('./migrate')()
var authenticator = require('../lib/authenticator')
var express = require('express')
Expand Down
27 changes: 27 additions & 0 deletions email/passwordlessSignin.hogan
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<html>
<body style="background: #fafafa">
<style>
.button:hover {
background: #F07911 !important;
}
.button:active, .button:focus {
box-shadow: 0 0 0 !important;
margin: 2px 4px -2px 10px !important;
}
</style>
<div style="font: normal normal 400 14px Roboto, Noto, 'Helvetica Neue', Helvetica, Arial, sans-serif; background: #fafafa; color: #212121">
<p style="margin: 8px"><strong style="color: #757575">{{providerName}}</strong></p>
<h1 style="margin: 8px; font-size: 36px; color: #e65100">
Sign in to {{providerName}}
</h1>
<p style="margin: 8px">Follow the link below by clicking the button if you
want to sign in to {{providerName}}.
</p>
<p style="margin: 8px"><a href="{{verifyURL}}" class="button"
style="display: block; display: inline-block; outline: none; width: 200px; text-align: center; margin: 0 8px; padding: 12px; box-shadow: 2px 2px 2px #757575; border-radius: 2px; -webkit-border-radius: 2px; background: #E37719; color: #fff; text-decoration: none; font-weight: 600">
Sign in to {{providerName}}</a></p>
<p style="margin: 8px; font-size: 12px">If you don't see a link above, paste the following URL into your browser: {{verifyURL}}</p>
<p style="margin: 8px; font-size: 12px">This e-mail was addressed to {{email}}</p>
</div>
</body>
</html>
25 changes: 25 additions & 0 deletions email/passwordlessSignup.hogan
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<html>
<body style="background: #fafafa">
<style>
.button:hover {
background: #F07911 !important;
}
.button:active, .button:focus {
box-shadow: 0 0 0 !important;
margin: 2px 4px -2px 10px !important;
}
</style>
<div style="font: normal normal 400 14px Roboto, Noto, 'Helvetica Neue', Helvetica, Arial, sans-serif; background: #fafafa; color: #212121">
<p style="margin: 8px"><strong style="color: #757575">{{providerName}}</strong></p>
<h1 style="margin: 8px; font-size: 36px; color: #e65100">
Verify your e-mail address
</h1>
<p style="margin: 8px">Great! You're almost done setting up your new {{providerName}} account.</p>
<p style="margin: 8px">All that's left is to <a href="{{verifyURL}}" class="button"
style="display: block; display: inline-block; outline: none; width: 200px; text-align: center; margin: 0 8px; padding: 12px; box-shadow: 2px 2px 2px #757575; border-radius: 2px; -webkit-border-radius: 2px; background: #E37719; color: #fff; text-decoration: none; font-weight: 600">
Verify your e-mail address</a></p>
<p style="margin: 8px; font-size: 12px">If you don't see a link above, paste the following URL into your browser: {{verifyURL}}</p>
<p style="margin: 8px; font-size: 12px">This e-mail was addressed to {{email}}</p>
</div>
</body>
</html>
23 changes: 23 additions & 0 deletions errors/PasswordlessDisabledError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Module dependencies
*/

var util = require('util')

/**
* PasswordlessDisabledError
*/

function PasswordlessDisabledError () {
this.name = 'PasswordlessDisabledError'
this.message = 'Email sign-in is disabled'
this.statusCode = 400
}

util.inherits(PasswordlessDisabledError, Error)

/**
* Exports
*/

module.exports = PasswordlessDisabledError
24 changes: 18 additions & 6 deletions oidc/determineProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,33 @@

var settings = require('../boot/settings')
var providers = require('../providers')
var InvalidRequestError = require('../errors/InvalidRequestError')

/**
* Determine provider middleware
*/

function determineProvider (req, res, next) {
var providerID = req.params.provider || req.body.provider
function determineProvider (options, req, res, next) {
var providerID = req.params.provider || req.connectParams.provider
if (providerID && settings.providers[providerID]) {
req.provider = providers[providerID]
}
if (options.requireProvider && !req.provider) {
return next(new InvalidRequestError('Invalid provider'))
}
next()
}

/**
* Module export
*/
module.exports = function (req, res, next) {
determineProvider({}, req, res, next)
}

module.exports = determineProvider
module.exports.setup = function (options) {
options = options || {}
options = {
requireProvider: options.requireProvider || false
}
return function (req, res, next) {
determineProvider(options, req, res, next)
}
}
6 changes: 4 additions & 2 deletions oidc/enforceReferrer.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ module.exports = function (pathname) {
pathname = [ pathname ]
}

var host = url.parse(settings.issuer).host

return function enforceReferrer (req, res, next) {
// allows changing settings for test by not reading them
// during module initialization.

var host = url.parse(settings.issuer).host
var referrer = req.get('referrer')

// Only allow requests with a referrer defined
Expand Down
2 changes: 2 additions & 0 deletions oidc/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ var oidc = {
verifyClientToken: require('./verifyClientToken'),
verifyClientIdentifiers: require('./verifyClientIdentifiers'),
verifyEmail: require('./verifyEmail'),
verifyEmailValid: require('./verifyEmailValid'),
verifyMailerConfigured: require('./verifyMailerConfigured'),
verifyRedirectURI: require('./verifyRedirectURI'),
verifyAuthorizationCode: require('./verifyAuthorizationCode')
}
Expand Down
2 changes: 1 addition & 1 deletion oidc/requireVerifiedEmail.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*/

var Role = require('../models/Role')
var mailer = require('../boot/mailer').getMailer()
var mailer = require('../boot/mailer')
var settings = require('../boot/settings')
var url = require('url')

Expand Down
2 changes: 1 addition & 1 deletion oidc/sendVerificationEmail.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function sendVerificationEmail (req, res, next) {
}

// Send verification email
mailer.getMailer().sendMail('verifyEmail', locals, {
mailer.sendMail('verifyEmail', locals, {
to: user.email,
subject: 'Verify your e-mail address'
}, function (err, responseStatus) {
Expand Down
32 changes: 32 additions & 0 deletions oidc/verifyEmailValid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Module dependencies
*/

// TODO share this with other email validations

var revalidator = require('revalidator')

/**
* Verify Email in req.connectParams.email is valid.
*
* This checks the validate on a syntax level.
*
* This middleware will not cause any errors, but instead remove
* the invalid values off of req.connectParams object.
*/

function verifyEmailValid (req, res, next) {
if (
!req.connectParams.email ||
!revalidator.validate.formats.email.test(req.connectParams.email)
) {
delete req.connectParams.email
}
next()
}

/**
* Exports
*/

module.exports = verifyEmailValid
17 changes: 17 additions & 0 deletions oidc/verifyMailerConfigured.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Module dependencies
*/

// TODO share this with other email validations

var mailer = require('../boot/mailer')

function verifyMailerConfigured (req, res, next) {
if (!mailer.transport) {
return next(new Error('Mailer not configured.'))
} else {
return next()
}
}

module.exports = verifyMailerConfigured
41 changes: 41 additions & 0 deletions protocols/Passwordless.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Passwordless Protocol
*
* This is essentially empty but is used as stub object so that other
* code dealing with protocols can stay ignorant of this.
*
* The logic which in other case is implemented in the
* Protocol class is implemented in routes/passwordless.
*/

function Passwordless (provider, configuration) {
this._provider = provider
this._configuration = configuration
this._emailField = 'email'
}

/**
* Initialize
*/

function initialize (provider, configuration) {
return new Passwordless(provider, configuration)
}

Passwordless.initialize = initialize

/*
* Authenticate request based on the contents of a form submission.
*
* @param {Object} req
* @api protected
*/
Passwordless.prototype.authenticate = function (req, options) {
throw new Error('Passwordless authenticate must not be called')
}

/**
* Exports
*/

module.exports = Passwordless
5 changes: 5 additions & 0 deletions providers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ function loadProviders (dir, files) {
if (typeof oamr !== 'undefined') {
provider.amr = oamr
}
// override the default token TTL (expiration) for the provider
var ottl = settings.providers[providerName].tokenTTL
if (typeof ottl !== 'undefined') {
provider.tokenTTL = ottl
}

// provider-specific refresh_userinfo setting
var orefuser_info = settings.providers[providerName].refresh_userinfo
Expand Down
19 changes: 19 additions & 0 deletions providers/passwordless.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Passwordless
*
* Signin/Signup by clicking a link send by email.
*/

module.exports = function (config) {
return {
id: 'passwordless',
name: 'Sign in or create account with your email',
protocol: 'Passwordless',
amr: 'email', // TODO: this is not a standard value, see https://tools.ietf.org/html/draft-jones-oauth-amr-values-01#section-2
tokenTTL: 60 * 15,
fields: [
{ name: 'email', type: 'email' }
],
usernameField: 'email' // TODO: The usernameField appears nowhere to be referenced?. This was copied from the Password provider.
}
}
36 changes: 36 additions & 0 deletions render/renderSignin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Module dependencies
*/
var _ = require('lodash')
var qs = require('qs')

var settings = require('../boot/settings')
var mailer = require('../boot/mailer')
var providers = require('../providers')

var providerInfo = {}
var providerNames = Object.keys(providers)
for (var i = 0; i < providerNames.length; i++) {
providerInfo[providerNames[i]] = providers[providerNames[i]]
}
var visibleProviders = {}
// Only render providers that are not marked as hidden
Object.keys(settings.providers).forEach(function (providerID) {
if (!settings.providers[providerID].hidden) {
visibleProviders[providerID] = settings.providers[providerID]
}
})

module.exports = function (res, params, options) {
var locals = {
params: qs.stringify(params),
request: params,
providers: visibleProviders,
providerInfo: providerInfo,
mailSupport: !!(mailer.transport)
}
if (typeof options === 'object') {
_.assign(locals, options)
}
res.render('signin', locals)
}
Loading