Skip to content
This repository has been archived by the owner on Jul 15, 2020. It is now read-only.

Commit

Permalink
Refactored auth option handling with ES6 params
Browse files Browse the repository at this point in the history
This makes it much clearer what the method options are, what options are being passed and removes the amount of code required.

Also improved error handling on startup.
  • Loading branch information
iaincollins committed Feb 13, 2017
1 parent 9457042 commit b309899
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 54 deletions.
10 changes: 8 additions & 2 deletions index.js
Expand Up @@ -75,8 +75,10 @@ app.prepare()
})
.then(db => {
// Once DB is available, setup sessions and routes for authentication
auth.configure(app, server, {
db: db,
auth.configure({
app: app,
server: server,
user: db.models.user,
secret: process.env.SESSION_SECRET
})

Expand Down Expand Up @@ -106,3 +108,7 @@ app.prepare()
console.log('> Ready on http://localhost:' + process.env.PORT + ' [' + process.env.NODE_ENV + ']')
})
})
.catch(err => {
console.log('An error occurred, unable to start the server')
console.log(err)
})
4 changes: 2 additions & 2 deletions package.json
@@ -1,7 +1,7 @@
{
"name": "nextjs-starter",
"version": "2.5.11",
"description": "A starter Next.js project",
"version": "2.5.13",
"description": "A starter Next.js project with email and oAuth authentication",
"author": "Iain Collins <me@iaincollins.com>",
"license": "ISC",
"repository": "https://github.com/iaincollins/nextjs-starter.git",
Expand Down
82 changes: 43 additions & 39 deletions routes/auth.js
Expand Up @@ -17,48 +17,47 @@ const FileStore = require('session-file-store')(session)
const nodemailer = require('nodemailer')
const csrf = require('lusca').csrf()
const uuid = require('uuid/v4')
const passport = require('./passport')

exports.configure = (app, server, options) => {
if (!options) {
options = {}
const passportStrategies = require('./passport-strategies')

exports.configure = ({
app = null, // Next.js App
server = null, // Express Server
user: User = null, // User model
// URL base path for authentication routes
path = '/auth',
// Directory in ./pages/ where auth pages can be found
pages = 'auth',
// Secret used to encrypt session data on the server
secret = 'change-me',
// Sessions store for express-session (defaults to /tmp/sessions file store)
store = new FileStore({path: '/tmp/sessions', secret: secret}),
// Max session age in ms (default is 4 weeks)
// NB: With 'rolling: true' passed to session() the session expiry time will
// be reset every time a user visits the site again before it expires.
maxAge = 60000 * 60 * 24 * 7 * 4,
// How often the client should revalidate the session in ms (default 60s)
// Does not impact the session life on the server, but causes the client to
// always refetch session info after N seconds has elapsed since last
// checked. Sensible values are between 0 (always check the server) and a
// few minutes.
clientMaxAge = 60000,
// URL of the server (e.g. 'http://www.example.com'). Used when sending
// sign in links in emails. Autodetects to hostname if null.
serverUrl = null,
// Mailserver configuration for nodemailer (defaults to localhost if null)
mailserver = null
} = {}) => {
if (app === null) {
throw new Error('app option must be a next server instance')
}

if (!options.db || !options.db.models || !options.db.models.user) {
throw new Error('Database with user model is a required option!')
if (server === null) {
throw new Error('server option must be an express server instance')
}

const User = options.db.models.user

// Base path for auth URLs
const path = options.path || '/auth'

// Directory for auth pages
const pages = options.pages || 'auth'

// The secret is used to encrypt/decrypt sessions (you should pass your own!)
const secret = options.secret || 'change-me'

// Configure session store (defaults to using file system)
const store = options.store || new FileStore({path: '/tmp/sessions', secret: secret})

// Max session age in ms (default is 4 weeks)
// NB: With 'rolling: true' passed to session() the session expiry time will
// be reset every time a user visits the site again before it expires.
const maxAge = options.maxAge || 60000 * 60 * 24 * 7 * 4

// How often the client should revalidate the session in ms (default 60s)
// Does not impact the session life on the server, but causes the client to
// always refetch session info after N seconds has elapsed since last checked.
// Sensible values are between 0 (always check the server) and a few minutes.
const clientMaxAge = options.clientMaxAge || 60000

// URL of the server (e.g. 'http://www.example.com'). Used when sending
// sign-in emails. Autodetects current server hostname / domain if null.
const serverUrl = options.serverUrl || null

// Mailserver (defaults to sending from localhost if null)
const mailserver = options.mailserver || null
if (User === null) {
throw new Error('user option must be a User model')
}

// Load body parser to handle POST requests
server.use(bodyParser.json())
Expand All @@ -84,7 +83,12 @@ exports.configure = (app, server, options) => {
})

// With sessions connfigured (& before routes) we need to configure Passport
passport.configure(app, server, {db: options.db, path: path})
// and trigger passport.initialize() before we add any routes
passportStrategies.configure({
app: app,
server: server,
user: User
})

// Add route to get CSRF token via AJAX
server.get(path + '/csrf', (req, res) => {
Expand Down
25 changes: 14 additions & 11 deletions routes/passport.js → routes/passport-strategies.js
@@ -1,23 +1,27 @@
/**
* Confgiure Passport Strategies
* Configure Passport Strategies
*/
'use strict'

const passport = require('passport')

exports.configure = (app, server, options) => {
if (!options) {
options = {}
exports.configure = ({
app = null, // Next.js App
server = null, // Express Server
user: User = null, // User model
path = '/auth' // URL base path for authentication routes
} = {}) => {
if (app === null) {
throw new Error('app option must be a next server instance')
}

if (!options.db || !options.db.models || !options.db.models.user) {
throw new Error('Database with user model is a required option!')
if (server === null) {
throw new Error('server option must be an express server instance')
}

const User = options.db.models.user

// Base path for auth URLs
const path = options.path || '/auth'
if (User === null) {
throw new Error('user option must be a User model')
}

// Tell Passport how to seralize/deseralize user accounts
passport.serializeUser(function (user, done) {
Expand Down Expand Up @@ -154,7 +158,6 @@ exports.configure = (app, server, options) => {
if (err) {
return done(err)
}

// If we already have an account associated with that email address in the databases, the user
// should sign in with that account instead (to prevent them creating two accounts by mistake)
// Note: Automatically linking them here could expose a potential security exploit allowing someone
Expand Down

0 comments on commit b309899

Please sign in to comment.