@infomaker/service-authorization-lib
Version:
IMID Service Authorization Library
161 lines (144 loc) • 5.17 kB
JavaScript
/* eslint-env node */
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