UNPKG

@sap/xssec

Version:

XS Advanced Container Security API for node.js

71 lines (61 loc) 3.35 kB
const Token = require('../token/Token'); const MissingJwtError = require("../error/validation/MissingJwtError"); const SecurityContext = require('./SecurityContext'); const { WrongAudienceError } = require('../error'); const { getLogger } = require('../util/logging'); const LOG = getLogger("createSecurityContext.js"); module.exports = createSecurityContext; /** * @typedef {import("../service/Service")} Service * @typedef {import("../util/Types").SecurityContextConfig} SecurityContextConfig */ /** * Tries to create a new security context by authenticating the user via the given service(s) based on a jwt token. * If the jwt is missing or cannot be validated, an error will be thrown. * The contextConfig must either contain a jwt token directly or a req object from whose Authorization header the jwt can be extracted as Bearer token. * @template {Service} T - The type of the service from which the security context is created * @param {T|T[]} services - The service or array of services to authenticate the user * @param {SecurityContextConfig} contextConfig - The configuration object containing the jwt token or req object * @returns {ReturnType<T['createSecurityContext']>} - A promise that resolves to the security context created by the target service * @throws {import('../error/XssecError')} - Error with a descriptive message and a suggested statusCode for the application response. The cause of the error can be checked via instanceof against the various XssecError subclasses. */ async function createSecurityContext(services, contextConfig) { contextConfig = { ...contextConfig }; SecurityContext.buildContextConfig(contextConfig); if (contextConfig.token == null && contextConfig.jwt == null) { throw new MissingJwtError(); } const token = contextConfig.token ?? new Token(contextConfig.jwt); LOG.debug(`Creating security context from JWT: ${token.jwt}`, { correlationId: contextConfig.correlationId }); // find target service of token let targetService = null; if (Array.isArray(services)) { contextConfig.services = services; if (services.length === 1) { targetService = services[0]; } else { targetService = findServiceForToken(services, token); if (targetService != null) { LOG.debug(`Selected ${targetService.constructor.name} with clientid ${targetService.credentials.clientid} based on token audience.`, { correlationId: contextConfig.correlationId }); } } } else { contextConfig.services = [services]; targetService = services; } if (targetService == null) { throw new WrongAudienceError(token, services, `The audiences of the token fit none of the supplied services.`); } // create context return targetService.createSecurityContext(token, contextConfig); } /** * Tries to find a service from the list for which the given token was issued based on logic implemented by individual subclasses of {@link Service}. * @param {Service[]} services * @param {Token} token * @returns {Service|undefined} */ function findServiceForToken(services, token) { // TODO: extend with heuristic to filter on XSUAA/IAS services to prevent false positive? return services.find(s => s.acceptsTokenAudience(token)); }