
Seamless JWT signing, decoding, and blacklisting in your hapi app.
Installation · Plugin Options · Usage
Follow @marcuspoehls for updates!
The Future Studio University supports development of this hapi plugin
Join the Future Studio University and Skyrocket in Node.js
Introduction
hapi-jwt
is a hapi plugin to create (sign) and access (decode) JSON web tokens (JWT).
Create a token via request.jwt.for(user)
and retrieve the payload of an existing token via request.jwt.payload()
Requirements
This plugin requires Node.js v12 or newer.
Compatibility
Major Release | hapi.js version | Node.js version |
---|---|---|
v2 |
>=18 hapi |
>=12 |
v1 |
>=18 hapi |
>=8 |
Installation
Install hapi-jwt
as a dependency to your project:
npm i @futurestudio/hapi-jwt
Register the Plugin
Register hapi-jwt
as a plugin to your hapi server.
await server.register({
plugin: require('@futurestudio/hapi-jwt'),
options: {
secret: 'your-secret' // this is the minimum required configuration to sign/decode JWTs
}
})
// went smooth like hot chocolate :)
Plugin Options
This plugin ships with a comprehensive default configuration. Please have a look at all available keys and related comments.
The following list outlines all options:
-
secret
: (string) the secret key used to sign and decode a JWT (with a symmetric algorithm). The secret is required if you don’t use a keypair provided inkeys
-
keys
: (object) describing a key pair when using asymmetric algorithms-
public
: (string) the path to the public key. The public key must be in PEM format -
private
: (string) the path to the private key. The private key can be in PEM format, OpenSSH format works as well.
-
-
algorithm
: (string, default: HS256) the JWT signing algorithm -
ttl
: (number, default: 15) the JWT lifetime in minutes -
blacklist
: (object) configurating the blacklist-
enabled
: (boolean, default: false) enables the blacklist -
cache
: (object) configures a hapi cache instance for the JWT blacklist. These options are used to create a cache via server.cache-
name
: (string) identifies both, the blacklisting cache name and segment -
provider
: (string) defines the catbox caching client, like@hapi/catbox-redis
-
-
Usage
hapi-jwt
decorates hapi’s request object with a JWT instance: request.jwt
.
Request Decorations
This decoration provides a convenient interface to interact with JWTs:
-
await request.jwt.for(user)
: creates a signed JWT -
await request.jwt.payload()
: returns the decoded JWT payload. This expects a valid JWT as a bearer token in the authorization header. -
await request.jwt.invalidate()
: decodes the JWT on the request (see payload method) and adds it to to the blacklist -
await request.jwt.invalidate('forever')
: blacklists a JWT indefinitely
Create a JWT
Creating a (signed) JWT is as simple as await request.jwt.for({ id: 1, name: 'Marcus' })
:
When creating the JWT, hapi-jwt
creates a handful of claims besides your provided data. It generates the following claims:
-
jti
: a token identifier -
iat
: issued at date in seconds -
nbf
: validity start date in seconds -
exp
: expiration date in seconds, based on the TTL -
iss
: retrieves the token issuer from the request domain -
sub
: if the givenuser
object contains anid
field, it will be used for thesub
claim
server.route({
method: 'POST',
path: '/login',
options: {
auth: 'basic', // assume the login route requires basic authentication
handler: async request => {
const token = await request.jwt.for(request.auth.credentials)
return token
}
}
})
You can debug a created JWT on jwt.io and have a look at the token headers and payload.
A sample token payload looks like this:
{
jti: 'babf5099a4561173c91f2cdc6c61c1aa',
iss: 'http://localhost',
iat: 1574094111,
nbf: 1574094111,
exp: 1574095011,
sub: 1
}
Decode a JWT and access the payload
You can access the JWT payload via await request.jwt.payload()
. Accessing the payload expects a valid JWT in the authorization request header. The authorization header must be in a format like Bearer <your-jwt>
.
Calling request.jwt.payload()
returns a Payload
instance containing the JWT claims set:
server.route({
method: 'GET',
path: '/me',
options: {
auth: 'jwt',
handler: async request => {
const payload = await request.jwt.payload()
const user = payload.has('sub')
? await User.findbyId(payload.get('sub'))
: await User.findOne({ email: payload.get('email') })
return user
}
}
})
Payload
A payload instance returned from await request.jwt.payload()
has the following methods:
-
toObject
: returns a plain JavaScript object -
get(key)
: returns the value identified bykey
-
has(key)
: returns a boolean,true
if the payload contains the claim identified bykey
, otherwisefalse
-
missing(key)
: returns a boolean,true
if the payload does not contain the claim identified by key, otherwisefalse
JWT Blacklist
Activating the JWT blacklist requires a cache. hapi-jwt
uses hapi’s server.cache
method to provision a blacklist storage.
When using the blacklist, please ensure a persistent caching store, like Redis via @hapi/catbox-redis or Memcached via @hapi/catbox-memcached. Using hapi’s default internal caching instance stores the blacklist in-memory and will be gone when restarting the server.
Links & Resources
- hapi tutorial series with 100+ tutorials
Contributing
- Create a fork
- Create your feature branch:
git checkout -b my-feature
- Commit your changes:
git commit -am 'Add some feature'
- Push to the branch:
git push origin my-new-feature
- Submit a pull request
🚀
License
MIT © Future Studio
futurestud.io · GitHub @futurestudio · Twitter @futurestud_io