authorize-jwt
Simple JSON Web Token authorization module for Node.js.
- Uses
jose
to perform verification of tokens. - Uses
pub-keystore
to retrieve and manage token issuers' public keys. - Adds extra check for maximum expiry time.
- Extracts tokens from HTTP Authorization (Basic or Bearer) headers or query strings.
- Unit tests with 100% code coverage.
- Support for the Web Authentication browser API.
-
Use case (thanks to Emil Lundberg for the summary):
- Alice logs in and proves she's an admin (e.g. by signing a challenge generated on the server, which is then verified on the server).
- Alice uses client side script to generate a JWT.
- Client side script uses Alice's public key credential to sign the JWT.
- JWT with Alice's admin signature is sent to ordinary user Bob.
- Bob sends signed JWT to server, and receives a perk.
- Uses Webauthn4JS.
- See this test for an example.
-
Use case (thanks to Emil Lundberg for the summary):
The API is described here.
Example
const authorize_jwt = require('..');
const http = require('http');
const assert = require('assert');
const { generateKeyPair, SignJWT, exportJWK } = require('jose');
const the_uri = 'mailto:example@davedoesdev.com';
const audience = 'urn:authorize-jwt:example';
process.on('unhandledRejection', err => { throw err });
// create authorization object
authorize_jwt({
db_type: 'pouchdb',
db_for_update: true, // we're going to update a public key
max_token_expiry: 60
}, async function (err, authz) {
assert.ifError(err);
const { privateKey: priv_key, publicKey: pub_key } = await generateKeyPair('EdDSA');
var the_issuer_id, the_rev, change_rev;
async function doit() {
assert.equal(the_rev, change_rev);
// create and sign a JWT
const the_token = await new SignJWT({
foo: 'bar'
})
.setProtectedHeader({
alg: 'EdDSA'
})
.setIssuer(the_issuer_id)
.setAudience(audience)
.setExpirationTime('1m')
.sign(priv_key);
// send and receive the token via HTTP Basic Auth
const http_server = http.createServer(function (req, res) {
authz.get_authz_data(req, function (err, info, token) {
assert.ifError(err);
assert.equal(info, 'test');
assert.equal(token, the_token);
// authorize the token
authz.authorize(token, ['EdDSA'], function (err, payload, uri, rev) {
assert.ifError(err);
assert.equal(uri, the_uri);
assert.equal(rev, the_rev);
assert.equal(payload.foo, 'bar');
res.end();
http_server.close(cb);
});
});
}).listen(6000, '127.0.0.1', function () {
http.request({ hostname: '127.0.0.1', port: 6000, auth: 'test:' + the_token }).end();
});
}
// just to demonstrate change events
authz.keystore.once('change', function (uri, rev) {
assert.equal(uri, the_uri);
change_rev = rev;
if (the_rev) { doit(); }
});
// add public key to the store
authz.keystore.add_pub_key(the_uri, await exportJWK(pub_key), function (err, issuer_id, rev) {
assert.ifError(err);
the_issuer_id = issuer_id;
the_rev = rev;
if (change_rev) { doit(); }
});
});
Installation
npm install authorize-jwt
Licence
Test
grunt test
Code Coverage
grunt coverage
c8 results are available here.
Coveralls page is here.
Lint
grunt lint
API
Source: index.js
- module.exports
- AuthorizeJWT.prototype.get_authz_data
- AuthorizeJWT.prototype.authorize
- AuthorizeJWT.prototype.close
module.exports(config, cb)
Creates a JWT authorizer.
Parameters:
-
{Object} config
Configures the authorizer.config
is passed down topub-keystore
,jose
andwebauthn4js
. The following extra properties are supported:-
{Integer} [max_token_expiry]
If set then all JSON Web Tokens must expire sooner thanmax_token_expiry
seconds in the future (from the time they're presented). Defaults toundefined
. -
{Boolean} [WEBAUTHN_MODE]
If truthy then instead of verifying standalone JSON Web Tokens, the authorizer will verify signed assertions generated by the Web Authentication browser API. The challenge contained in each assertion's client data must be an unsigned JSON Web Token. Defaults tofalse
. -
{Function} [complete_webauthn_token]
This applies only ifWEBAUTHN_MODE
is truthy and is mandatory if you pass strings toauthorize
. It will receive the following arguments:-
{Object} partial_webauthn_token
. This is a partially-complete Web Authentication assertion containingissuer_id
andcar
properties (seeauthorize
for a description). -
{Function} cb
Call this function when you have filled in the remaining properties. It takes the following arguments:-
{Object} err
If an error occurred then pass details of the error, otherwise passnull
. -
{Object} webauthn_token
This should have the same properties aspartial_webauthn_token
plus theopts
property, if required (seeauthorize
). It's safe to modifypartial_webauthn_token
and then pass it here.
-
-
-
{PubKeyStore} [keystore]
If you have a pre-existingPubKeyStore
instance, pass it here. The authorizer will use it to look up the public keys of token issuers. The default is to make a new one by callingpub-keystore
. -
{WebAuthn4JS} [webAuthn]
If you have a pre-existingWebAuthn4JS
instance, pass it here. The default is to make a new one by callingwebauthn4js
.
-
-
{Function} cb
Function called with the result of creating the authorizer. It will receive the following arguments:-
{Object} err
If an error occurred then details of the error, otherwisenull
. -
{AuthorizeJWT} authz
TheAuthorizeJWT
object. As well asAuthorizeJWT
's prototype methods, it has the following properties:-
{PubKeyStore} keystore
ThePubKeyStore
object that the authorizer is using to look up the public keys of token issuers. For example, you could listen to PubKeyStore.events.change events so you know that previously verified tokens are invalid. -
{WebAuthn4JS} [webAuthn]
TheWebAuthn4JS
object being used to verify assertions, ifWEBAUTHN_MODE
is truthy.
-
-
AuthorizeJWT.prototype.get_authz_data(req, cb)
Extracts JSON Web Tokens from a HTTP request.
Parameters:
-
{http.IncomingMessage} req
HTTP request object which should contain the tokens either in theAuthorization
header (Basic or Bearer auth) or in theauthz_token
query string parameter. -
{Function} cb
Function called with the tokens obtained fromreq
. TheAuthorization
header is used in preference to the query string.cb
will receive the following arguments:-
{Object} err
If an error occurred then details of the error, otherwisenull
. -
{String} info
Extra information retrieved fromreq
along with the tokens. This is either the username extracted from theAuthorization
header or theauthz_info
query string parameter. -
{String|Array} token
The JSON Web Tokens retrieved fromreq
. If only one token is retrieved, it will be passed as a string, otherwise an array of the tokens retrieved will be passed. If no tokens are present inreq
theninfo
andtoken
will beundefined
. The tokens are obtained from either:- The password part of the
Authorization
header, split into multiple tokens using comma as a separator. -
Or from any
authz_token
query string parameters present.
- The password part of the
-
Go: TOC | AuthorizeJWT.prototype
AuthorizeJWT.prototype.authorize(authz_token, algorithms, cb)
Authorizes (or not) a JSON Web Token.
The token must pass all the tests made by jose
and
- If
config.max_token_expiry
was passed tomodule.exports
then the token must expire sooner thanconfig.max_token_expiry
seconds in the future.
Parameters:
-
{String | Object} authz_token
The token to authorize.-
If
config.WEBAUTHN_MODE
was not passed truthy tomodule.exports
thenauthz_token
must be a JWT string.- The
iss
property in the token's payload is used to retrieve a public key fromAuthorizeJWT
's key store usingPubKeyStore.prototype_get_pub_key_by_issuer_id
. - If the retrieved value has a
pub_key
property then that is used as the public key otherwise the retrieved value itself is used.
- The
-
If
config.WEBAUTHN_MODE
was passed truthy tomodule.exports
thenauthz_token
must be a Web Authentication assertion. It must either be an object with the following properties or a string of the formissuer_id.id.clientDataJSON.authenticatorData.signature.userHandle
- i.e.issuer_id
and the properties ofcar.response
(both described below) separated by a period. In the latter case, the remainingopts
property is obtained by callingconfig.complete_webauthn_token
(seemodule.exports
).-
{String} issuer_id
This is used to retrieve aUser
fromAuthorizeJWT
's key store usingPubKeyStore.prototype_get_pub_key_by_issuer_id
.- If the retrieved value has a
user
property then that is used as the user otherwise the retrieved value itself is used.
- If the retrieved value has a
-
{(
PublicKeyCredentialRequestOptions
=>
PublicKeyCredentialRequestOptions
)}[]
[opts] Optional list of functions which are used to modify the login requirements when producingcar
. -
{
CredentialAssertionResponse
} car
The credential assertion result that was generated by the authenticator in the browser. It must contain an unsigned JWT in its client data.
-
-
-
{Array} algorithms
This is passed tojose
and specifies the algorithms expected to be used to signauthz_token
. If you passundefined
then all algorithms available on the public key are allowed. Note this parameter is ignored ifconfig.WEBAUTHN_MODE
was passed truthy tomodule.exports
. -
{Function} cb
Function called with the result of authorizing the token. It will receive the following arguments:-
{Object} err
If authorization fails for some reason (e.g. the token isn't valid) then details of the failure, otherwisenull
. -
{Object} payload
The JWT's payload. -
{String} uri
The permanent URI of the token's issuer. This is different to the issuer ID in the payload'siss
property (PubKeyStore
generates a different issuer ID each time a public key is stored, even for the same issuer). -
{String} rev
Revision string for the public key used to verify the token. You can use this to identify tokens that become invalid when a PubKeyStore.events.change event occurs for the same issuer but with a different revision string. -
{
Credential
} [credential]
Ifconfig.WEBAUTHN_MODE
was passed truthy tomodule.exports
then this contains the validated credential, plus the issuer ID in theissuer_id
property and theUser
in theuser
property.
-
Go: TOC | AuthorizeJWT.prototype
AuthorizeJWT.prototype.close(cb)
Closes the JW authorizer.
If you passed your own keystore
or webAuthn
to module.exports
, it will not be closed.
Parameters:
-
{Function} cb
Called when everything's closed. It will receive the following argument:-
{Object} err
If something failed to close, details of the error.
-
Go: TOC | AuthorizeJWT.prototype
—generated by apidox—