@equisoft/oauth2orize-jwt-to-bearer
Version:
JSON Web Token (JWT) Bearer Token Exchange Middleware for OAuth2orize.
116 lines (103 loc) • 3.83 kB
JavaScript
/**
* Module dependencies.
*/
const TokenError = require('oauth2orize/lib/errors/tokenerror');
const utils = require('./utils');
/**
* JWTs as Authorization Grants.
*
* This exchange middleware is used to by clients to request an access token by
* using a JSON Web Token (JWT) generated by the client and verified by a
* Public Key stored on the server.
*
* Callbacks:
*
* This middleware requires an `issue` callback, for which the function
* signature is as follows:
*
* function(client, scope, assertion, done) { ... }
*
* `client` is the authenticated client instance attempting to obtain an access
* token. `assertion` is the JWT encoded header and claim set. `done` is
* called to issue an access token:
*
* done(err, accessToken, refreshToken, params)
*
* `accessToken` is the access token that will be sent to the client.
* `refreshToken` is the refresh token that will be sent to the client. Any
* additional `params` will be included in the response. If an error occurs,
* `done` should be invoked with `err` set in idomatic Node.js fashion.
*
* Options:
*
* userProperty property of `req` which contains the authenticated client (default: 'user')
* scopeSeparator Character separating scopes (default: ' ')
*
*
* Examples:
*
* server.exchange('urn:ietf:params:oauth:grant-type:jwt-bearer', oauth2orize.exchange.jwtBearer(function(client, scope, assertion, done) {
*
* AccessToken.create(client, scope, function(err, accessToken) {
* if (err) { return done(err); }
* done(null, accessToken);
* });
* }));
*
* References:
* - [JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants](https://tools.ietf.org/html/rfc7523#section-2.1)
* - [JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7519)
*
*
* @param {Function} issue middleware callback function
* @param {Object} options userProperty (default: 'user'), scopeSeparator (default: ' ')
* @api public
* @returns {Function} return middleware function
*/
module.exports = (issue, options = {}) => {
if (!issue) throw new Error('OAuth 2.0 jwtBearer exchange middleware requires an issue function.');
const userProperty = options.userProperty || 'user';
const scopeSeparator = options.scopeSeparator || ' ';
return (req, res, next) => {
if (!req.body) {
return next(new Error('Request body not parsed. Use bodyParser middleware.'));
}
// The 'user' property of `req` holds the authenticated user. In the case
// of the token endpoint, the property will contain the OAuth 2.0 client.
const client = req[userProperty];
const { assertion } = req.body;
let { scope } = req.body;
if (!assertion) {
return next(new TokenError('missing assertion parameter', 'invalid_request'));
}
if (scope) {
if (typeof scope !== 'string') {
return next(new TokenError('Invalid parameter: scope must be a string', 'invalid_request'));
}
scope = scope.split(scopeSeparator);
} else {
scope = [];
}
function issued(err, accessToken, refreshToken = null, params = {}) {
if (err) {
return next(err);
}
if (!accessToken) {
return next(new TokenError('Invalid authorization code', 'invalid_grant'));
}
const tok = {};
tok.access_token = accessToken;
if (refreshToken !== null) {
tok.refresh_token = refreshToken;
}
utils.merge(tok, params);
tok.token_type = tok.token_type || 'Bearer';
const json = JSON.stringify(tok);
res.setHeader('Content-Type', 'application/json');
res.setHeader('Cache-Control', 'no-store');
res.setHeader('Pragma', 'no-cache');
res.end(json);
}
issue(client, scope, assertion, issued);
};
};