Skip to content
This repository has been archived by the owner on Sep 14, 2022. It is now read-only.

Commit

Permalink
Splits token generation and validation
Browse files Browse the repository at this point in the history
  • Loading branch information
arcanis authored and dougwilson committed Nov 20, 2016
1 parent 1075d5d commit cb74e9b
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 66 deletions.
108 changes: 64 additions & 44 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* csurf
* Copyright(c) 2011 Sencha Inc.
* Copyright(c) 2014 Jonathan Ong
* Copyright(c) 2014 Maël Nison
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/
Expand Down Expand Up @@ -39,6 +40,25 @@ module.exports = csurf
*/

function csurf (options) {
var generator = csurf.generator(options)
var validator = csurf.validator(options)

return function csurf (req, res, next) {
generator(req, res, function () {
try {
validator(req, res, next)
} catch (error) {
next(error)
}
})
}
}

csurf.generator = function csurfGeneratorBuilder (options) {
var curToken = null
var curSecret

// default options
var opts = options || {}

// get cookie options
Expand All @@ -47,65 +67,65 @@ function csurf (options) {
// get session options
var sessionKey = opts.sessionKey || 'session'

// get value getter
var value = opts.value || defaultValue
// token manager
var tokenManager = new Tokens(opts)

// token repo
var tokens = new Tokens(opts)
// csrf parameter getter
var getRequestCsrf = opts.value || defaultValue

// ignored methods
var ignoreMethods = opts.ignoreMethods === undefined
? ['GET', 'HEAD', 'OPTIONS']
: opts.ignoreMethods
return function csurfGenerator (req, res, next) {
req.csrfToken = function () {
var secret = getSecret(req, sessionKey, cookie)

if (!Array.isArray(ignoreMethods)) {
throw new TypeError('option ignoreMethods must be an array')
}
if (curToken !== null && curSecret === secret) {
// use cached token if secret has not changed
return curToken
} else {
if (secret === undefined) {
// no csrf in the session, so we create a new one
secret = tokenManager.secretSync()
setSecret(req, res, sessionKey, secret, cookie)
}

// generate lookup
var ignoreMethod = getIgnoredMethods(ignoreMethods)
// update cached token
curToken = tokenManager.create(secret)
curSecret = secret

return function csrf (req, res, next) {
var secret = getSecret(req, sessionKey, cookie)
var token
return curToken
}
}

// lazy-load token getter
req.csrfToken = function csrfToken () {
var sec = !cookie
? getSecret(req, sessionKey, cookie)
: secret
req.checkCsrf = function () {
var secret = getSecret(req, sessionKey, cookie)

// use cached token if secret has not changed
if (token && sec === secret) {
return token
}
return tokenManager.verify(secret, getRequestCsrf(req))
}

// generate & set new secret
if (sec === undefined) {
sec = tokens.secretSync()
setSecret(req, res, sessionKey, sec, cookie)
}
next()
}
}

// update changed secret
secret = sec
csurf.validator = function csurfValidatorBuilder (options) {
// default options
options = options || {}

// create new token
token = tokens.create(secret)
// ignored methods
var ignoreMethods = options.ignoreMethods === undefined
? ['GET', 'HEAD', 'OPTIONS'] : options.ignoreMethods

return token
}
if (!Array.isArray(ignoreMethods)) {
throw new TypeError('option ignoreMethods must be an array')
}

// generate & set secret
if (!secret) {
secret = tokens.secretSync()
setSecret(req, res, sessionKey, secret, cookie)
}
// generate lookup
var ignoreMethod = getIgnoredMethods(ignoreMethods)

return function csurfValidator (req, res, next) {
// verify the incoming token
if (!ignoreMethod[req.method] && !tokens.verify(secret, value(req))) {
return next(createError(403, 'invalid csrf token', {
if (!ignoreMethod[req.method] && !req.checkCsrf()) {
throw createError(403, 'invalid csrf token', {
code: 'EBADCSRFTOKEN'
}))
})
}

next()
Expand Down
23 changes: 1 addition & 22 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,31 +235,10 @@ describe('csurf', function () {
app.use(csurf())

request(app)
.get('/')
.expect(500, /misconfigured csrf/, done)
})

it('should error without cookie secret storage', function (done) {
var app = connect()

app.use(csurf({ cookie: true }))

request(app)
.get('/')
.post('/')
.expect(500, /misconfigured csrf/, done)
})

it('should error without cookieParser secret and signed cookie storage', function (done) {
var app = connect()

app.use(cookieParser())
app.use(csurf({ cookie: { signed: true } }))

request(app)
.get('/')
.expect(500, /cookieParser.*secret/, done)
})

describe('with "ignoreMethods" option', function () {
it('should reject invalid value', function () {
assert.throws(createServer.bind(null, {ignoreMethods: 'tj'}), /option ignoreMethods/)
Expand Down

0 comments on commit cb74e9b

Please sign in to comment.