@backstage/backend-defaults
Version:
Backend defaults used by Backstage backend apps
115 lines (111 loc) • 3.56 kB
JavaScript
;
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