UNPKG

@kronos-integration/service-http

Version:
92 lines (79 loc) 2.35 kB
import { Interceptor } from "@kronos-integration/interceptor"; import { prepareAttributesDefinitions } from "pacc"; import { verifyJWT } from "./util.mjs"; import { TEXT_PLAIN } from "./constants.mjs"; /** * Only forward requests if a valid JWT token is present. */ export class CTXJWTVerifyInterceptor extends Interceptor { /** * @return {string} 'ctx-jwt-verify' */ static get name() { return "ctx-jwt-verify"; } static attributes = prepareAttributesDefinitions({ requiredEntitlements: { description: "entitlements to be present in the token" }, ...Interceptor.attributes }); //requiredEntitlements = new Set(); async receive(endpoint, next, ctx, ...args) { const token = tokenFromAuthorizationHeader(ctx.req.headers); if (token) { try { const key = endpoint.owner.jwt?.public; const decoded = await verifyJWT(token, key); if (this.requiredEntitlements) { const entitlements = new Set(decoded.entitlements); for (const e of this.requiredEntitlements) { if (!entitlements.has(e)) { reportError(ctx, 403, new Error("Insufficient entitlements")); return; } } } // ctx.state[tokenKey] = decoded; } catch (error) { reportError(ctx, 401, error); return; } return await next(ctx, ...args); } else { reportError(ctx, 401); } } } function tokenFromAuthorizationHeader(headers) { if (headers.authorization) { const parts = headers.authorization.split(" "); if (parts.length === 2) { const [scheme, credentials] = parts; if (/^Bearer$/i.test(scheme)) { return credentials; } } } } /** * Write WWW-Authenticate header. * * @param {*} ctx * @param {number} error code * @param {*} [error] * @param {string} [description] */ function reportError(ctx, code, error, description) { const entries = Object.entries({ error: error?.message || "Missing token", description }).filter(([name, value]) => value !== undefined); ctx.res.writeHead(code, { "WWW-Authenticate": "Bearer," + entries.map(([name, value]) => `${name}="${value}"`).join(","), ...TEXT_PLAIN }); ctx.res.end(entries.map(([name, value]) => `${name}: ${value}`).join("\n")); }