UNPKG

@infomaker/service-authorization-lib

Version:

IMID Service Authorization Library

161 lines (144 loc) 5.17 kB
/* eslint-env node */ 'use strict' const authorize = require('../../authorize') const log = require('@infomaker/json-log')(__filename) const authorizationTokenMissing = authorizationHeader => { return !authorizationHeader || !authorizationHeader.toLowerCase().startsWith('bearer') } /** * @module ExpressMiddleware */ /** * The type definition of the full auhtorization object with all parameters. * * Passed to the authorize function. * * @typedef {Object} FullAuthorizationParameters * @property {Function} [onPreAuth] - Function to run before authorize is called * @property {string|Function|Boolean} org - Organiztion to authorize against * @property {Array<AccessRule>} [accessRules] - Optional access rules to authorize against * @property {Boolean} [suppressLoginTrigger] - If true, do not redirect failed authorization to login */ /** * The type definition of the access rule. * * Passed to the authorize function within the <FullAuthorizationParameter> object as a list of access rules. * * * All properties are optional, but at least one must exist * @typedef {Object} AccessRule * @property {string|Function} [unit] - Unit that should match token * @property {string|Function} [permission] - Permission that should match token * @property {string|Function} [sub] - Subject that should match token * */ /** * Type defintion of the authorization mode. * * SERVICE_ADMIN_ENDPOINT - Authorization validates if you are a service admin and have a valid token. Either accessed or thrown out. * * OPEN_ENDPOINT - Authorization validates if you have a valid token and lets you through to the open endpoint. Either accessed or thrown out. * * * Either SERVICE_ADMIN_ENDPOINT or OPEN_ENDPOINT * @typedef {String} AuthorizationMode */ /** * ServiceAuthorizationMiddleware */ class ServiceAuthorizationMiddleware { /** * Create a ServiceAuthorizationMiddleware * @param {Object} options * @param {string} options.serviceTokenSignSecret - Secret to validate token signature against */ constructor(options) { this.serviceTokenSignSecret = options.serviceTokenSignSecret this.config = null } /** * Extract and authorize token using the provided auth params * @param {(FullAuthorizationParameters|AuthorizationMode)} authParams - Authorization parameters to pass to */ authorize(authParams) { return async (request, res, next) => { const reqId = request.get('x-imid-req-id') /* istanbul ignore next */ const imidToken = request.get('x-imid-token') || null this._setAuthOnRequest(request) try { const unverifiedServiceToken = this._extractTokenPayloadFromRequest(request) log.debug('SAL got request to authorize', { 'x-imid-req-id': reqId, serviceReqId: request.id, unverifiedServiceToken }) const result = await authorize({ authParams, unverifiedServiceToken: unverifiedServiceToken, imidToken, serviceTokenSignSecret: this.serviceTokenSignSecret, request }) log.debug('SAL authorize result', { 'x-imid-req-id': reqId, serviceReqId: request.id, result }) if (result.err) { if (result.err.internalData && result.err.internalData.serviceToken) { this._setAuthOnRequest(request, { serviceToken: result.err.internalData.serviceToken }) } return next(result.err) } this._setAuthOnRequest(request, result.credentials, result.artifacts) return next() } catch (err) { next(err) } } } /** * Set the authorization on the request object * @private * @param {Object} request - The request object * @param {Object} credentials - The credentials * @param {Object} artifacts - The artifacts */ _setAuthOnRequest(request, credentials=null, artifacts=null) { request.auth = request.auth || {} request.auth.credentials = credentials request.auth.artifacts = artifacts } /** * Extract the token payload from requst * @private * @param {Object} request - The request object */ _extractTokenPayloadFromRequest(request) { const authorizationHeader = request.get('authorization') if (authorizationTokenMissing(authorizationHeader)) { return null } return authorizationHeader.replace(/bearer /i, '') } /** * Error handler for errors thrown by ServiceAuthorizationMiddleware * * Will handle telling IMSG to redirect unauthorized requests, but will pass on * any other errors to next() * @param {Object} [err] - Express err * @param {Object} req - Express req * @param {Object} res - Express res * @param {Function} next - Express next */ static errorHandler(err, req, res, next) { if (err && err.internalData && err.internalData.loginRedirectOrg) { res.setHeader('x-imid-login-redirect-organization', err.internalData.loginRedirectOrg) res.status(err.httpCode).send({msg: err.publicMessage}) } else { next(err) } } } module.exports = ServiceAuthorizationMiddleware