UNPKG

@backstage/backend-defaults

Version:

Backend defaults used by Backstage backend apps

115 lines (111 loc) 3.56 kB
'use strict'; var errors = require('@backstage/errors'); var pluginAuthNode = require('@backstage/plugin-auth-node'); var jose = require('jose'); var JwksClient = require('../JwksClient.cjs.js'); class UserTokenHandler { static create(options) { const jwksClient = new JwksClient.JwksClient(async () => { const url = await options.discovery.getBaseUrl("auth"); return new URL(`${url}/.well-known/jwks.json`); }); return new UserTokenHandler(jwksClient, options.logger); } jwksClient; logger; constructor(jwksClient, logger) { this.jwksClient = jwksClient; this.logger = logger; } async verifyToken(token) { const verifyOpts = this.#getTokenVerificationOptions(token); if (!verifyOpts) { return void 0; } await this.jwksClient.refreshKeyStore(token); const { payload } = await jose.jwtVerify( token, this.jwksClient.getKey, verifyOpts ).catch((e) => { this.logger.warn("Failed to verify incoming user token", e); throw new errors.AuthenticationError("Failed user token verification"); }); const userEntityRef = payload.sub; if (!userEntityRef) { throw new errors.AuthenticationError("No user sub found in token"); } return { userEntityRef }; } #getTokenVerificationOptions(token) { try { const { typ } = jose.decodeProtectedHeader(token); if (typ === pluginAuthNode.tokenTypes.user.typParam) { return { requiredClaims: ["iat", "exp", "sub"], typ: pluginAuthNode.tokenTypes.user.typParam }; } if (typ === pluginAuthNode.tokenTypes.limitedUser.typParam) { return { requiredClaims: ["iat", "exp", "sub"], typ: pluginAuthNode.tokenTypes.limitedUser.typParam }; } const { aud } = jose.decodeJwt(token); if (aud === pluginAuthNode.tokenTypes.user.audClaim) { return { audience: pluginAuthNode.tokenTypes.user.audClaim }; } } catch { } return void 0; } createLimitedUserToken(backstageToken) { const [headerRaw, payloadRaw] = backstageToken.split("."); const header = JSON.parse( new TextDecoder().decode(jose.base64url.decode(headerRaw)) ); const payload = JSON.parse( new TextDecoder().decode(jose.base64url.decode(payloadRaw)) ); const tokenType = header.typ; if (!tokenType || tokenType === pluginAuthNode.tokenTypes.limitedUser.typParam) { return { token: backstageToken, expiresAt: new Date(payload.exp * 1e3) }; } if (tokenType !== pluginAuthNode.tokenTypes.user.typParam) { throw new errors.AuthenticationError( "Failed to create limited user token, invalid token type" ); } const limitedUserToken = [ jose.base64url.encode( JSON.stringify({ typ: pluginAuthNode.tokenTypes.limitedUser.typParam, alg: header.alg, kid: header.kid }) ), jose.base64url.encode( JSON.stringify({ sub: payload.sub, iat: payload.iat, exp: payload.exp }) ), payload.uip ].join("."); return { token: limitedUserToken, expiresAt: new Date(payload.exp * 1e3) }; } isLimitedUserToken(token) { try { const { typ } = jose.decodeProtectedHeader(token); return typ === pluginAuthNode.tokenTypes.limitedUser.typParam; } catch { return false; } } } exports.UserTokenHandler = UserTokenHandler; //# sourceMappingURL=UserTokenHandler.cjs.js.map