Skip to content

Commit

Permalink
Merge pull request #24 from niekheezemans/master
Browse files Browse the repository at this point in the history
Fix for not able to pass in default options on plugin register.
  • Loading branch information
mcollina authored Oct 16, 2018
2 parents 92f6dee + c2b872e commit 85ae2cf
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 18 deletions.
9 changes: 9 additions & 0 deletions certs/private.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJAdSOBnRt497PErO0oTyccx5WAwN/IdGTUCiUbjUOOMsto0GJ3sVvV
s43u4SrA/FgrwBQvec4/XbV19As6Qq9TywIDAQABAkAb9MplTX40V9ITl1IseeAz
fenJnl7/8GRuu2Z082+HjZLZBKL6IsanPbWayVLNjoohkn2vFm9D9IlQNyEuMcTZ
AiEAxqk2N7iCqvh8nX2DtkQYy+vtURiR2eyllBU7Xxe+nX8CIQCW8rlq26MHDMRY
Zxc++3ZTAuchZOqnCQCbdmoYMiSHtQIhAJC5tUXipU7ot+AEEHA4K9hjiAAMZjTc
scOvtBESu0sBAiEAiA2Srgv1b19EgeG5hB0m0z9kkCbjkVZ946Hhkplvb5kCIBIV
ldm/7SHCMoN7gBZIc3Xo90YXxmWJK84yaXr2705t
-----END RSA PRIVATE KEY-----
4 changes: 4 additions & 0 deletions certs/public.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFswDQYJKoZIhvcNAQEBBQADSgAwRwJAdSOBnRt497PErO0oTyccx5WAwN/IdGTU
CiUbjUOOMsto0GJ3sVvVs43u4SrA/FgrwBQvec4/XbV19As6Qq9TywIDAQAB
-----END PUBLIC KEY-----
2 changes: 2 additions & 0 deletions certs/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# RSA Signatures - Certificates
Demo certificates generates with http://travistidwell.com/jsencrypt/demo/
50 changes: 34 additions & 16 deletions jwt.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,30 @@ function wrapStaticSecretInCallback (secret) {
}

