@hyper.authio/bff 0.0.0-stage → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/claims.ts","../src/http.ts","../src/options.ts","../src/protection.ts","../src/remote-api.ts","../src/session-locks.ts","../src/session-store.ts","../src/bff.ts"],"names":["AuthioResourceAccessClaims","AuthioClaimTypes","resourceAccessSourceOf","parseResourceAccess","isAuthioTokenEncrypted","AuthioPrompts","AuthioConfigurationError","hkdfSync","createHmac","timingSafeEqual","randomBytes","createCipheriv","createDecipheriv","pipeline","Readable","systemClock","client","createAuthioClient","createAuthioTokenDecrypter","encryptionKeyFromFile","createHash","AuthioTokenTypes","AuthioProtocolError","AuthioOAuthErrors","target","logout","AuthioError","AuthioDefaults"],"mappings":"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","file":"index.cjs","sourcesContent":["import {\n AuthioClaimTypes,\n AuthioResourceAccessClaims,\n isAuthioTokenEncrypted,\n parseResourceAccess,\n resourceAccessSourceOf,\n type AuthioTokenDecrypter,\n} from \"@hyper.authio/client\";\n\n/**\n * The claim types `/bff/user` adds to the person's claims.\n */\nexport const AuthioBffClaimTypes = {\n /** Seconds until the BFF session expires. */\n SessionExpiresIn: \"bff:session_expires_in\",\n /** The URL the SPA navigates to in order to log out; it carries the session's `sid`. */\n LogoutUrl: \"bff:logout_url\",\n /** One Authio role the person holds (flattened from the `*_resource_access` claims). */\n Role: \"role\",\n /** One permission the person holds, as `{resource}:{method}` (flattened from the `*_resource_access` claims). */\n Permission: \"permission\",\n} as const;\n\n/**\n * One entry of the `/bff/user` answer: a string value, or a number for `bff:session_expires_in`.\n */\nexport interface AuthioBffClaim {\n readonly type: string;\n readonly value: string | number;\n}\n\n/**\n * The JSON body of a BFF refusal, shaped like an OAuth error.\n */\nexport interface AuthioBffError {\n readonly error: string;\n readonly error_description?: string;\n}\n\nconst accessTokenClaimTypes = new Set<string>([\n ...Object.keys(AuthioResourceAccessClaims),\n AuthioClaimTypes.OrganizationId,\n AuthioClaimTypes.OrganizationAlias,\n AuthioClaimTypes.AccountId,\n]);\n\nconst protocolClaimTypes = new Set([\"nonce\", \"aud\", \"azp\", \"iss\", \"iat\", \"nbf\", \"exp\", \"at_hash\", \"c_hash\", \"ipaddr\", \"platf\", \"ver\"]);\n\n/**\n * The session's claims from a validated ID token: every claim except the protocol ones the OpenID Connect handler of\n * ASP.NET Core drops (`nonce`, `aud`, `azp`, `iss`, `iat`, `nbf`, `exp`, `at_hash`…).\n *\n * @internal\n */\nexport function toSessionClaims(idToken: Readonly<Record<string, unknown>> | undefined, fallbackSubject: string | undefined): Record<string, unknown> {\n const claims: Record<string, unknown> = {};\n\n for (const [type, value] of Object.entries(idToken ?? {})) if (!protocolClaimTypes.has(type) && value !== undefined && value !== null) claims[type] = value;\n\n if (claims.sub === undefined && fallbackSubject) claims.sub = fallbackSubject;\n\n return claims;\n}\n\n/**\n * Adds the UserInfo claims the session does not carry yet.\n *\n * @internal\n */\nexport function mergeUserInfo(claims: Record<string, unknown>, userInfo: Readonly<Record<string, unknown>>): void {\n for (const [type, value] of Object.entries(userInfo)) if (claims[type] === undefined && value !== undefined && value !== null) claims[type] = value;\n}\n\n/**\n * Replaces the claims only the access token carries (the seven `*_resource_access`, `org_id`, `org_alias`,\n * `account_id`) with the access token's. The token came straight from Authio's token endpoint over TLS, so its payload\n * is read without checking the signature; one encrypted to the client's key is opened with `decrypt`, and an opaque\n * one, or one the BFF cannot open, changes nothing.\n *\n * @internal\n */\nexport async function replaceAccessTokenClaims(claims: Record<string, unknown>, accessToken: string | undefined, decrypt: AuthioTokenDecrypter): Promise<void> {\n const payload = await readPayload(accessToken, decrypt);\n\n if (!payload) return;\n\n for (const type of Object.keys(claims)) if (accessTokenClaimTypes.has(type)) delete claims[type];\n for (const [type, value] of Object.entries(payload)) if (accessTokenClaimTypes.has(type) && value !== undefined && value !== null) claims[type] = value;\n}\n\n/**\n * The `sub` of an access token, when it is a readable JWT.\n *\n * @internal\n */\nexport async function readAccessTokenSubject(accessToken: string | undefined, decrypt: AuthioTokenDecrypter): Promise<string | undefined> {\n const subject = (await readPayload(accessToken, decrypt))?.sub;\n\n return typeof subject === \"string\" && subject ? subject : undefined;\n}\n\n/**\n * Builds the `/bff/user` answer: every claim of the session but the raw `*_resource_access` JSON (arrays as one entry\n * per element, other values as text), then one `role` per Authio role and one `permission` per grant, then\n * `bff:session_expires_in` and `bff:logout_url`.\n *\n * @internal\n */\nexport function toAuthioBffClaims(claims: Readonly<Record<string, unknown>>, sessionExpiresIn: number, logoutUrl: string): AuthioBffClaim[] {\n const entries: AuthioBffClaim[] = [];\n const seen = new Set<string>();\n const add = (type: string, value: string) => {\n const key = `${type}\\u0000${value}`;\n\n if (seen.has(key)) return;\n\n seen.add(key);\n entries.push({ type, value });\n };\n\n for (const [type, value] of Object.entries(claims)) {\n if (resourceAccessSourceOf(type)) continue;\n\n for (const item of Array.isArray(value) ? value : [value]) if (item !== undefined && item !== null) add(type, toText(item));\n }\n\n const access = parseResourceAccess(claims);\n\n for (const role of access.roles) add(AuthioBffClaimTypes.Role, role);\n for (const [resource, methods] of Object.entries(access.permissions)) for (const method of methods) add(AuthioBffClaimTypes.Permission, `${resource}:${method}`);\n\n entries.push({ type: AuthioBffClaimTypes.SessionExpiresIn, value: sessionExpiresIn });\n entries.push({ type: AuthioBffClaimTypes.LogoutUrl, value: logoutUrl });\n\n return entries;\n}\n\nasync function readPayload(token: string | undefined, decrypt: AuthioTokenDecrypter): Promise<Record<string, unknown> | undefined> {\n if (!token) return undefined;\n\n try {\n const signed = isAuthioTokenEncrypted(token) ? await decrypt(token) : token;\n const parts = signed.split(\".\");\n\n if (parts.length !== 3 || !parts[1]) return undefined;\n\n const payload: unknown = JSON.parse(Buffer.from(parts[1], \"base64url\").toString(\"utf8\"));\n\n return payload && typeof payload === \"object\" && !Array.isArray(payload) ? (payload as Record<string, unknown>) : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction toText(value: unknown): string {\n return typeof value === \"string\" ? value : typeof value === \"object\" ? JSON.stringify(value) : String(value);\n}\n","import type { Request, Response } from \"express\";\nimport type { AuthioBffCookieSameSite } from \"./options.js\";\n\n/**\n * Reads the request's cookies, first value per name, without needing `cookie-parser`.\n *\n * @internal\n */\nexport function readCookies(request: Request): Map<string, string> {\n const cookies = new Map<string, string>();\n\n for (const part of (request.headers.cookie ?? \"\").split(\";\")) {\n const separator = part.indexOf(\"=\");\n\n if (separator <= 0) continue;\n\n const name = part.slice(0, separator).trim();\n\n if (!name || cookies.has(name)) continue;\n\n let value = part.slice(separator + 1).trim();\n\n if (value.startsWith('\"') && value.endsWith('\"') && value.length >= 2) value = value.slice(1, -1);\n\n try {\n cookies.set(name, decodeURIComponent(value));\n } catch {\n cookies.set(name, value);\n }\n }\n\n return cookies;\n}\n\n/**\n * Adds a `Set-Cookie` header: `HttpOnly`, `Secure`, `Path=/`, no `Domain`; a session cookie unless `maxAge` (seconds)\n * is given, deleted when it is 0.\n *\n * @internal\n */\nexport function setCookie(response: Response, name: string, value: string, sameSite: AuthioBffCookieSameSite, maxAge?: number): void {\n if (response.headersSent) return;\n\n const attributes = [\n `${name}=${encodeURIComponent(value)}`,\n \"Path=/\",\n ...(maxAge === undefined ? [] : [`Max-Age=${Math.max(0, Math.floor(maxAge))}`]),\n ...(maxAge === 0 ? [\"Expires=Thu, 01 Jan 1970 00:00:00 GMT\"] : []),\n \"HttpOnly\",\n \"Secure\",\n `SameSite=${sameSite === \"strict\" ? \"Strict\" : \"Lax\"}`,\n ];\n\n response.append(\"Set-Cookie\", attributes.join(\"; \"));\n}\n\n/**\n * Answers with a JSON error (`{ error, error_description }`) and `Cache-Control: no-store`, unless the response\n * already started.\n *\n * @internal\n */\nexport function sendError(response: Response, status: number, error: string, description?: string): void {\n if (response.headersSent) return;\n\n response.status(status).set(\"Cache-Control\", \"no-store\").json(description ? { error, error_description: description } : { error });\n}\n\n/**\n * Whether the request carries the anti-forgery header exactly once, with the expected value.\n *\n * @internal\n */\nexport function hasAntiForgeryHeader(request: Request, name: string, value: string): boolean {\n const lower = name.toLowerCase();\n let count = 0;\n let matched = false;\n\n for (let index = 0; index < request.rawHeaders.length; index += 2) {\n if (request.rawHeaders[index]?.toLowerCase() !== lower) continue;\n\n count += 1;\n matched = request.rawHeaders[index + 1] === value;\n }\n\n return count === 1 && matched;\n}\n\n/**\n * The query parameters of the request, first value per name, read from the URL rather than Express's parser.\n *\n * @internal\n */\nexport function readQuery(request: Request): URLSearchParams {\n return new URL(request.originalUrl || request.url, \"http://localhost\").searchParams;\n}\n\n/**\n * The public origin and mount path of the request: `{protocol}://{host}{baseUrl}`. Honours `trust proxy` for the\n * protocol.\n *\n * @internal\n */\nexport function requestBase(request: Request): string {\n return `${request.protocol}://${request.get(\"host\") ?? \"localhost\"}${request.baseUrl ?? \"\"}`;\n}\n\n/**\n * Appends a query parameter to a local URL.\n *\n * @internal\n */\nexport function withQueryParameter(url: string, name: string, value: string): string {\n const hash = url.indexOf(\"#\");\n const base = hash >= 0 ? url.slice(0, hash) : url;\n const fragment = hash >= 0 ? url.slice(hash) : \"\";\n\n return `${base}${base.includes(\"?\") ? \"&\" : \"?\"}${encodeURIComponent(name)}=${encodeURIComponent(value)}${fragment}`;\n}\n\n/**\n * Reads a form field of a urlencoded body, whether a body parser already ran or not.\n *\n * @internal\n */\nexport async function readFormField(request: Request, field: string, maxBytes = 64 * 1024): Promise<string | undefined> {\n const parsed = (request as Request & { body?: unknown }).body;\n\n if (parsed && typeof parsed === \"object\" && !Buffer.isBuffer(parsed)) {\n const value = (parsed as Record<string, unknown>)[field];\n\n if (typeof value === \"string\") return value;\n }\n\n if (!(request.headers[\"content-type\"] ?? \"\").toLowerCase().startsWith(\"application/x-www-form-urlencoded\")) return undefined;\n\n const text = Buffer.isBuffer(parsed) ? parsed.toString(\"utf8\") : typeof parsed === \"string\" ? parsed : request.readableEnded ? undefined : await readText(request, maxBytes);\n\n return text === undefined ? undefined : (new URLSearchParams(text).get(field) ?? undefined);\n}\n\nfunction readText(request: Request, maxBytes: number): Promise<string | undefined> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let size = 0;\n let done = false;\n\n request.on(\"data\", (chunk: Buffer) => {\n if (done) return;\n\n size += chunk.byteLength;\n\n if (size > maxBytes) {\n done = true;\n request.pause();\n resolve(undefined);\n return;\n }\n\n chunks.push(chunk);\n });\n request.on(\"end\", () => {\n if (!done) resolve(Buffer.concat(chunks).toString(\"utf8\"));\n });\n request.on(\"error\", reject);\n });\n}\n","import { AuthioConfigurationError, AuthioPrompts, type AuthioClient, type AuthioClientOptions, type AuthioPrompt } from \"@hyper.authio/client\";\nimport type { AuthioBffSessionStore } from \"./session-store.js\";\n\n/**\n * Default names and values of the Authio BFF, the same as `Authio.AspNetCore.Bff`'s.\n */\nexport const AuthioBffDefaults = {\n /** The session cookie name. `__Host-` makes browsers refuse it unless it is `Secure`, has `Path=/` and no `Domain`. */\n CookieName: \"__Host-authio\",\n /** The route prefix of the BFF endpoints. */\n EndpointPrefix: \"/bff\",\n /** The anti-forgery header the SPA sends on every API call. */\n AntiForgeryHeaderName: \"X-CSRF\",\n /** The value the anti-forgery header carries. */\n AntiForgeryHeaderValue: \"1\",\n /** The redirect path Authio sends the person back to after sign-in. */\n CallbackPath: \"/signin-oidc\",\n /** The redirect path Authio sends the person back to after sign-out. */\n SignedOutCallbackPath: \"/signout-callback-oidc\",\n /** The `source` of the message the silent-login page posts to its parent window. */\n SilentLoginMessageSource: \"authio-bff-silent-login\",\n /** The prefix of the keys sessions are stored under. */\n SessionKeyPrefix: \"authio-bff:\",\n /** Seconds a session lasts without activity (8 hours). */\n SessionLifetime: 8 * 60 * 60,\n /** Seconds between two checks of the Authio session (5 minutes). */\n SessionValidationInterval: 5 * 60,\n /** Seconds a sign-in may take between the login request and the callback (15 minutes). */\n LoginLifetime: 15 * 60,\n /** The scopes asked for at sign-in; `offline_access` gives the BFF a refresh token. */\n Scopes: Object.freeze([\"openid\", \"profile\", \"email\", \"offline_access\"]) as readonly string[],\n} as const;\n\n/**\n * How the BFF checks that the person's Authio session is still alive: `none`, or `introspection` of the session's\n * refresh token (its access token when there is none) every `sessionValidationInterval`.\n */\nexport type AuthioBffSessionValidation = \"none\" | \"introspection\";\n\n/**\n * The `SameSite` mode of the session cookie.\n */\nexport type AuthioBffCookieSameSite = \"lax\" | \"strict\";\n\n/**\n * Settings of the Authio BFF. The client itself (`issuer` or `authority` + `realm`, `clientId`, `clientSecret`,\n * `clientAuthentication`, `clientPrivateKey`, `clientEncryptionKey`, `tokenRefreshSkew`…) takes the names of\n * `conformance/options.md`; the BFF's own settings are those of `Authio.AspNetCore.Bff` (`Authio:Bff`).\n */\nexport interface AuthioBffOptions extends Partial<AuthioClientOptions> {\n /**\n * An Authio client to use instead of creating one from the connection and credential options above. The BFF still\n * reads `tokenRefreshSkew`, `clientEncryptionKey`, `redirectUri` and `postLogoutRedirectUri` from these options.\n */\n client?: AuthioClient;\n /**\n * Secrets the session cookie is signed with and the stored sessions and sign-in cookies are encrypted with, at least\n * 32 characters each. The first one signs and encrypts; every one is accepted, so a new secret can be put first and\n * the old one removed once the sessions it protects have expired.\n */\n cookieSecret: string | readonly string[];\n /** Where sessions live. Defaults to an in-memory store; give a shared one (Redis…) for several instances. */\n store?: AuthioBffSessionStore;\n /** The route prefix of the BFF endpoints. Defaults to `/bff`. */\n endpointPrefix?: string;\n /** The scopes asked for at sign-in. Defaults to `openid profile email offline_access`; `openid` is always sent. */\n scopes?: readonly string[];\n /** Adds the `roles` scope, so the session carries the `*_resource_access` claims flattened on `/bff/user`. */\n requestRoles?: boolean;\n /** Calls Authio's UserInfo endpoint after sign-in and adds its claims to the session. Defaults to `false`. */\n getClaimsFromUserInfoEndpoint?: boolean;\n /** The `organization` sent on every sign-in unless the login request names one. */\n organization?: string;\n /**\n * The `resource` (RFC 8707) sent on every sign-in unless the login request names one: the session's access token is\n * then addressed to that API. Keep `offline_access`, so the session check introspects the refresh token.\n */\n resource?: string;\n /** The resources a login request may ask for with `?resource=`; any other is refused with `400`. */\n allowedResources?: readonly string[];\n /** The `prompt` sent on every sign-in unless the login request names one. */\n prompt?: AuthioPrompt;\n /** The `ui_locales` sent on every sign-in and sign-out unless the request names one. */\n uiLocales?: string;\n /** The `max_age` (seconds) sent on every sign-in unless the login request names one. */\n maxAge?: number;\n /** Extra authorize parameters sent on every sign-in. */\n additionalAuthorizationParameters?: Readonly<Record<string, string>>;\n /** Refuses an authorization response without `iss` (RFC 9207). Defaults to `true`; Authio always sends it. */\n requireIssuerParameter?: boolean;\n /** The redirect path registered at Authio for sign-in. Defaults to `/signin-oidc`. */\n callbackPath?: string;\n /** The post-logout redirect path registered at Authio. Defaults to `/signout-callback-oidc`. */\n signedOutCallbackPath?: string;\n /** Where the browser lands after login or logout without a `returnUrl`. Must be local. Defaults to `/`. */\n defaultReturnUrl?: string;\n /** The session cookie name. Defaults to `__Host-authio`; it must start with `__Host-` or `__Secure-`. */\n cookieName?: string;\n /** The session cookie's `SameSite`: `lax` (default) or `strict`. */\n cookieSameSite?: AuthioBffCookieSameSite;\n /** Seconds a session lasts without activity. Defaults to 28800 (8 hours). */\n sessionLifetime?: number;\n /** Renews `sessionLifetime` on activity once half of it has passed. Defaults to `true`. */\n slidingExpiration?: boolean;\n /** How the Authio session is checked. Defaults to `introspection`. */\n sessionValidation?: AuthioBffSessionValidation;\n /** Seconds between two checks of the Authio session. Defaults to 300. */\n sessionValidationInterval?: number;\n /** Revokes the refresh token at Authio on logout. Defaults to `true`. */\n revokeRefreshTokenOnLogout?: boolean;\n /** The anti-forgery header API calls must carry. Defaults to `X-CSRF`. */\n antiForgeryHeaderName?: string;\n /** The value the anti-forgery header must carry. Defaults to `1`. */\n antiForgeryHeaderValue?: string;\n /** The prefix of the keys sessions are stored under. Defaults to `authio-bff:`. */\n sessionKeyPrefix?: string;\n /**\n * Serves `POST {endpointPrefix}/backchannel-logout`, which ends the sessions of the Authio session (`sid`) or person\n * (`sub`) a logout token names. Defaults to `true`; register that URL as the client's back-channel logout URI.\n */\n backChannelLogout?: boolean;\n /** Called with errors the BFF recovers from: a failed revocation, introspection or refresh, a refused logout token. */\n onError?: (error: unknown) => void;\n}\n\n/**\n * The BFF settings, resolved and validated.\n *\n * @internal\n */\nexport interface AuthioBffSettings {\n readonly cookieSecrets: readonly string[];\n readonly endpointPrefix: string;\n readonly scopes: readonly string[];\n readonly getClaimsFromUserInfoEndpoint: boolean;\n readonly organization: string | undefined;\n readonly resource: string | undefined;\n readonly allowedResources: readonly string[];\n readonly prompt: AuthioPrompt | undefined;\n readonly uiLocales: string | undefined;\n readonly maxAge: number | undefined;\n readonly additionalAuthorizationParameters: Readonly<Record<string, string>>;\n readonly requireIssuerParameter: boolean;\n readonly callbackPath: string;\n readonly signedOutCallbackPath: string;\n readonly defaultReturnUrl: string;\n readonly cookieName: string;\n readonly cookieSameSite: AuthioBffCookieSameSite;\n readonly sessionLifetimeMs: number;\n readonly slidingExpiration: boolean;\n readonly sessionValidation: AuthioBffSessionValidation;\n readonly sessionValidationIntervalMs: number;\n readonly revokeRefreshTokenOnLogout: boolean;\n readonly antiForgeryHeaderName: string;\n readonly antiForgeryHeaderValue: string;\n readonly sessionKeyPrefix: string;\n readonly backChannelLogout: boolean;\n readonly tokenRefreshSkewMs: number;\n readonly redirectUri: string | undefined;\n readonly postLogoutRedirectUri: string | undefined;\n readonly onError: (error: unknown) => void;\n}\n\n/**\n * Resolves the BFF settings, naming every problem at once.\n *\n * @throws AuthioConfigurationError listing what is wrong.\n * @internal\n */\nexport function resolveBffSettings(options: AuthioBffOptions): AuthioBffSettings {\n const problems: string[] = [];\n const cookieSecrets = (typeof options.cookieSecret === \"string\" ? [options.cookieSecret] : [...(options.cookieSecret ?? [])]).filter(\n (secret) => typeof secret === \"string\",\n );\n const settings: AuthioBffSettings = {\n cookieSecrets,\n endpointPrefix: `/${(options.endpointPrefix ?? AuthioBffDefaults.EndpointPrefix).replace(/^\\/+|\\/+$/g, \"\")}`,\n scopes: withOpenId([...(options.scopes ?? AuthioBffDefaults.Scopes), ...(options.requestRoles ? [\"roles\"] : [])]),\n getClaimsFromUserInfoEndpoint: options.getClaimsFromUserInfoEndpoint ?? false,\n organization: nonEmpty(options.organization),\n resource: nonEmpty(options.resource),\n allowedResources: [...(options.allowedResources ?? [])],\n prompt: options.prompt || undefined,\n uiLocales: nonEmpty(options.uiLocales),\n maxAge: options.maxAge,\n additionalAuthorizationParameters: { ...options.additionalAuthorizationParameters },\n requireIssuerParameter: options.requireIssuerParameter ?? true,\n callbackPath: options.callbackPath ?? AuthioBffDefaults.CallbackPath,\n signedOutCallbackPath: options.signedOutCallbackPath ?? AuthioBffDefaults.SignedOutCallbackPath,\n defaultReturnUrl: options.defaultReturnUrl ?? \"/\",\n cookieName: options.cookieName ?? AuthioBffDefaults.CookieName,\n cookieSameSite: options.cookieSameSite ?? \"lax\",\n sessionLifetimeMs: (options.sessionLifetime ?? AuthioBffDefaults.SessionLifetime) * 1000,\n slidingExpiration: options.slidingExpiration ?? true,\n sessionValidation: options.sessionValidation ?? \"introspection\",\n sessionValidationIntervalMs: (options.sessionValidationInterval ?? AuthioBffDefaults.SessionValidationInterval) * 1000,\n revokeRefreshTokenOnLogout: options.revokeRefreshTokenOnLogout ?? true,\n antiForgeryHeaderName: options.antiForgeryHeaderName ?? AuthioBffDefaults.AntiForgeryHeaderName,\n antiForgeryHeaderValue: options.antiForgeryHeaderValue ?? AuthioBffDefaults.AntiForgeryHeaderValue,\n sessionKeyPrefix: options.sessionKeyPrefix ?? AuthioBffDefaults.SessionKeyPrefix,\n backChannelLogout: options.backChannelLogout ?? true,\n tokenRefreshSkewMs: (options.tokenRefreshSkew ?? 60) * 1000,\n redirectUri: nonEmpty(options.redirectUri),\n postLogoutRedirectUri: nonEmpty(options.postLogoutRedirectUri),\n onError: options.onError ?? (() => undefined),\n };\n\n if (cookieSecrets.length === 0) problems.push(\"cookieSecret is required: the session cookie is signed with it.\");\n else if (cookieSecrets.some((secret) => secret.length < 32)) problems.push(\"Every cookieSecret must be at least 32 characters long.\");\n\n if (!settings.cookieName.trim()) problems.push(\"cookieName is required.\");\n else if (!settings.cookieName.startsWith(\"__Host-\") && !settings.cookieName.startsWith(\"__Secure-\"))\n problems.push(\"cookieName must start with __Host- (or __Secure-) so browsers enforce a Secure, host-only cookie.\");\n else if (!/^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/.test(settings.cookieName)) problems.push(\"cookieName holds characters a cookie name cannot.\");\n\n if (settings.cookieSameSite !== \"lax\" && settings.cookieSameSite !== \"strict\") problems.push(\"cookieSameSite must be lax or strict.\");\n if (!isPositive(settings.sessionLifetimeMs)) problems.push(\"sessionLifetime must be a positive number of seconds.\");\n\n if (settings.sessionValidation !== \"none\" && settings.sessionValidation !== \"introspection\")\n problems.push(\"sessionValidation must be none or introspection.\");\n else if (settings.sessionValidation === \"introspection\" && !isPositive(settings.sessionValidationIntervalMs))\n problems.push(\"sessionValidationInterval must be a positive number of seconds.\");\n\n if (!settings.antiForgeryHeaderName.trim() || !/^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/.test(settings.antiForgeryHeaderName))\n problems.push(\"antiForgeryHeaderName must be a header name.\");\n if (!settings.antiForgeryHeaderValue) problems.push(\"antiForgeryHeaderValue is required.\");\n if (!isLocalUrl(settings.defaultReturnUrl)) problems.push(\"defaultReturnUrl must be a local URL starting with a single '/'.\");\n if (!isLocalUrl(settings.callbackPath)) problems.push(\"callbackPath must start with '/'.\");\n if (!isLocalUrl(settings.signedOutCallbackPath)) problems.push(\"signedOutCallbackPath must start with '/'.\");\n if (settings.endpointPrefix === \"/\") problems.push(\"endpointPrefix cannot be empty.\");\n if (settings.prompt !== undefined && !AuthioPrompts.includes(settings.prompt)) problems.push(\"prompt must be none, login, select_account or consent.\");\n\n if (settings.maxAge !== undefined && (!Number.isInteger(settings.maxAge) || settings.maxAge < 0))\n problems.push(\"maxAge must be a non-negative whole number of seconds.\");\n\n if (!settings.sessionKeyPrefix.trim()) problems.push(\"sessionKeyPrefix is required.\");\n if (!Number.isFinite(settings.tokenRefreshSkewMs) || settings.tokenRefreshSkewMs < 0) problems.push(\"tokenRefreshSkew must be a non-negative number of seconds.\");\n\n if (problems.length > 0) throw new AuthioConfigurationError(`The Authio BFF options are not valid:\\n- ${problems.join(\"\\n- \")}`);\n\n return settings;\n}\n\n/**\n * Whether a URL is local to the application: it starts with a single `/` (not `//` nor `/\\`) and holds no control\n * characters, so redirecting to it cannot leave the site.\n */\nexport function isLocalUrl(url: string | undefined | null): url is string {\n if (typeof url !== \"string\" || !url || /[\\u0000-\\u001f\\u007f-\\u009f]/.test(url)) return false;\n\n return url[0] === \"/\" && (url.length === 1 || (url[1] !== \"/\" && url[1] !== \"\\\\\"));\n}\n\nfunction withOpenId(scopes: readonly string[]): readonly string[] {\n const distinct = [...new Set(scopes.map((scope) => scope.trim()).filter(Boolean))];\n\n return Object.freeze(distinct.includes(\"openid\") ? distinct : [\"openid\", ...distinct]);\n}\n\nfunction nonEmpty(value: string | undefined): string | undefined {\n return value?.trim() ? value : undefined;\n}\n\nfunction isPositive(value: number): boolean {\n return Number.isFinite(value) && value > 0;\n}\n","import { createCipheriv, createDecipheriv, createHmac, hkdfSync, randomBytes, timingSafeEqual } from \"node:crypto\";\n\n/**\n * What the BFF signs or encrypts, each with its own key derived from `cookieSecret`.\n *\n * @internal\n */\nexport type AuthioBffProtectionPurpose = \"session-cookie\" | \"session-data\" | \"login\" | \"logout-state\";\n\n/**\n * Signs and encrypts with keys derived (HKDF-SHA256) from the cookie secrets: the first secret protects, every one is\n * accepted when reading.\n *\n * @internal\n */\nexport interface AuthioBffProtector {\n /** `value.signature`, HMAC-SHA256. */\n sign(value: string): string;\n /** The value of a signed text, or `undefined` when no secret's signature matches. */\n unsign(signed: string): string | undefined;\n /** AES-256-GCM over the JSON of `value`, base64url. */\n seal(purpose: AuthioBffProtectionPurpose, value: unknown): string;\n /** The value sealed for `purpose`, or `undefined` when it cannot be opened. */\n open<T>(purpose: AuthioBffProtectionPurpose, sealed: string): T | undefined;\n}\n\n/**\n * Creates the protector.\n *\n * @internal\n */\nexport function createAuthioBffProtector(secrets: readonly string[]): AuthioBffProtector {\n const keys = new Map<AuthioBffProtectionPurpose, Buffer[]>();\n const keysFor = (purpose: AuthioBffProtectionPurpose) => {\n let derived = keys.get(purpose);\n\n if (!derived) {\n derived = secrets.map((secret) => Buffer.from(hkdfSync(\"sha256\", secret, \"authio-bff\", `authio-bff:${purpose}`, 32)));\n keys.set(purpose, derived);\n }\n\n return derived;\n };\n const mac = (key: Buffer, value: string) => createHmac(\"sha256\", key).update(value, \"utf8\").digest();\n\n return {\n sign(value) {\n return `${value}.${mac(keysFor(\"session-cookie\")[0]!, value).toString(\"base64url\")}`;\n },\n unsign(signed) {\n const dot = signed.lastIndexOf(\".\");\n\n if (dot <= 0) return undefined;\n\n const value = signed.slice(0, dot);\n const signature = Buffer.from(signed.slice(dot + 1), \"base64url\");\n\n for (const key of keysFor(\"session-cookie\")) {\n const expected = mac(key, value);\n\n if (signature.length === expected.length && timingSafeEqual(signature, expected)) return value;\n }\n\n return undefined;\n },\n seal(purpose, value) {\n const iv = randomBytes(12);\n const cipher = createCipheriv(\"aes-256-gcm\", keysFor(purpose)[0]!, iv);\n const ciphertext = Buffer.concat([cipher.update(JSON.stringify(value), \"utf8\"), cipher.final()]);\n\n return Buffer.concat([iv, ciphertext, cipher.getAuthTag()]).toString(\"base64url\");\n },\n open<T>(purpose: AuthioBffProtectionPurpose, sealed: string) {\n const bytes = Buffer.from(sealed, \"base64url\");\n\n if (bytes.length < 29) return undefined;\n\n for (const key of keysFor(purpose)) {\n try {\n const decipher = createDecipheriv(\"aes-256-gcm\", key, bytes.subarray(0, 12));\n\n decipher.setAuthTag(bytes.subarray(bytes.length - 16));\n\n const plaintext = Buffer.concat([decipher.update(bytes.subarray(12, bytes.length - 16)), decipher.final()]).toString(\"utf8\");\n\n return JSON.parse(plaintext) as T;\n } catch {\n continue;\n }\n }\n\n return undefined;\n },\n };\n}\n","import { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport type { ReadableStream as NodeReadableStream } from \"node:stream/web\";\nimport type { Request, Response } from \"express\";\nimport { AuthioConfigurationError } from \"@hyper.authio/client\";\nimport { sendError } from \"./http.js\";\n\n/**\n * Which token a remote API call is forwarded with, as `AuthioBffRemoteApiTokenMode` in .NET:\n *\n * - `user`: the person's access token (refreshed when needed); anonymous calls are refused with `401`.\n * - `userOrNone`: the person's token when signed in, otherwise no token.\n * - `clientCredentials`: a client-credentials token of the BFF itself, whoever is signed in.\n * - `userOrClientCredentials`: the person's token when signed in, otherwise a client-credentials token.\n * - `none`: no token (cookies and the anti-forgery header are still stripped).\n */\nexport type AuthioBffRemoteApiTokenMode = \"user\" | \"userOrNone\" | \"clientCredentials\" | \"userOrClientCredentials\" | \"none\";\n\n/**\n * Settings of one remote API.\n */\nexport interface AuthioBffRemoteApiOptions {\n /** Which token the calls are forwarded with. Defaults to `user`. */\n tokenMode?: AuthioBffRemoteApiTokenMode;\n /**\n * The resource (RFC 8707) the token must be issued for: a user token is exchanged for it (cached per session and\n * resource), a client-credentials token is asked for it.\n */\n resource?: string;\n /** The scopes the token must carry; for a user token, setting them triggers a token exchange (down-scoping). */\n scopes?: readonly string[];\n /** Requires the anti-forgery header on every call. Defaults to `true`; turn it off only for an anonymous API. */\n requireAntiForgery?: boolean;\n /** When the remote API answers `401` to a user call, checks the Authio session and ends the BFF session if it is gone. Defaults to `true`. */\n validateSessionOnUnauthorized?: boolean;\n /** The `fetch` the calls go through. Defaults to the global one. */\n fetch?: typeof fetch;\n}\n\n/**\n * A remote API, resolved.\n *\n * @internal\n */\nexport interface AuthioBffRemoteApi {\n readonly destination: string;\n readonly tokenMode: AuthioBffRemoteApiTokenMode;\n readonly resource: string | undefined;\n readonly scopes: readonly string[];\n readonly requireAntiForgery: boolean;\n readonly validateSessionOnUnauthorized: boolean;\n readonly fetch: typeof fetch;\n}\n\n/**\n * What forwarding needs from the BFF.\n *\n * @internal\n */\nexport interface AuthioBffRemoteApiContext {\n antiForgery(): boolean;\n readonly antiForgeryHeaderName: string;\n hasSession(): Promise<boolean>;\n userToken(): Promise<string | undefined>;\n clientToken(): Promise<string>;\n endIfInactive(): Promise<void>;\n}\n\nconst tokenModes: readonly AuthioBffRemoteApiTokenMode[] = [\"user\", \"userOrNone\", \"clientCredentials\", \"userOrClientCredentials\", \"none\"];\n\nconst hopByHop = new Set([\"connection\", \"keep-alive\", \"proxy-authenticate\", \"proxy-authorization\", \"proxy-connection\", \"te\", \"trailer\", \"transfer-encoding\", \"upgrade\"]);\n\nconst decodedEncodings = new Set([\"gzip\", \"x-gzip\", \"deflate\", \"br\"]);\n\n/**\n * Resolves and validates a remote API's settings.\n *\n * @throws AuthioConfigurationError when the destination is not an absolute HTTP(S) URL or the token mode is unknown.\n * @internal\n */\nexport function resolveRemoteApi(destination: string, options: AuthioBffRemoteApiOptions): AuthioBffRemoteApi {\n let url: URL;\n\n try {\n url = new URL(destination);\n } catch {\n throw new AuthioConfigurationError(`The remote API destination '${destination}' must be an absolute HTTP(S) URL.`);\n }\n\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\")\n throw new AuthioConfigurationError(`The remote API destination '${destination}' must be an absolute HTTP(S) URL.`);\n\n const tokenMode = options.tokenMode ?? \"user\";\n\n if (!tokenModes.includes(tokenMode)) throw new AuthioConfigurationError(`The remote API tokenMode must be one of ${tokenModes.join(\", \")}.`);\n\n const fetchImplementation = options.fetch ?? globalThis.fetch;\n\n if (typeof fetchImplementation !== \"function\") throw new AuthioConfigurationError(\"No fetch implementation is available: pass fetch to remoteApi.\");\n\n return {\n destination: destination.replace(/\\/+$/, \"\"),\n tokenMode,\n resource: options.resource?.trim() || undefined,\n scopes: [...(options.scopes ?? [])],\n requireAntiForgery: options.requireAntiForgery ?? true,\n validateSessionOnUnauthorized: options.validateSessionOnUnauthorized ?? true,\n fetch: fetchImplementation,\n };\n}\n\n/**\n * Forwards one call: enforces the anti-forgery header, obtains the token, maps the path below the mount point onto\n * the destination, strips the browser's cookies, the anti-forgery header and any `Authorization`, drops upstream\n * `Set-Cookie`, and after an upstream `401` to a user call checks the Authio session. An unreachable API answers `502`.\n *\n * @internal\n */\nexport async function forwardToRemoteApi(request: Request, response: Response, remote: AuthioBffRemoteApi, context: AuthioBffRemoteApiContext): Promise<void> {\n if (remote.requireAntiForgery && !context.antiForgery())\n return sendError(response, 401, \"antiforgery_required\", `The ${context.antiForgeryHeaderName} header is required.`);\n\n const token = await tokenFor(remote, context);\n\n if (token.refused) return sendError(response, 401, \"unauthorized\", \"Sign in first.\");\n\n const headers = outgoingHeaders(request, context.antiForgeryHeaderName);\n\n if (token.accessToken) headers.set(\"authorization\", `Bearer ${token.accessToken}`);\n\n let upstream: globalThis.Response;\n\n try {\n upstream = await remote.fetch(remote.destination + remainingUrl(request), {\n method: request.method,\n headers,\n body: outgoingBody(request),\n redirect: \"manual\",\n duplex: \"half\",\n } as RequestInit);\n } catch {\n return sendError(response, 502, \"bad_gateway\", \"The remote API could not be reached.\");\n }\n\n if (upstream.status === 401 && token.isUserToken && remote.validateSessionOnUnauthorized) await context.endIfInactive();\n\n if (response.headersSent) return;\n\n const encoding = upstream.headers.get(\"content-encoding\")?.trim().toLowerCase();\n const decoded = encoding !== undefined && decodedEncodings.has(encoding);\n\n response.status(upstream.status);\n\n upstream.headers.forEach((value, name) => {\n if (hopByHop.has(name) || name === \"set-cookie\" || (decoded && (name === \"content-encoding\" || name === \"content-length\"))) return;\n\n response.setHeader(name, value);\n });\n\n if (!upstream.body || request.method === \"HEAD\") {\n response.end();\n return;\n }\n\n await pipeline(Readable.fromWeb(upstream.body as NodeReadableStream), response);\n}\n\nasync function tokenFor(remote: AuthioBffRemoteApi, context: AuthioBffRemoteApiContext): Promise<{ refused: boolean; accessToken?: string; isUserToken?: boolean }> {\n switch (remote.tokenMode) {\n case \"none\":\n return { refused: false };\n case \"clientCredentials\":\n return { refused: false, accessToken: await context.clientToken() };\n default: {\n const userToken = (await context.hasSession()) ? await context.userToken() : undefined;\n\n if (userToken) return { refused: false, accessToken: userToken, isUserToken: true };\n if (remote.tokenMode === \"userOrNone\") return { refused: false };\n if (remote.tokenMode === \"userOrClientCredentials\") return { refused: false, accessToken: await context.clientToken() };\n\n return { refused: true };\n }\n }\n}\n\nfunction outgoingHeaders(request: Request, antiForgeryHeaderName: string): Headers {\n const headers = new Headers();\n const skipped = new Set([\n ...hopByHop,\n \"host\",\n \"cookie\",\n \"authorization\",\n \"content-length\",\n antiForgeryHeaderName.toLowerCase(),\n ...(request.headers.connection ?? \"\").split(\",\").map((name) => name.trim().toLowerCase()),\n ]);\n\n for (const [name, value] of Object.entries(request.headers)) {\n if (value === undefined || skipped.has(name)) continue;\n\n for (const item of Array.isArray(value) ? value : [value]) headers.append(name, item);\n }\n\n headers.set(\"accept-encoding\", \"identity\");\n\n return headers;\n}\n\nfunction outgoingBody(request: Request): BodyInit | undefined {\n const declaresBody = request.headers[\"transfer-encoding\"] !== undefined || Number(request.headers[\"content-length\"] ?? 0) > 0;\n\n if (request.method === \"GET\" || request.method === \"HEAD\" || !declaresBody) return undefined;\n\n const parsed = (request as Request & { body?: unknown }).body;\n\n if (!request.readableEnded) return Readable.toWeb(request) as unknown as BodyInit;\n if (parsed === undefined || parsed === null) return undefined;\n if (Buffer.isBuffer(parsed)) return new Uint8Array(parsed);\n if (typeof parsed === \"string\") return parsed;\n if ((request.headers[\"content-type\"] ?? \"\").toLowerCase().startsWith(\"application/x-www-form-urlencoded\"))\n return new URLSearchParams(parsed as Record<string, string>).toString();\n\n return JSON.stringify(parsed);\n}\n\nfunction remainingUrl(request: Request): string {\n const url = request.url || \"/\";\n const originalPath = new URL(request.originalUrl || url, \"http://localhost\").pathname;\n\n if ((url === \"/\" || url.startsWith(\"/?\")) && !originalPath.endsWith(\"/\")) return url.slice(1);\n\n return url;\n}\n","/**\n * Hands out one asynchronous lock per session, so concurrent requests of the same person never refresh the same\n * refresh token twice (Authio rotates it and treats a late reuse as a replay that revokes the session). The lock is\n * local to the process, as `AuthioBffSessionLockProvider` is in .NET: with several instances, keep a person on one\n * instance or rely on Authio's short reuse leeway.\n *\n * @internal\n */\nexport class AuthioBffSessionLocks {\n private readonly tails = new Map<string, { promise: Promise<void>; references: number }>();\n\n /** The number of keys with a holder or a waiter. */\n get size(): number {\n return this.tails.size;\n }\n\n /**\n * Runs `work` once every earlier holder of `key` released it, and releases it afterwards, even when `work` throws.\n */\n async run<T>(key: string, work: () => Promise<T>): Promise<T> {\n const previous = this.tails.get(key);\n let release!: () => void;\n const turn = new Promise<void>((resolve) => {\n release = resolve;\n });\n const entry = { promise: (previous?.promise ?? Promise.resolve()).then(() => turn), references: (previous?.references ?? 0) + 1 };\n\n this.tails.set(key, entry);\n\n try {\n await previous?.promise;\n\n return await work();\n } finally {\n release();\n\n const current = this.tails.get(key);\n\n if (current) {\n current.references -= 1;\n if (current.references === 0) this.tails.delete(key);\n }\n }\n }\n}\n","import { systemClock, type AuthioClock } from \"@hyper.authio/client\";\n\n/**\n * A session as the store keeps it. `data` is encrypted by the BFF (AES-256-GCM, from `cookieSecret`): the store never\n * sees the tokens. `sessionId` and `subject` are kept in clear so back-channel logout can find the sessions to end.\n */\nexport interface AuthioBffStoredSession {\n /** The store key: `sessionKeyPrefix` followed by 64 random hex characters. The cookie carries the part after the prefix. */\n readonly key: string;\n /** The Authio session (`sid` of the ID token), when Authio named one. */\n readonly sessionId: string | undefined;\n /** The person (`sub`). */\n readonly subject: string | undefined;\n /** When the session expires, in milliseconds since the epoch; a store may drop it from then on. */\n readonly expiresAt: number;\n /** The encrypted session. */\n readonly data: string;\n}\n\n/**\n * Where the BFF keeps its sessions. The default is in memory (one process); implement this over Redis or a database for\n * several instances, indexing each session by `sessionId` and `subject` so back-channel logout can end them.\n * Every method may return a value or a promise.\n */\nexport interface AuthioBffSessionStore {\n /** Reads a session; `undefined` when it is unknown or expired. */\n get(key: string): AuthioBffStoredSession | undefined | Promise<AuthioBffStoredSession | undefined>;\n /** Creates or replaces a session, kept until `expiresAt`. */\n set(session: AuthioBffStoredSession): void | Promise<void>;\n /** Deletes a session. */\n delete(key: string): void | Promise<void>;\n /** Deletes every session of an Authio session (`sid`). */\n deleteBySessionId(sessionId: string): void | Promise<void>;\n /** Deletes every session of a person (`sub`). */\n deleteBySubject(subject: string): void | Promise<void>;\n}\n\n/**\n * Options of the in-memory store.\n */\nexport interface AuthioBffMemoryStoreOptions {\n /** The clock expiry is measured with. Defaults to the system clock. */\n clock?: AuthioClock;\n}\n\n/**\n * An in-memory session store, indexed by `sessionId` and `subject`, that drops expired sessions. Sessions live in one\n * process and are lost on restart: use a shared store for several instances.\n */\nexport interface AuthioBffMemoryStore extends AuthioBffSessionStore {\n /** The number of sessions held, expired ones included until they are swept. */\n readonly size: number;\n get(key: string): AuthioBffStoredSession | undefined;\n set(session: AuthioBffStoredSession): void;\n delete(key: string): void;\n deleteBySessionId(sessionId: string): void;\n deleteBySubject(subject: string): void;\n}\n\n/**\n * Creates the in-memory session store.\n */\nexport function createAuthioBffMemoryStore(options: AuthioBffMemoryStoreOptions = {}): AuthioBffMemoryStore {\n const clock = options.clock ?? systemClock;\n const sessions = new Map<string, AuthioBffStoredSession>();\n const bySessionId = new Map<string, Set<string>>();\n const bySubject = new Map<string, Set<string>>();\n let nextSweep = 0;\n\n const index = (lookup: Map<string, Set<string>>, value: string | undefined, key: string) => {\n if (!value) return;\n\n const keys = lookup.get(value) ?? new Set<string>();\n\n keys.add(key);\n lookup.set(value, keys);\n };\n\n const unindex = (lookup: Map<string, Set<string>>, value: string | undefined, key: string) => {\n if (!value) return;\n\n const keys = lookup.get(value);\n\n keys?.delete(key);\n if (keys?.size === 0) lookup.delete(value);\n };\n\n const remove = (key: string) => {\n const session = sessions.get(key);\n\n if (!session) return;\n\n sessions.delete(key);\n unindex(bySessionId, session.sessionId, key);\n unindex(bySubject, session.subject, key);\n };\n\n const sweep = () => {\n const now = clock.now();\n\n if (now < nextSweep) return;\n\n nextSweep = now + 60_000;\n\n for (const [key, session] of sessions) if (session.expiresAt <= now) remove(key);\n };\n\n return {\n get size() {\n return sessions.size;\n },\n get(key) {\n const session = sessions.get(key);\n\n if (!session) return undefined;\n if (session.expiresAt > clock.now()) return session;\n\n remove(key);\n\n return undefined;\n },\n set(session) {\n sweep();\n remove(session.key);\n sessions.set(session.key, session);\n index(bySessionId, session.sessionId, session.key);\n index(bySubject, session.subject, session.key);\n },\n delete: remove,\n deleteBySessionId(sessionId) {\n for (const key of [...(bySessionId.get(sessionId) ?? [])]) remove(key);\n },\n deleteBySubject(subject) {\n for (const key of [...(bySubject.get(subject) ?? [])]) remove(key);\n },\n };\n}\n","import { createHash, randomBytes, timingSafeEqual } from \"node:crypto\";\nimport type { NextFunction, Request, RequestHandler, Response } from \"express\";\nimport {\n AuthioDefaults,\n AuthioError,\n AuthioOAuthErrors,\n AuthioPrompts,\n AuthioProtocolError,\n AuthioTokenTypes,\n createAuthioClient,\n createAuthioTokenDecrypter,\n encryptionKeyFromFile,\n type AuthioAuthorizationTransaction,\n type AuthioClient,\n type AuthioClientOptions,\n type AuthioPrompt,\n} from \"@hyper.authio/client\";\nimport { mergeUserInfo, readAccessTokenSubject, replaceAccessTokenClaims, toAuthioBffClaims, toSessionClaims } from \"./claims.js\";\nimport { hasAntiForgeryHeader, readCookies, readFormField, readQuery, requestBase, sendError, setCookie, withQueryParameter } from \"./http.js\";\nimport { AuthioBffDefaults, isLocalUrl, resolveBffSettings, type AuthioBffOptions } from \"./options.js\";\nimport { createAuthioBffProtector } from \"./protection.js\";\nimport { forwardToRemoteApi, resolveRemoteApi, type AuthioBffRemoteApiOptions } from \"./remote-api.js\";\nimport { AuthioBffSessionLocks } from \"./session-locks.js\";\nimport { createAuthioBffMemoryStore, type AuthioBffSessionStore } from \"./session-store.js\";\n\n/**\n * The signed-in session as the application sees it: who, which Authio session, until when. Never the tokens.\n */\nexport interface AuthioBffSessionInfo {\n /** The person (`sub`). */\n readonly subject: string | undefined;\n /** The Authio session (`sid`). */\n readonly sessionId: string | undefined;\n /** The session's claims: the ID token's, the access token's `*_resource_access` and organization, UserInfo's. */\n readonly claims: Readonly<Record<string, unknown>>;\n /** When the BFF session expires, in milliseconds since the epoch. */\n readonly expiresAt: number;\n}\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Express {\n interface Request {\n /** The Authio BFF session, once the BFF read it for this request; `undefined` when nobody is signed in. */\n authioBff?: AuthioBffSessionInfo;\n }\n }\n}\n\n/**\n * Options of `bff.api()`.\n */\nexport interface AuthioBffApiOptions {\n /** Requires the anti-forgery header. Defaults to `true`. */\n requireAntiForgery?: boolean;\n /** Lets anonymous calls through (with `req.authioBff` undefined) instead of answering `401`. Defaults to `false`. */\n optional?: boolean;\n}\n\n/**\n * What a user token is asked for: a resource (RFC 8707) and/or narrower scopes make the BFF exchange the session's\n * token (RFC 8693) and cache the result per session, resource and scopes.\n */\nexport interface AuthioBffUserTokenOptions {\n /** The resource the token must be issued for. */\n resource?: string;\n /** The scopes the token must carry. */\n scopes?: readonly string[];\n}\n\n/**\n * The Authio BFF: an Express middleware serving the BFF endpoints, plus the helpers the application's own routes use.\n */\nexport interface AuthioBff extends RequestHandler {\n /** The Authio client the BFF talks to Authio with. */\n readonly client: AuthioClient;\n /** Where the sessions live. */\n readonly store: AuthioBffSessionStore;\n /**\n * Marks a local API: requires the anti-forgery header (`401 antiforgery_required`) and a session (`401\n * unauthorized`), never redirects, and sets `req.authioBff`.\n */\n api(options?: AuthioBffApiOptions): RequestHandler;\n /**\n * Forwards the calls mounted under a path to a remote API with the token `tokenMode` asks for:\n * `app.use(\"/api/orders\", bff.remoteApi(\"https://orders.example.com\"))`. Mount it before any body parser.\n */\n remoteApi(destination: string, options?: AuthioBffRemoteApiOptions): RequestHandler;\n /** Reads the request's session (once per request), checking and renewing it as configured. */\n getSession(request: Request, response: Response): Promise<AuthioBffSessionInfo | undefined>;\n /**\n * The person's access token, refreshed `tokenRefreshSkew` before it expires (one refresh per session at a time), or\n * exchanged for `resource`/`scopes`. `undefined` when nobody is signed in or the session ended (a refused refresh\n * ends it).\n */\n getUserAccessToken(request: Request, response: Response, options?: AuthioBffUserTokenOptions): Promise<string | undefined>;\n /** A `fetch` that sends the person's access token (see `getUserAccessToken`), for calling APIs from a route. */\n userFetch(request: Request, response: Response, options?: AuthioBffUserTokenOptions & { fetch?: typeof fetch }): typeof fetch;\n /** Ends the request's session locally: deletes it from the store and clears the cookie. Authio is not told. */\n endSession(request: Request, response: Response): Promise<void>;\n}\n\ninterface SessionData {\n publicId: string;\n claims: Record<string, unknown>;\n accessToken?: string;\n accessTokenExpiresAt?: number;\n refreshToken?: string;\n idToken?: string;\n tokenType?: string;\n resource?: string;\n createdAt: number;\n validatedAt: number;\n}\n\ninterface LoadedSession {\n readonly key: string;\n readonly expiresAt: number;\n readonly data: SessionData;\n}\n\ninterface LoginState {\n transaction: AuthioAuthorizationTransaction;\n returnUrl: string;\n expiresAt: number;\n}\n\ninterface LoginParameters {\n returnUrl: string;\n prompt?: AuthioPrompt;\n loginHint?: string;\n uiLocales?: string;\n maxAge?: number;\n organization?: string;\n resource?: string;\n}\n\ninterface ExchangedToken {\n readonly sessionKey: string;\n readonly accessToken: string;\n readonly refreshAt: number;\n readonly expiresAt: number;\n}\n\nconst sessionSlot = Symbol.for(\"authio.bff.session\");\n\ntype RequestWithSession = Request & { [sessionSlot]?: Promise<LoadedSession | undefined> };\n\n/**\n * Creates the Authio BFF. Mount it with `app.use(bff)`: it serves `{endpointPrefix}/login`, `/logout`, `/user`,\n * `/silent-login`, `/silent-login-callback`, `/backchannel-logout`, the sign-in callback (`callbackPath`) and the\n * sign-out callback (`signedOutCallbackPath`), and reads the session of every other request that carries the cookie.\n *\n * @throws AuthioConfigurationError naming every invalid option.\n */\nexport function authioBff(options: AuthioBffOptions): AuthioBff {\n const settings = resolveBffSettings(options);\n const client = options.client ?? createAuthioClient(options as AuthioClientOptions);\n const clock = client.connection.clock;\n const store = options.store ?? createAuthioBffMemoryStore({ clock });\n const protector = createAuthioBffProtector(settings.cookieSecrets);\n const locks = new AuthioBffSessionLocks();\n const exchanges = new Map<string, ExchangedToken>();\n const decrypt = createAuthioTokenDecrypter(\n options.clientEncryptionKey ?? (options.clientEncryptionKeyPath ? encryptionKeyFromFile(options.clientEncryptionKeyPath) : undefined),\n );\n const prefix = settings.endpointPrefix;\n const loginLifetimeMs = AuthioBffDefaults.LoginLifetime * 1000;\n\n const loginCookieName = (state: string) => `${settings.cookieName}.login.${createHash(\"sha256\").update(state).digest(\"base64url\").slice(0, 16)}`;\n\n const toInfo = (session: LoadedSession): AuthioBffSessionInfo =>\n Object.freeze({\n subject: textOf(session.data.claims.sub),\n sessionId: textOf(session.data.claims.sid),\n claims: Object.freeze({ ...session.data.claims }),\n expiresAt: session.expiresAt,\n });\n\n const expose = (request: Request, session: LoadedSession | undefined) => {\n (request as RequestWithSession)[sessionSlot] = Promise.resolve(session);\n request.authioBff = session ? toInfo(session) : undefined;\n };\n\n const read = async (key: string): Promise<LoadedSession | undefined> => {\n const stored = await store.get(key);\n\n if (!stored || stored.expiresAt <= clock.now()) return undefined;\n\n const data = protector.open<SessionData>(\"session-data\", stored.data);\n\n return data ? { key, expiresAt: stored.expiresAt, data } : undefined;\n };\n\n const write = async (key: string, data: SessionData, expiresAt: number): Promise<LoadedSession> => {\n await store.set({ key, sessionId: textOf(data.claims.sid), subject: textOf(data.claims.sub), expiresAt, data: protector.seal(\"session-data\", data) });\n\n return { key, expiresAt, data };\n };\n\n const update = (key: string, change: (session: LoadedSession) => { data: SessionData; expiresAt: number }) =>\n locks.run(key, async () => {\n const current = await read(key);\n\n if (!current) return undefined;\n\n const next = change(current);\n\n return write(key, next.data, next.expiresAt);\n });\n\n const dropExchanges = (key: string) => {\n for (const [cacheKey, entry] of exchanges) if (entry.sessionKey === key) exchanges.delete(cacheKey);\n };\n\n const clearCookie = (response: Response) => setCookie(response, settings.cookieName, \"\", settings.cookieSameSite, 0);\n\n const endSessionByKey = async (request: Request, response: Response, key: string) => {\n await store.delete(key);\n dropExchanges(key);\n clearCookie(response);\n expose(request, undefined);\n };\n\n const report = (error: unknown) => {\n try {\n settings.onError(error);\n } catch {\n return;\n }\n };\n\n const isActive = async (data: SessionData): Promise<boolean | undefined> => {\n const token = data.refreshToken ?? data.accessToken;\n\n if (!token) return undefined;\n\n try {\n const result = await client.introspect(token, { tokenTypeHint: data.refreshToken ? AuthioTokenTypes.RefreshToken : AuthioTokenTypes.AccessToken });\n\n return result.active === true;\n } catch (error) {\n report(error);\n\n return undefined;\n }\n };\n\n const maintain = async (request: Request, response: Response, session: LoadedSession): Promise<LoadedSession | undefined> => {\n const now = clock.now();\n const validate = settings.sessionValidation === \"introspection\" && now - session.data.validatedAt >= settings.sessionValidationIntervalMs;\n const slide = settings.slidingExpiration && session.expiresAt - now < settings.sessionLifetimeMs / 2;\n let validated = false;\n\n if (validate) {\n const active = await isActive(session.data);\n\n if (active === false) {\n await endSessionByKey(request, response, session.key);\n return undefined;\n }\n\n validated = active === true;\n }\n\n if (!validated && !slide) return session;\n\n const renewed = await update(session.key, (current) => ({\n data: validated ? { ...current.data, validatedAt: now } : current.data,\n expiresAt: slide ? now + settings.sessionLifetimeMs : current.expiresAt,\n }));\n\n return renewed;\n };\n\n const loadSession = (request: Request, response: Response): Promise<LoadedSession | undefined> => {\n const holder = request as RequestWithSession;\n const cached = holder[sessionSlot];\n\n if (cached) return cached;\n\n const loading = (async () => {\n const cookie = readCookies(request).get(settings.cookieName);\n\n if (!cookie) return undefined;\n\n const id = protector.unsign(cookie);\n const found = id ? await read(settings.sessionKeyPrefix + id) : undefined;\n const session = found ? await maintain(request, response, found) : undefined;\n\n if (!session) {\n clearCookie(response);\n request.authioBff = undefined;\n return undefined;\n }\n\n request.authioBff = toInfo(session);\n\n return session;\n })();\n\n holder[sessionSlot] = loading;\n\n return loading;\n };\n\n const isFresh = (data: SessionData, skewMs: number) =>\n Boolean(data.accessToken) && (data.accessTokenExpiresAt === undefined || clock.now() < data.accessTokenExpiresAt - skewMs);\n\n const refresh = async (request: Request, response: Response, session: LoadedSession): Promise<string | undefined> => {\n const data = { ...session.data, claims: { ...session.data.claims } };\n const unexpired = isFresh(data, 0) ? data.accessToken : undefined;\n\n if (!data.refreshToken) return unexpired;\n\n try {\n const issuedAt = clock.now();\n const answer = await client.tokens.refresh({ refreshToken: data.refreshToken });\n\n data.accessToken = answer.access_token;\n data.accessTokenExpiresAt = issuedAt + lifetimeOf(answer.expires_in);\n data.tokenType = answer.token_type ?? data.tokenType;\n if (answer.refresh_token) data.refreshToken = answer.refresh_token;\n if (answer.id_token) data.idToken = answer.id_token;\n\n await replaceAccessTokenClaims(data.claims, answer.access_token, decrypt);\n\n if (!(await store.get(session.key))) {\n await endSessionByKey(request, response, session.key);\n return undefined;\n }\n\n expose(request, await write(session.key, data, session.expiresAt));\n\n return answer.access_token;\n } catch (error) {\n report(error);\n\n if (error instanceof AuthioProtocolError && error.error === AuthioOAuthErrors.InvalidGrant) {\n await endSessionByKey(request, response, session.key);\n return undefined;\n }\n\n return unexpired;\n }\n };\n\n const sessionAccessToken = async (request: Request, response: Response): Promise<string | undefined> => {\n const session = await loadSession(request, response);\n\n if (!session) return undefined;\n if (isFresh(session.data, settings.tokenRefreshSkewMs)) return session.data.accessToken;\n\n return locks.run(session.key, async () => {\n const current = await read(session.key);\n\n if (!current) {\n await endSessionByKey(request, response, session.key);\n return undefined;\n }\n\n if (isFresh(current.data, settings.tokenRefreshSkewMs)) {\n expose(request, current);\n return current.data.accessToken;\n }\n\n return refresh(request, response, current);\n });\n };\n\n const getUserAccessToken: AuthioBff[\"getUserAccessToken\"] = async (request, response, tokenOptions = {}) => {\n const scopes = [...new Set((tokenOptions.scopes ?? []).map((scope) => scope.trim()).filter(Boolean))].sort();\n const resource = tokenOptions.resource?.trim() || undefined;\n const subjectToken = await sessionAccessToken(request, response);\n\n if (!subjectToken || (!resource && scopes.length === 0)) return subjectToken;\n\n const session = await loadSession(request, response);\n\n if (!session) return undefined;\n\n const cacheKey = `${session.key}|${resource ?? \"\"}|${scopes.join(\" \")}`;\n const cached = exchanges.get(cacheKey);\n\n if (cached && clock.now() < cached.refreshAt) return cached.accessToken;\n\n return locks.run(cacheKey, async () => {\n const again = exchanges.get(cacheKey);\n\n if (again && clock.now() < again.refreshAt) return again.accessToken;\n\n const issuedAt = clock.now();\n const answer = await client.tokens.exchange({ subjectToken, scopes: scopes.length > 0 ? scopes : undefined, resource });\n const lifetime = lifetimeOf(answer.expires_in);\n\n for (const [key, entry] of exchanges) if (entry.expiresAt <= issuedAt) exchanges.delete(key);\n\n exchanges.set(cacheKey, {\n sessionKey: session.key,\n accessToken: answer.access_token,\n refreshAt: issuedAt + lifetime - Math.min(settings.tokenRefreshSkewMs, lifetime / 2),\n expiresAt: issuedAt + lifetime,\n });\n\n return answer.access_token;\n });\n };\n\n const endIfInactive = async (request: Request, response: Response) => {\n const session = await loadSession(request, response);\n\n if (session && (await isActive(session.data)) === false) await endSessionByKey(request, response, session.key);\n };\n\n const antiForgery = (request: Request) => hasAntiForgeryHeader(request, settings.antiForgeryHeaderName, settings.antiForgeryHeaderValue);\n\n const antiForgeryRequired = (response: Response) =>\n sendError(response, 401, \"antiforgery_required\", `The ${settings.antiForgeryHeaderName} header is required.`);\n\n const startLogin = async (request: Request, response: Response, parameters: LoginParameters) => {\n const { url, transaction } = await client.createAuthorizationUrl({\n redirectUri: settings.redirectUri ?? requestBase(request) + settings.callbackPath,\n scopes: settings.scopes,\n resource: parameters.resource ?? settings.resource,\n organization: parameters.organization ?? settings.organization,\n prompt: parameters.prompt ?? settings.prompt,\n loginHint: parameters.loginHint,\n uiLocales: parameters.uiLocales ?? settings.uiLocales,\n maxAge: parameters.maxAge ?? settings.maxAge,\n extraParameters: settings.additionalAuthorizationParameters,\n });\n const state: LoginState = { transaction, returnUrl: parameters.returnUrl, expiresAt: clock.now() + loginLifetimeMs };\n\n setCookie(response, loginCookieName(transaction.state), protector.seal(\"login\", state), \"lax\", AuthioBffDefaults.LoginLifetime);\n response.set(\"Cache-Control\", \"no-store\").redirect(302, url);\n };\n\n const login = async (request: Request, response: Response) => {\n const query = readQuery(request);\n const returnUrl = query.get(\"returnUrl\") ?? \"\";\n const prompt = query.get(\"prompt\") ?? \"\";\n const maxAge = query.get(\"max_age\") ?? \"\";\n const resource = query.get(\"resource\") ?? \"\";\n\n if (returnUrl && !isLocalUrl(returnUrl)) return sendError(response, 400, \"invalid_request\", \"returnUrl must be a local URL.\");\n if (prompt && !(AuthioPrompts as readonly string[]).includes(prompt))\n return sendError(response, 400, \"invalid_request\", \"prompt must be none, login, select_account or consent.\");\n if (maxAge && !/^\\d+$/.test(maxAge)) return sendError(response, 400, \"invalid_request\", \"max_age must be a number of seconds.\");\n if (resource && resource !== settings.resource && !settings.allowedResources.includes(resource))\n return sendError(response, 400, \"invalid_target\", \"The resource is not allowed by allowedResources.\");\n\n await startLogin(request, response, {\n returnUrl: returnUrl || settings.defaultReturnUrl,\n prompt: (prompt || undefined) as AuthioPrompt | undefined,\n loginHint: query.get(\"login_hint\") || undefined,\n uiLocales: query.get(\"ui_locales\") || undefined,\n maxAge: maxAge ? Number(maxAge) : undefined,\n organization: query.get(\"organization\") || undefined,\n resource: resource || undefined,\n });\n };\n\n const silentLogin = (request: Request, response: Response) =>\n startLogin(request, response, { returnUrl: `${request.baseUrl ?? \"\"}${prefix}/silent-login-callback`, prompt: \"none\" });\n\n const callback = async (request: Request, response: Response) => {\n const query = readQuery(request);\n const state = query.get(\"state\") ?? \"\";\n const cookieName = state ? loginCookieName(state) : undefined;\n const sealed = cookieName ? readCookies(request).get(cookieName) : undefined;\n const pending = sealed ? protector.open<LoginState>(\"login\", sealed) : undefined;\n\n if (cookieName && sealed) setCookie(response, cookieName, \"\", \"lax\", 0);\n\n response.set(\"Cache-Control\", \"no-store\");\n\n const fail = (target: string, error: string) => response.redirect(302, withQueryParameter(target, \"error\", error));\n\n if (!pending || pending.transaction?.state !== state || pending.expiresAt <= clock.now()) return fail(settings.defaultReturnUrl, \"login_failed\");\n\n const target = isLocalUrl(pending.returnUrl) ? pending.returnUrl : settings.defaultReturnUrl;\n\n if (settings.requireIssuerParameter && !query.has(\"iss\") && !query.has(\"error\")) return fail(target, \"login_failed\");\n\n let data: SessionData;\n\n try {\n const { tokens, idToken } = await client.handleAuthorizationCallback(query, pending.transaction);\n const now = clock.now();\n const claims = toSessionClaims(idToken, await readAccessTokenSubject(tokens.access_token, decrypt));\n\n await replaceAccessTokenClaims(claims, tokens.access_token, decrypt);\n\n if (settings.getClaimsFromUserInfoEndpoint) mergeUserInfo(claims, await client.userInfo(tokens.access_token));\n\n data = {\n publicId: randomBytes(16).toString(\"hex\"),\n claims,\n accessToken: tokens.access_token,\n accessTokenExpiresAt: now + lifetimeOf(tokens.expires_in),\n refreshToken: tokens.refresh_token,\n idToken: tokens.id_token,\n tokenType: tokens.token_type,\n resource: pending.transaction.resource,\n createdAt: now,\n validatedAt: now,\n };\n } catch (error) {\n report(error);\n\n return fail(target, error instanceof AuthioProtocolError ? error.error : \"login_failed\");\n }\n\n const previous = readCookies(request).get(settings.cookieName);\n const previousId = previous ? protector.unsign(previous) : undefined;\n\n if (previousId) {\n await store.delete(settings.sessionKeyPrefix + previousId);\n dropExchanges(settings.sessionKeyPrefix + previousId);\n }\n\n const id = randomBytes(32).toString(\"hex\");\n\n expose(request, await write(settings.sessionKeyPrefix + id, data, clock.now() + settings.sessionLifetimeMs));\n setCookie(response, settings.cookieName, protector.sign(id), settings.cookieSameSite);\n response.redirect(302, target);\n };\n\n const user = async (request: Request, response: Response) => {\n if (!antiForgery(request)) return antiForgeryRequired(response);\n\n const session = await loadSession(request, response);\n\n if (!session) return sendError(response, 401, \"unauthorized\", \"Nobody is signed in.\");\n\n const expiresIn = Math.max(0, Math.floor((session.expiresAt - clock.now()) / 1000));\n const logoutUrl = `${request.baseUrl ?? \"\"}${prefix}/logout?sid=${encodeURIComponent(session.data.publicId)}`;\n\n response.set(\"Cache-Control\", \"no-store\").json(toAuthioBffClaims(session.data.claims, expiresIn, logoutUrl));\n };\n\n const logout = async (request: Request, response: Response) => {\n const query = readQuery(request);\n const returnUrl = query.get(\"returnUrl\") ?? \"\";\n\n if (returnUrl && !isLocalUrl(returnUrl)) return sendError(response, 400, \"invalid_request\", \"returnUrl must be a local URL.\");\n\n const target = returnUrl || settings.defaultReturnUrl;\n const session = await loadSession(request, response);\n\n response.set(\"Cache-Control\", \"no-store\");\n\n if (!session) {\n clearCookie(response);\n return response.redirect(302, target);\n }\n\n if (!antiForgery(request) && !fixedTimeEquals(query.get(\"sid\") ?? \"\", session.data.publicId))\n return sendError(response, 400, \"invalid_request\", `Logging out requires the ${settings.antiForgeryHeaderName} header or the session's sid parameter.`);\n\n if (settings.revokeRefreshTokenOnLogout && session.data.refreshToken) {\n try {\n await client.revoke(session.data.refreshToken, { tokenTypeHint: AuthioTokenTypes.RefreshToken });\n } catch (error) {\n report(error);\n }\n }\n\n await endSessionByKey(request, response, session.key);\n\n const document = await client.discovery.get();\n\n if (!document.end_session_endpoint) return response.redirect(302, target);\n\n const endSessionUrl = await client.createEndSessionUrl({\n idTokenHint: session.data.idToken,\n postLogoutRedirectUri: settings.postLogoutRedirectUri ?? requestBase(request) + settings.signedOutCallbackPath,\n state: protector.seal(\"logout-state\", { returnUrl: target, expiresAt: clock.now() + loginLifetimeMs }),\n uiLocales: settings.uiLocales,\n });\n\n response.redirect(302, endSessionUrl);\n };\n\n const signedOutCallback = (request: Request, response: Response) => {\n const state = readQuery(request).get(\"state\");\n const value = state ? protector.open<{ returnUrl: string; expiresAt: number }>(\"logout-state\", state) : undefined;\n const target = value && value.expiresAt > clock.now() && isLocalUrl(value.returnUrl) ? value.returnUrl : settings.defaultReturnUrl;\n\n response.set(\"Cache-Control\", \"no-store\").redirect(302, target);\n };\n\n const silentLoginCallback = async (request: Request, response: Response) => {\n const session = await loadSession(request, response);\n const error = readQuery(request).get(\"error\") || null;\n const message = JSON.stringify({ source: AuthioBffDefaults.SilentLoginMessageSource, isLoggedIn: Boolean(session) && !error, error }).replace(\n /[^ -~]|[<>&]/g,\n (character) => `\\\\u${character.charCodeAt(0).toString(16).padStart(4, \"0\")}`,\n );\n\n response\n .set(\"Cache-Control\", \"no-store\")\n .type(\"text/html; charset=utf-8\")\n .send(\n `<!doctype html><html><head><meta charset=\"utf-8\"><title>Authio</title></head><body><script>(function(){var m=${message};(window.parent!==window?window.parent:window.opener||window).postMessage(m,window.location.origin);})();</script></body></html>`,\n );\n };\n\n const backChannelLogout = async (request: Request, response: Response) => {\n response.set(\"Cache-Control\", \"no-store\");\n\n if (!settings.backChannelLogout) return void response.status(404).end();\n\n const token = await readFormField(request, \"logout_token\");\n\n if (!token?.trim()) return sendError(response, 400, \"invalid_request\", \"The logout_token form field is required.\");\n\n let logout;\n\n try {\n logout = await client.validateLogoutToken(token);\n } catch (error) {\n report(error);\n\n if (!(error instanceof AuthioError)) throw error;\n\n return sendError(response, 400, \"invalid_request\", \"The logout token is not valid.\");\n }\n\n if (logout.sessionId) await store.deleteBySessionId(logout.sessionId);\n else if (logout.subject) await store.deleteBySubject(logout.subject);\n\n response.status(200).end();\n };\n\n const routes = new Map<string, { methods: readonly string[]; run: (request: Request, response: Response) => unknown }>([\n [settings.callbackPath, { methods: [\"GET\"], run: callback }],\n [settings.signedOutCallbackPath, { methods: [\"GET\"], run: signedOutCallback }],\n [`${prefix}/login`, { methods: [\"GET\"], run: login }],\n [`${prefix}/logout`, { methods: [\"GET\", \"POST\"], run: logout }],\n [`${prefix}/user`, { methods: [\"GET\"], run: user }],\n [`${prefix}/silent-login`, { methods: [\"GET\"], run: silentLogin }],\n [`${prefix}/silent-login-callback`, { methods: [\"GET\"], run: silentLoginCallback }],\n [`${prefix}/backchannel-logout`, { methods: [\"POST\"], run: backChannelLogout }],\n ]);\n\n const middleware = (request: Request, response: Response, next: NextFunction) => {\n const route = routes.get(request.path.replace(/(.)\\/+$/, \"$1\"));\n\n if (route && route.methods.includes(request.method)) {\n Promise.resolve()\n .then(() => route.run(request, response))\n .catch(next);\n return;\n }\n\n loadSession(request, response).then(() => next(), next);\n };\n\n const bff = middleware as AuthioBff;\n\n return Object.assign(bff, {\n client,\n store,\n api(apiOptions: AuthioBffApiOptions = {}): RequestHandler {\n return (request, response, next) => {\n if (apiOptions.requireAntiForgery !== false && !antiForgery(request)) return antiForgeryRequired(response);\n\n loadSession(request, response).then((session) => {\n if (!session && !apiOptions.optional) return sendError(response, 401, \"unauthorized\", \"Sign in first.\");\n\n next();\n }, next);\n };\n },\n remoteApi(destination: string, remoteOptions: AuthioBffRemoteApiOptions = {}): RequestHandler {\n const remote = resolveRemoteApi(destination, remoteOptions);\n\n return (request, response, next) => {\n forwardToRemoteApi(request, response, remote, {\n antiForgery: () => antiForgery(request),\n antiForgeryHeaderName: settings.antiForgeryHeaderName,\n hasSession: async () => Boolean(await loadSession(request, response)),\n userToken: () => getUserAccessToken(request, response, { resource: remote.resource, scopes: remote.scopes }),\n clientToken: async () => (await client.clientCredentials.getToken({ scopes: remote.scopes, resource: remote.resource })).accessToken,\n endIfInactive: () => endIfInactive(request, response),\n }).catch(next);\n };\n },\n async getSession(request: Request, response: Response) {\n const session = await loadSession(request, response);\n\n return session ? toInfo(session) : undefined;\n },\n getUserAccessToken,\n userFetch(request: Request, response: Response, fetchOptions: AuthioBffUserTokenOptions & { fetch?: typeof fetch } = {}): typeof fetch {\n const inner = fetchOptions.fetch ?? client.connection.fetch;\n\n return async (input, init) => {\n const token = await getUserAccessToken(request, response, fetchOptions);\n const headers = new Headers(init?.headers ?? (input instanceof globalThis.Request ? input.headers : undefined));\n\n if (token) headers.set(\"authorization\", `Bearer ${token}`);\n\n return inner(input, { ...init, headers });\n };\n },\n async endSession(request: Request, response: Response) {\n const session = await loadSession(request, response);\n\n if (session) await endSessionByKey(request, response, session.key);\n },\n });\n}\n\nfunction lifetimeOf(expiresIn: number | undefined): number {\n return (expiresIn && expiresIn > 0 ? expiresIn : AuthioDefaults.AssumedTokenLifetime) * 1000;\n}\n\nfunction textOf(value: unknown): string | undefined {\n return typeof value === \"string\" && value ? value : undefined;\n}\n\nfunction fixedTimeEquals(left: string, right: string): boolean {\n const a = Buffer.from(left, \"utf8\");\n const b = Buffer.from(right, \"utf8\");\n\n return a.length > 0 && a.length === b.length && timingSafeEqual(a, b);\n}\n\n"]}
@@ -0,0 +1,327 @@
1
+ import { RequestHandler, Request, Response } from 'express';
2
+ import { AuthioClock, AuthioClientOptions, AuthioClient, AuthioPrompt } from '@hyper.authio/client';
3
+
4
+ /**
5
+ * A session as the store keeps it. `data` is encrypted by the BFF (AES-256-GCM, from `cookieSecret`): the store never
6
+ * sees the tokens. `sessionId` and `subject` are kept in clear so back-channel logout can find the sessions to end.
7
+ */
8
+ interface AuthioBffStoredSession {
9
+ /** The store key: `sessionKeyPrefix` followed by 64 random hex characters. The cookie carries the part after the prefix. */
10
+ readonly key: string;
11
+ /** The Authio session (`sid` of the ID token), when Authio named one. */
12
+ readonly sessionId: string | undefined;
13
+ /** The person (`sub`). */
14
+ readonly subject: string | undefined;
15
+ /** When the session expires, in milliseconds since the epoch; a store may drop it from then on. */
16
+ readonly expiresAt: number;
17
+ /** The encrypted session. */
18
+ readonly data: string;
19
+ }
20
+ /**
21
+ * Where the BFF keeps its sessions. The default is in memory (one process); implement this over Redis or a database for
22
+ * several instances, indexing each session by `sessionId` and `subject` so back-channel logout can end them.
23
+ * Every method may return a value or a promise.
24
+ */
25
+ interface AuthioBffSessionStore {
26
+ /** Reads a session; `undefined` when it is unknown or expired. */
27
+ get(key: string): AuthioBffStoredSession | undefined | Promise<AuthioBffStoredSession | undefined>;
28
+ /** Creates or replaces a session, kept until `expiresAt`. */
29
+ set(session: AuthioBffStoredSession): void | Promise<void>;
30
+ /** Deletes a session. */
31
+ delete(key: string): void | Promise<void>;
32
+ /** Deletes every session of an Authio session (`sid`). */
33
+ deleteBySessionId(sessionId: string): void | Promise<void>;
34
+ /** Deletes every session of a person (`sub`). */
35
+ deleteBySubject(subject: string): void | Promise<void>;
36
+ }
37
+ /**
38
+ * Options of the in-memory store.
39
+ */
40
+ interface AuthioBffMemoryStoreOptions {
41
+ /** The clock expiry is measured with. Defaults to the system clock. */
42
+ clock?: AuthioClock;
43
+ }
44
+ /**
45
+ * An in-memory session store, indexed by `sessionId` and `subject`, that drops expired sessions. Sessions live in one
46
+ * process and are lost on restart: use a shared store for several instances.
47
+ */
48
+ interface AuthioBffMemoryStore extends AuthioBffSessionStore {
49
+ /** The number of sessions held, expired ones included until they are swept. */
50
+ readonly size: number;
51
+ get(key: string): AuthioBffStoredSession | undefined;
52
+ set(session: AuthioBffStoredSession): void;
53
+ delete(key: string): void;
54
+ deleteBySessionId(sessionId: string): void;
55
+ deleteBySubject(subject: string): void;
56
+ }
57
+ /**
58
+ * Creates the in-memory session store.
59
+ */
60
+ declare function createAuthioBffMemoryStore(options?: AuthioBffMemoryStoreOptions): AuthioBffMemoryStore;
61
+
62
+ /**
63
+ * Default names and values of the Authio BFF, the same as `Authio.AspNetCore.Bff`'s.
64
+ */
65
+ declare const AuthioBffDefaults: {
66
+ /** The session cookie name. `__Host-` makes browsers refuse it unless it is `Secure`, has `Path=/` and no `Domain`. */
67
+ readonly CookieName: "__Host-authio";
68
+ /** The route prefix of the BFF endpoints. */
69
+ readonly EndpointPrefix: "/bff";
70
+ /** The anti-forgery header the SPA sends on every API call. */
71
+ readonly AntiForgeryHeaderName: "X-CSRF";
72
+ /** The value the anti-forgery header carries. */
73
+ readonly AntiForgeryHeaderValue: "1";
74
+ /** The redirect path Authio sends the person back to after sign-in. */
75
+ readonly CallbackPath: "/signin-oidc";
76
+ /** The redirect path Authio sends the person back to after sign-out. */
77
+ readonly SignedOutCallbackPath: "/signout-callback-oidc";
78
+ /** The `source` of the message the silent-login page posts to its parent window. */
79
+ readonly SilentLoginMessageSource: "authio-bff-silent-login";
80
+ /** The prefix of the keys sessions are stored under. */
81
+ readonly SessionKeyPrefix: "authio-bff:";
82
+ /** Seconds a session lasts without activity (8 hours). */
83
+ readonly SessionLifetime: number;
84
+ /** Seconds between two checks of the Authio session (5 minutes). */
85
+ readonly SessionValidationInterval: number;
86
+ /** Seconds a sign-in may take between the login request and the callback (15 minutes). */
87
+ readonly LoginLifetime: number;
88
+ /** The scopes asked for at sign-in; `offline_access` gives the BFF a refresh token. */
89
+ readonly Scopes: readonly string[];
90
+ };
91
+ /**
92
+ * How the BFF checks that the person's Authio session is still alive: `none`, or `introspection` of the session's
93
+ * refresh token (its access token when there is none) every `sessionValidationInterval`.
94
+ */
95
+ type AuthioBffSessionValidation = "none" | "introspection";
96
+ /**
97
+ * The `SameSite` mode of the session cookie.
98
+ */
99
+ type AuthioBffCookieSameSite = "lax" | "strict";
100
+ /**
101
+ * Settings of the Authio BFF. The client itself (`issuer` or `authority` + `realm`, `clientId`, `clientSecret`,
102
+ * `clientAuthentication`, `clientPrivateKey`, `clientEncryptionKey`, `tokenRefreshSkew`…) takes the names of
103
+ * `conformance/options.md`; the BFF's own settings are those of `Authio.AspNetCore.Bff` (`Authio:Bff`).
104
+ */
105
+ interface AuthioBffOptions extends Partial<AuthioClientOptions> {
106
+ /**
107
+ * An Authio client to use instead of creating one from the connection and credential options above. The BFF still
108
+ * reads `tokenRefreshSkew`, `clientEncryptionKey`, `redirectUri` and `postLogoutRedirectUri` from these options.
109
+ */
110
+ client?: AuthioClient;
111
+ /**
112
+ * Secrets the session cookie is signed with and the stored sessions and sign-in cookies are encrypted with, at least
113
+ * 32 characters each. The first one signs and encrypts; every one is accepted, so a new secret can be put first and
114
+ * the old one removed once the sessions it protects have expired.
115
+ */
116
+ cookieSecret: string | readonly string[];
117
+ /** Where sessions live. Defaults to an in-memory store; give a shared one (Redis…) for several instances. */
118
+ store?: AuthioBffSessionStore;
119
+ /** The route prefix of the BFF endpoints. Defaults to `/bff`. */
120
+ endpointPrefix?: string;
121
+ /** The scopes asked for at sign-in. Defaults to `openid profile email offline_access`; `openid` is always sent. */
122
+ scopes?: readonly string[];
123
+ /** Adds the `roles` scope, so the session carries the `*_resource_access` claims flattened on `/bff/user`. */
124
+ requestRoles?: boolean;
125
+ /** Calls Authio's UserInfo endpoint after sign-in and adds its claims to the session. Defaults to `false`. */
126
+ getClaimsFromUserInfoEndpoint?: boolean;
127
+ /** The `organization` sent on every sign-in unless the login request names one. */
128
+ organization?: string;
129
+ /**
130
+ * The `resource` (RFC 8707) sent on every sign-in unless the login request names one: the session's access token is
131
+ * then addressed to that API. Keep `offline_access`, so the session check introspects the refresh token.
132
+ */
133
+ resource?: string;
134
+ /** The resources a login request may ask for with `?resource=`; any other is refused with `400`. */
135
+ allowedResources?: readonly string[];
136
+ /** The `prompt` sent on every sign-in unless the login request names one. */
137
+ prompt?: AuthioPrompt;
138
+ /** The `ui_locales` sent on every sign-in and sign-out unless the request names one. */
139
+ uiLocales?: string;
140
+ /** The `max_age` (seconds) sent on every sign-in unless the login request names one. */
141
+ maxAge?: number;
142
+ /** Extra authorize parameters sent on every sign-in. */
143
+ additionalAuthorizationParameters?: Readonly<Record<string, string>>;
144
+ /** Refuses an authorization response without `iss` (RFC 9207). Defaults to `true`; Authio always sends it. */
145
+ requireIssuerParameter?: boolean;
146
+ /** The redirect path registered at Authio for sign-in. Defaults to `/signin-oidc`. */
147
+ callbackPath?: string;
148
+ /** The post-logout redirect path registered at Authio. Defaults to `/signout-callback-oidc`. */
149
+ signedOutCallbackPath?: string;
150
+ /** Where the browser lands after login or logout without a `returnUrl`. Must be local. Defaults to `/`. */
151
+ defaultReturnUrl?: string;
152
+ /** The session cookie name. Defaults to `__Host-authio`; it must start with `__Host-` or `__Secure-`. */
153
+ cookieName?: string;
154
+ /** The session cookie's `SameSite`: `lax` (default) or `strict`. */
155
+ cookieSameSite?: AuthioBffCookieSameSite;
156
+ /** Seconds a session lasts without activity. Defaults to 28800 (8 hours). */
157
+ sessionLifetime?: number;
158
+ /** Renews `sessionLifetime` on activity once half of it has passed. Defaults to `true`. */
159
+ slidingExpiration?: boolean;
160
+ /** How the Authio session is checked. Defaults to `introspection`. */
161
+ sessionValidation?: AuthioBffSessionValidation;
162
+ /** Seconds between two checks of the Authio session. Defaults to 300. */
163
+ sessionValidationInterval?: number;
164
+ /** Revokes the refresh token at Authio on logout. Defaults to `true`. */
165
+ revokeRefreshTokenOnLogout?: boolean;
166
+ /** The anti-forgery header API calls must carry. Defaults to `X-CSRF`. */
167
+ antiForgeryHeaderName?: string;
168
+ /** The value the anti-forgery header must carry. Defaults to `1`. */
169
+ antiForgeryHeaderValue?: string;
170
+ /** The prefix of the keys sessions are stored under. Defaults to `authio-bff:`. */
171
+ sessionKeyPrefix?: string;
172
+ /**
173
+ * Serves `POST {endpointPrefix}/backchannel-logout`, which ends the sessions of the Authio session (`sid`) or person
174
+ * (`sub`) a logout token names. Defaults to `true`; register that URL as the client's back-channel logout URI.
175
+ */
176
+ backChannelLogout?: boolean;
177
+ /** Called with errors the BFF recovers from: a failed revocation, introspection or refresh, a refused logout token. */
178
+ onError?: (error: unknown) => void;
179
+ }
180
+ /**
181
+ * Whether a URL is local to the application: it starts with a single `/` (not `//` nor `/\`) and holds no control
182
+ * characters, so redirecting to it cannot leave the site.
183
+ */
184
+ declare function isLocalUrl(url: string | undefined | null): url is string;
185
+
186
+ /**
187
+ * Which token a remote API call is forwarded with, as `AuthioBffRemoteApiTokenMode` in .NET:
188
+ *
189
+ * - `user`: the person's access token (refreshed when needed); anonymous calls are refused with `401`.
190
+ * - `userOrNone`: the person's token when signed in, otherwise no token.
191
+ * - `clientCredentials`: a client-credentials token of the BFF itself, whoever is signed in.
192
+ * - `userOrClientCredentials`: the person's token when signed in, otherwise a client-credentials token.
193
+ * - `none`: no token (cookies and the anti-forgery header are still stripped).
194
+ */
195
+ type AuthioBffRemoteApiTokenMode = "user" | "userOrNone" | "clientCredentials" | "userOrClientCredentials" | "none";
196
+ /**
197
+ * Settings of one remote API.
198
+ */
199
+ interface AuthioBffRemoteApiOptions {
200
+ /** Which token the calls are forwarded with. Defaults to `user`. */
201
+ tokenMode?: AuthioBffRemoteApiTokenMode;
202
+ /**
203
+ * The resource (RFC 8707) the token must be issued for: a user token is exchanged for it (cached per session and
204
+ * resource), a client-credentials token is asked for it.
205
+ */
206
+ resource?: string;
207
+ /** The scopes the token must carry; for a user token, setting them triggers a token exchange (down-scoping). */
208
+ scopes?: readonly string[];
209
+ /** Requires the anti-forgery header on every call. Defaults to `true`; turn it off only for an anonymous API. */
210
+ requireAntiForgery?: boolean;
211
+ /** When the remote API answers `401` to a user call, checks the Authio session and ends the BFF session if it is gone. Defaults to `true`. */
212
+ validateSessionOnUnauthorized?: boolean;
213
+ /** The `fetch` the calls go through. Defaults to the global one. */
214
+ fetch?: typeof fetch;
215
+ }
216
+
217
+ /**
218
+ * The signed-in session as the application sees it: who, which Authio session, until when. Never the tokens.
219
+ */
220
+ interface AuthioBffSessionInfo {
221
+ /** The person (`sub`). */
222
+ readonly subject: string | undefined;
223
+ /** The Authio session (`sid`). */
224
+ readonly sessionId: string | undefined;
225
+ /** The session's claims: the ID token's, the access token's `*_resource_access` and organization, UserInfo's. */
226
+ readonly claims: Readonly<Record<string, unknown>>;
227
+ /** When the BFF session expires, in milliseconds since the epoch. */
228
+ readonly expiresAt: number;
229
+ }
230
+ declare global {
231
+ namespace Express {
232
+ interface Request {
233
+ /** The Authio BFF session, once the BFF read it for this request; `undefined` when nobody is signed in. */
234
+ authioBff?: AuthioBffSessionInfo;
235
+ }
236
+ }
237
+ }
238
+ /**
239
+ * Options of `bff.api()`.
240
+ */
241
+ interface AuthioBffApiOptions {
242
+ /** Requires the anti-forgery header. Defaults to `true`. */
243
+ requireAntiForgery?: boolean;
244
+ /** Lets anonymous calls through (with `req.authioBff` undefined) instead of answering `401`. Defaults to `false`. */
245
+ optional?: boolean;
246
+ }
247
+ /**
248
+ * What a user token is asked for: a resource (RFC 8707) and/or narrower scopes make the BFF exchange the session's
249
+ * token (RFC 8693) and cache the result per session, resource and scopes.
250
+ */
251
+ interface AuthioBffUserTokenOptions {
252
+ /** The resource the token must be issued for. */
253
+ resource?: string;
254
+ /** The scopes the token must carry. */
255
+ scopes?: readonly string[];
256
+ }
257
+ /**
258
+ * The Authio BFF: an Express middleware serving the BFF endpoints, plus the helpers the application's own routes use.
259
+ */
260
+ interface AuthioBff extends RequestHandler {
261
+ /** The Authio client the BFF talks to Authio with. */
262
+ readonly client: AuthioClient;
263
+ /** Where the sessions live. */
264
+ readonly store: AuthioBffSessionStore;
265
+ /**
266
+ * Marks a local API: requires the anti-forgery header (`401 antiforgery_required`) and a session (`401
267
+ * unauthorized`), never redirects, and sets `req.authioBff`.
268
+ */
269
+ api(options?: AuthioBffApiOptions): RequestHandler;
270
+ /**
271
+ * Forwards the calls mounted under a path to a remote API with the token `tokenMode` asks for:
272
+ * `app.use("/api/orders", bff.remoteApi("https://orders.example.com"))`. Mount it before any body parser.
273
+ */
274
+ remoteApi(destination: string, options?: AuthioBffRemoteApiOptions): RequestHandler;
275
+ /** Reads the request's session (once per request), checking and renewing it as configured. */
276
+ getSession(request: Request, response: Response): Promise<AuthioBffSessionInfo | undefined>;
277
+ /**
278
+ * The person's access token, refreshed `tokenRefreshSkew` before it expires (one refresh per session at a time), or
279
+ * exchanged for `resource`/`scopes`. `undefined` when nobody is signed in or the session ended (a refused refresh
280
+ * ends it).
281
+ */
282
+ getUserAccessToken(request: Request, response: Response, options?: AuthioBffUserTokenOptions): Promise<string | undefined>;
283
+ /** A `fetch` that sends the person's access token (see `getUserAccessToken`), for calling APIs from a route. */
284
+ userFetch(request: Request, response: Response, options?: AuthioBffUserTokenOptions & {
285
+ fetch?: typeof fetch;
286
+ }): typeof fetch;
287
+ /** Ends the request's session locally: deletes it from the store and clears the cookie. Authio is not told. */
288
+ endSession(request: Request, response: Response): Promise<void>;
289
+ }
290
+ /**
291
+ * Creates the Authio BFF. Mount it with `app.use(bff)`: it serves `{endpointPrefix}/login`, `/logout`, `/user`,
292
+ * `/silent-login`, `/silent-login-callback`, `/backchannel-logout`, the sign-in callback (`callbackPath`) and the
293
+ * sign-out callback (`signedOutCallbackPath`), and reads the session of every other request that carries the cookie.
294
+ *
295
+ * @throws AuthioConfigurationError naming every invalid option.
296
+ */
297
+ declare function authioBff(options: AuthioBffOptions): AuthioBff;
298
+
299
+ /**
300
+ * The claim types `/bff/user` adds to the person's claims.
301
+ */
302
+ declare const AuthioBffClaimTypes: {
303
+ /** Seconds until the BFF session expires. */
304
+ readonly SessionExpiresIn: "bff:session_expires_in";
305
+ /** The URL the SPA navigates to in order to log out; it carries the session's `sid`. */
306
+ readonly LogoutUrl: "bff:logout_url";
307
+ /** One Authio role the person holds (flattened from the `*_resource_access` claims). */
308
+ readonly Role: "role";
309
+ /** One permission the person holds, as `{resource}:{method}` (flattened from the `*_resource_access` claims). */
310
+ readonly Permission: "permission";
311
+ };
312
+ /**
313
+ * One entry of the `/bff/user` answer: a string value, or a number for `bff:session_expires_in`.
314
+ */
315
+ interface AuthioBffClaim {
316
+ readonly type: string;
317
+ readonly value: string | number;
318
+ }
319
+ /**
320
+ * The JSON body of a BFF refusal, shaped like an OAuth error.
321
+ */
322
+ interface AuthioBffError {
323
+ readonly error: string;
324
+ readonly error_description?: string;
325
+ }
326
+
327
+ export { type AuthioBff, type AuthioBffApiOptions, type AuthioBffClaim, AuthioBffClaimTypes, type AuthioBffCookieSameSite, AuthioBffDefaults, type AuthioBffError, type AuthioBffMemoryStore, type AuthioBffMemoryStoreOptions, type AuthioBffOptions, type AuthioBffRemoteApiOptions, type AuthioBffRemoteApiTokenMode, type AuthioBffSessionInfo, type AuthioBffSessionStore, type AuthioBffSessionValidation, type AuthioBffStoredSession, type AuthioBffUserTokenOptions, authioBff, createAuthioBffMemoryStore, isLocalUrl };