Skip to content

Commit

Permalink
Merge pull request #10 from Ethan-Arrowood/revamp
Browse files Browse the repository at this point in the history
complete rebuild
  • Loading branch information
delvedor authored Jan 18, 2018
2 parents 3ebc704 + 18d6b9a commit 9f7fc75
Show file tree
Hide file tree
Showing 4 changed files with 487 additions and 77 deletions.
120 changes: 116 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ npm i fastify-jwt --save
```

## Usage
Register it as plugin and then access it via `jwt`.
The api is the same of [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken), refer to their documentation to find how use the utilities.
Register as a plugin. This will decorate your `fastify` instance with the standard [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken) methods `decode`, `sign`, and `verify`; refer to their documentation to find how to use the utilities. It will also register `request.jwtVerify` and `reply.jwtSign`. You must pass a `secret` when registering the plugin.

```js
const fastify = require('fastify')
fastify.register(require('fastify-jwt'), { secret: 'supersecret' }, err => {
if (err) throw err
fastify.register(require('fastify-jwt'), {
secret: 'supersecret'
})

fastify.post('/signup', (req, reply) => {
Expand All @@ -30,6 +29,119 @@ fastify.listen(3000, err => {
})
```

## API Spec

### fastify-jwt
`fastify-jwt` is a fastify plugin. You must pass a `secret` to the `options` parameter. The `secret` can be a primitive type String or a function that returns a String. Function based `secret` is supported by the `request.jwtVerify()` and `reply.jwtSign()` methods and is called with `request`, `reply`, and `callback` parameters.
#### Example
```js
const fastify = require('fastify')()
const jwt = require('fastify-jwt')
// secret as a string
fastify.register(jwt, { secret: 'supersecret' })
// secret as a function
fastify.register(jwt, {
secret: function (request, reply, callback) {
// do something
callback(null, 'supersecret')
}
})
```

### fastify.jwt.sign(payload [,options] [,callback])
The `sign` method is an implementation of [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken#jwtsignpayload-secretorprivatekey-options-callback) `.sign()`. Can be used asynchronously by passing a callback function; synchronously without a callback.

### fastify.jwt.verify(token, [,options] [,callback])
The `verify` method is an implementation of [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken#jwtverifytoken-secretorpublickey-options-callback) `.verify()`. Can be used asynchronously by passing a callback function; synchronously without a callback.
#### Example
```js
const token = fastify.jwt.sign({ foo: 'bar' })
// synchronously
const decoded = fastify.jwt.verify(token)
// asycnhronously
fastify.jwt.verify(token, (err, decoded) => {
if (err) fastify.log.error(err)
fastify.log.info(`Token verified. Foo is ${decoded.foo}`)
})
```

### fastify.jwt.decode(token [,options])
The `decode` method is an implementation of [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken#jwtdecodetoken--options) `.decode()`. Can only be used synchronously.
#### Example
```js
const token = fastify.jwt.sign({ foo: 'bar' })
const decoded = fastify.jwt.decode(token)
fastify.log.info(`Decoded JWT: ${decoded}`)
```

### fastify.jwt.secret
For your convenience, the `secret` you specify during `.register` is made available via `fastify.jwt.secret`. `request.jwtVerify()` and `reply.jwtSign()` will wrap non-function secrets in a callback function. `request.jwtVerify()` and `reply.jwtSign()` use an asynchronous waterfall method to retrieve your secret. It's recommended that your use these methods if your `secret` method is asynchronous.

### reply.jwtSign(payload, [options,] callback)
### request.jwtVerify([options,] callback)
These methods are very similar to their standard jsonwebtoken counterparts.
#### Example
```js
const fastify = require('fastify')()

fastify.register(jwt, {
secret: function (request, reply, callback) {
// do something
callback(null, 'supersecret')
}
})

fastify.post('/sign', function (request, reply) {
reply.jwtSign(request.body.payload, function (err, token) {
return reply.send(err || { 'token': token })
})
})

fastify.get('/verify', function (request, reply) {
request.jwtVerify(function (err, decoded) {
return reply.send(err || decoded)
})
})

fastify.listen(3000, function (err) {
if (err) fastify.log.error(err)
fastify.log.info(`Server live on port: ${fastify.server.address().port}`)

// sign payload and get JWT
request({
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: {
payload: {
foo: 'bar'
}
},
uri: `http://localhost:${fastify.server.address().port}/sign`,
json: true
}, function (err, response, body) {
if (err) fastify.log.error(err)
fastify.log.info(`JWT token is ${body}`)

// verify JWT
request({
method: 'GET',
headers: {
'Content-Type': 'application/json',
authorization: 'Bearer ' + sign.token
},
uri: 'http://localhost:' + fastify.server.address().port + '/verify',
json: true
}, function (err, response, body) {
if (err) fastify.log.error(err)
fastify.log.info(`JWT verified. Foo is ${body.bar}`)
})
})
})
```


## Acknowledgements

This project is kindly sponsored by:
Expand Down
104 changes: 95 additions & 9 deletions jwt.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
'use strict'

const fp = require('fastify-plugin')
const JWT = require('jsonwebtoken')
const assert = require('assert')
var fp = require('fastify-plugin')
var JWT = require('jsonwebtoken')
var assert = require('assert')
var steed = require('steed')

function fastifyJWT (fastify, opts, next) {
if (!opts.secret) {
function wrapStaticSecretInCallback (secret) {
return function (request, payload, cb) {
return cb(null, secret)
}
}

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

const secret = opts.secret
var secret = options.secret
var secretCallback = secret
if (typeof secretCallback !== 'function') { secretCallback = wrapStaticSecretInCallback(secretCallback) }

fastify.decorate('jwt', {
decode: decode,
sign: sign,
verify: verify,
decode: decode,
secret: secret
secret: options.secret
})

fastify.decorateReply('jwtSign', replySign)

fastify.decorateRequest('jwtVerify', requestVerify)

next()

function sign (payload, options, callback) {
Expand Down Expand Up @@ -56,6 +69,79 @@ function fastifyJWT (fastify, opts, next) {
options = options || {}
return JWT.decode(token, options)
}

function replySign (payload, options, next) {
if (typeof options === 'function') {
next = options
options = {}
} // support no options
var reply = this
if (next === undefined) {
return new Promise(function (resolve, reject) {
reply.jwtSign(payload, options, function (err, val) {
err ? reject(err) : resolve(val)
})
})
}

if (!payload) {
return next(new Error('jwtSign requires a payload'))
}
steed.waterfall([
function getSecret (callback) {
secretCallback(reply.request, payload, callback)
},
function sign (secret, callback) {
JWT.sign(payload, secret, options, callback)
}
], next)
} // end sign

function requestVerify (options, next) {
if (typeof options === 'function') {
next = options
options = {}
} // support no options

var request = this

if (next === undefined) {
return new Promise(function (resolve, reject) {
request.jwtVerify(options, function (err, val) {
err ? reject(err) : resolve(val)
})
})
}

var token
if (request.headers && request.headers.authorization) {
var parts = request.headers.authorization.split(' ')
if (parts.length === 2) {
var scheme = parts[0]
token = parts[1]

if (!/^Bearer$/i.test(scheme)) {
return next(new Error('Format is Authorization: Bearer [token]'))
}
}
} else {
return next(new Error('No Authorization was found in request.headers'))
}

var decodedToken = JWT.decode(token, options)
steed.waterfall([
function getSecret (callback) {
secretCallback(request, decodedToken, callback)
},
function verify (secret, callback) {
JWT.verify(token, secret, options, callback)
}
], function (err, result) {
if (err) next(err)
request.user = result
next(null, result)
})
} // end verify
}

module.exports = fp(fastifyJWT, '>=0.13.1')
module.exports = fp(fastifyJwt, '>= 0.39')
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@
"homepage": "https://github.com/fastify/fastify-jwt#readme",
"dependencies": {
"fastify-plugin": "^0.2.1",
"jsonwebtoken": "^8.1.0"
"jsonwebtoken": "^8.1.0",
"steed": "^1.1.3"
},
"devDependencies": {
"fastify": "^0.30.2",
"fastify": "^0.39",
"request": "^2.83.0",
"request-promise-native": "^1.0.5",
"standard": "^10.0.3",
"tap": "^10.7.2"
"tap": "^11.0.1"
}
}
Loading

0 comments on commit 9f7fc75

Please sign in to comment.