@sap/xssec
Version:
XS Advanced Container Security API for node.js
71 lines (61 loc) • 3.35 kB
JavaScript
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));
}