function fastifyJwt (fastify, options, next) {
var secretKey, secretPass
if (!options.secret) {
return next(new Error('missing secret'))
}

var secret = options.secret

if (typeof secret === 'object') {
if (!secret.key || !secret.passphrase) {
return next(new Error('missing secret key and/or passphrase'))
}
secretKey = secret.key
secretPass = secret.passphrase
} else {
secretKey = secretPass = secret
}
var secretCallback = secret
if (typeof secretCallback !== 'function') { secretCallback = wrapStaticSecretInCallback(secretCallback) }

var defaultOptions = options.options || {}

if (defaultOptions && defaultOptions.algorithm && defaultOptions.algorithm.includes('RS') && typeof secret === 'string') {
return next(new Error(`RSA Signatures set as Algorithm in the options require a key and passphrase to be set as the secret`))
}

fastify.decorate('jwt', {
decode: decode,
sign: sign,
Expand All @@ -37,34 +53,36 @@ function fastifyJwt (fastify, options, next) {

next()

function sign (payload, options, callback) {
function sign (payload, signOptions, callback) {
assert(payload, 'missing payload')
options = options || {}
if (typeof options === 'function') {
callback = options
options = {}
signOptions = signOptions || {}
if (typeof signOptions === 'function') {
callback = signOptions
signOptions = {}
}
signOptions = Object.assign(defaultOptions, signOptions)
delete signOptions['algorithms']

if (typeof callback === 'function') {
jwt.sign(payload, secret, options, callback)
jwt.sign(payload, secretKey, signOptions, callback)
} else {
return jwt.sign(payload, secret, options)
return jwt.sign(payload, secretKey, signOptions)
}
}

function verify (token, options, callback) {
function verify (token, verifyOptions, callback) {
assert(token, 'missing token')
assert(secret, 'missing secret')
options = options || {}
if (typeof options === 'function') {
callback = options
options = {}
verifyOptions = verifyOptions || {}
if (typeof verifyOptions === 'function') {
callback = verifyOptions
verifyOptions = {}
}

verifyOptions = Object.assign(defaultOptions, verifyOptions)
if (typeof callback === 'function') {
jwt.verify(token, secret, options, callback)
jwt.verify(token, secretPass, verifyOptions, callback)
} else {
return jwt.verify(token, secret, options)
return jwt.verify(token, secretPass, verifyOptions)
}
}

Expand Down
161 changes: 159 additions & 2 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
'use strict'

const fs = require('fs')
const path = require('path')
const test = require('tap').test
const Fastify = require('fastify')

const jwt = require('./jwt')

const publicKey = fs.readFileSync(`${path.join(__dirname, 'certs')}/public.key`, 'utf8')
const privateKey = fs.readFileSync(`${path.join(__dirname, 'certs')}/private.key`, 'utf8')

test('register', function (t) {
t.plan(3)
t.plan(7)

t.test('expose jwt methods', function (t) {
t.plan(6)
Expand All @@ -32,6 +37,38 @@ test('register', function (t) {
})
})

t.test('secret as an object', function (t) {
t.plan(1)
const fastify = Fastify()
fastify.register(jwt, { secret: { key: privateKey, passphrase: publicKey } }).ready(function (error) {
t.is(error, null)
})
})

t.test('options as an object with default HS algorithm', function (t) {
t.plan(1)
const fastify = Fastify()
fastify.register(jwt, { secret: 'test', options: { issuer: 'Some issuer', subject: 'Some subject', audience: 'Some audience' } }).ready(function (error) {
t.is(error, null)
})
})

t.test('options and secret as an object with RS algorithm', function (t) {
t.plan(1)
const fastify = Fastify()
fastify.register(jwt, { secret: { key: privateKey, passphrase: publicKey }, options: { issuer: 'Some issuer', subject: 'Some subject', audience: 'Some audience', algorithm: 'RS256' } }).ready(function (error) {
t.is(error, null)
})
})

t.test('secret as string, options as an object with RS algorithm', function (t) {
t.plan(1)
const fastify = Fastify()
fastify.register(jwt, { secret: 'test', options: { issuer: 'Some issuer', subject: 'Some subject', audience: 'Some audience', algorithm: 'RS256' } }).ready(function (error) {
t.is(error.message, 'RSA Signatures set as Algorithm in the options require a key and passphrase to be set as the secret')
})
})

t.test('secret as a function', function (t) {
t.plan(2)

Expand Down Expand Up @@ -94,7 +131,7 @@ test('register', function (t) {
})
})

test('sign and verify', function (t) {
test('sign and verify with HS-secret', function (t) {
t.plan(2)

t.test('server methods', function (t) {
Expand Down Expand Up @@ -130,6 +167,126 @@ test('sign and verify', function (t) {
})
})

t.test('route methods', function (t) {
t.plan(2)

const fastify = Fastify()
fastify.register(jwt, { secret: 'test' })

fastify.post('/signSync', function (request, reply) {
return reply.jwtSign(request.body).then(function (token) {
return { token }
})
})

fastify.get('/verifySync', function (request, reply) {
return request.jwtVerify().then(function (decodedToken) {
return reply.send(decodedToken)
})
})

fastify.post('/signAsync', function (request, reply) {
reply.jwtSign(request.body, function (error, token) {
return reply.send(error || { token })
})
})

fastify.get('/verifyAsync', function (request, reply) {
request.jwtVerify(function (error, decodedToken) {
return reply.send(error || decodedToken)
})
})

fastify
.ready()
.then(function () {
t.test('synchronous', function (t) {
t.plan(2)

fastify.inject({
method: 'post',
url: '/signSync',
payload: { foo: 'bar' }
}).then(function (signResponse) {
const token = JSON.parse(signResponse.payload).token
t.ok(token)

fastify.inject({
method: 'get',
url: '/verifySync',
headers: {
authorization: `Bearer ${token}`
}
}).then(function (verifyResponse) {
const decodedToken = JSON.parse(verifyResponse.payload)
t.is(decodedToken.foo, 'bar')
})
})
})

t.test('with callbacks', function (t) {
t.plan(2)

fastify.inject({
method: 'post',
url: '/signAsync',
payload: { foo: 'bar' }
}).then(function (signResponse) {
const token = JSON.parse(signResponse.payload).token
t.ok(token)

fastify.inject({
method: 'get',
url: '/verifyAsync',
headers: {
authorization: `Bearer ${token}`
}
}).then(function (verifyResponse) {
const decodedToken = JSON.parse(verifyResponse.payload)
t.is(decodedToken.foo, 'bar')
})
})
})
})
})
})

test('sign and verify with RSA and options', function (t) {
t.plan(2)

t.test('server methods', function (t) {
t.plan(2)

const fastify = Fastify()
fastify.register(jwt, { secret: { key: privateKey, passphrase: publicKey }, options: { issuer: 'Some issuer', subject: 'Some subject', audience: 'Some audience', algorithm: 'RS256' } })

fastify
.ready()
.then(function () {
t.test('synchronous', function (t) {
t.plan(1)

const token = fastify.jwt.sign({ foo: 'bar' })
const decoded = fastify.jwt.verify(token)

t.is(decoded.foo, 'bar')
})

t.test('with callbacks', function (t) {
t.plan(3)

fastify.jwt.sign({ foo: 'bar' }, function (error, token) {
t.error(error)

fastify.jwt.verify(token, function (error, decoded) {
t.error(error)
t.is(decoded.foo, 'bar')
})
})
})
})
})

t.test('route methods', function (t) {
t.plan(2)

Expand Down

0 comments on commit 85ae2cf

Please sign in to comment.