@landmineaknpm2/fuga-maiores-distinctio v1.0.0
@landmineaknpm2/fuga-maiores-distinctio

Simple JSON Web Token authorization module for Node.js.
- Uses
joseto perform verification of tokens. - Uses
pub-keystoreto 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:@landmineaknpm2/fuga-maiores-distinctio: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 @landmineaknpm2/fuga-maiores-distinctioLicence
Test
grunt testCode Coverage
grunt coveragec8 results are available here.
Coveralls page is here.
Lint
grunt lintAPI
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} configConfigures the authorizer.configis passed down topub-keystore,joseandwebauthn4js. The following extra properties are supported:{Integer} [max_token_expiry]If set then all JSON Web Tokens must expire sooner thanmax_token_expiryseconds 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_MODEis 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_idandcarproperties (seeauthorizefor a description).{Function} cbCall this function when you have filled in the remaining properties. It takes the following arguments:{Object} errIf an error occurred then pass details of the error, otherwise passnull.{Object} webauthn_tokenThis should have the same properties aspartial_webauthn_tokenplus theoptsproperty, if required (seeauthorize). It's safe to modifypartial_webauthn_tokenand then pass it here.
{PubKeyStore} [keystore]If you have a pre-existingPubKeyStoreinstance, 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-existingWebAuthn4JSinstance, pass it here. The default is to make a new one by callingwebauthn4js.
{Function} cbFunction called with the result of creating the authorizer. It will receive the following arguments:{Object} errIf an error occurred then details of the error, otherwisenull.{AuthorizeJWT} authzTheAuthorizeJWTobject. As well asAuthorizeJWT's prototype methods, it has the following properties:{PubKeyStore} keystoreThePubKeyStoreobject 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]TheWebAuthn4JSobject being used to verify assertions, ifWEBAUTHN_MODEis truthy.
AuthorizeJWT.prototype.get_authz_data(req, cb)
Extracts JSON Web Tokens from a HTTP request.
Parameters:
{http.IncomingMessage} reqHTTP request object which should contain the tokens either in theAuthorizationheader (Basic or Bearer auth) or in theauthz_tokenquery string parameter.{Function} cbFunction called with the tokens obtained fromreq. TheAuthorizationheader is used in preference to the query string.cbwill receive the following arguments:{Object} errIf an error occurred then details of the error, otherwisenull.{String} infoExtra information retrieved fromreqalong with the tokens. This is either the username extracted from theAuthorizationheader or theauthz_infoquery string parameter.{String|Array} tokenThe 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 inreqtheninfoandtokenwill beundefined. The tokens are obtained from either:- The password part of the
Authorizationheader, split into multiple tokens using comma as a separator. - Or from any
authz_tokenquery 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_expirywas passed tomodule.exportsthen the token must expire sooner thanconfig.max_token_expiryseconds in the future.
Parameters:
{String | Object} authz_tokenThe token to authorize.
If
config.WEBAUTHN_MODEwas not passed truthy tomodule.exportsthenauthz_tokenmust be a JWT string.- The
issproperty 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_keyproperty then that is used as the public key otherwise the retrieved value itself is used.
- The
If
config.WEBAUTHN_MODEwas passed truthy tomodule.exportsthenauthz_tokenmust 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_idand the properties ofcar.response(both described below) separated by a period. In the latter case, the remainingoptsproperty is obtained by callingconfig.complete_webauthn_token(seemodule.exports).{String} issuer_idThis is used to retrieve aUserfromAuthorizeJWT's key store usingPubKeyStore.prototype_get_pub_key_by_issuer_id.- If the retrieved value has a
userproperty 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} carThe credential assertion result that was generated by the authenticator in the browser. It must contain an unsigned JWT in its client data.
{Array} algorithmsThis is passed tojoseand specifies the algorithms expected to be used to signauthz_token. If you passundefinedthen all algorithms available on the public key are allowed. Note this parameter is ignored ifconfig.WEBAUTHN_MODEwas passed truthy tomodule.exports.{Function} cbFunction called with the result of authorizing the token. It will receive the following arguments:{Object} errIf authorization fails for some reason (e.g. the token isn't valid) then details of the failure, otherwisenull.{Object} payloadThe JWT's payload.{String} uriThe permanent URI of the token's issuer. This is different to the issuer ID in the payload'sissproperty (PubKeyStoregenerates a different issuer ID each time a public key is stored, even for the same issuer).{String} revRevision 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_MODEwas passed truthy tomodule.exportsthen this contains the validated credential, plus the issuer ID in theissuer_idproperty and theUserin theuserproperty.
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} cbCalled when everything's closed. It will receive the following argument:{Object} errIf something failed to close, details of the error.
Go: TOC | AuthorizeJWT.prototype
—generated by apidox—
2 years ago