@axiom-lattice/auth-oidc 0.2.0 → 0.3.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.
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthProviderDescriptor, AuthCookie,
|
|
1
|
+
import { AuthProviderDescriptor, AuthCookie, AuthProvider, AuthChallenge, AuthRequestContext, Principal } from '@axiom-lattice/protocols';
|
|
2
2
|
import { Configuration } from 'openid-client';
|
|
3
3
|
|
|
4
4
|
interface OidcConfig {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthProviderDescriptor, AuthCookie,
|
|
1
|
+
import { AuthProviderDescriptor, AuthCookie, AuthProvider, AuthChallenge, AuthRequestContext, Principal } from '@axiom-lattice/protocols';
|
|
2
2
|
import { Configuration } from 'openid-client';
|
|
3
3
|
|
|
4
4
|
interface OidcConfig {
|
package/dist/index.js
CHANGED
|
@@ -341,7 +341,7 @@ function readOidcConfig(env = process.env, options = {}) {
|
|
|
341
341
|
return {
|
|
342
342
|
issuer,
|
|
343
343
|
clientId: required(env, "OIDC_CLIENT_ID"),
|
|
344
|
-
clientSecret:
|
|
344
|
+
clientSecret: env.OIDC_CLIENT_SECRET?.trim() ?? "",
|
|
345
345
|
redirectUri,
|
|
346
346
|
postLoginRedirectUri,
|
|
347
347
|
scopes: env.OIDC_SCOPES?.trim() || "openid profile email",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/endpointSafety.ts","../src/config.ts","../src/stateCookie.ts","../src/openidClient.ts","../src/probe.ts","../src/OidcAuthProvider.ts"],"sourcesContent":["export * from \"./config\";\nexport * from \"./stateCookie\";\nexport * from \"./endpointSafety\";\nexport * from \"./openidClient\";\nexport * from \"./probe\";\nexport * from \"./OidcAuthProvider\";\n","import { lookup } from \"node:dns/promises\";\nimport { isIP } from \"node:net\";\n\nexport interface EndpointSafetyOptions {\n allowInsecure: boolean;\n /**\n * Exact hostnames allowed to resolve to a private address.\n *\n * This is intentionally narrower than the general SSRF guard: it does not\n * allow HTTP, userinfo, query strings, or other private hosts. Deployments\n * that must reach a private OIDC issuer through a VPN/fake-IP DNS should list\n * only that issuer host here.\n */\n allowedPrivateHosts?: readonly string[];\n}\n\nfunction hostAddress(hostname: string): string {\n return hostname.replace(/^\\[/, \"\").replace(/\\]$/, \"\");\n}\n\nfunction normalizeHostEntry(raw: string): string | undefined {\n const trimmed = raw.trim();\n if (!trimmed) return undefined;\n let host = trimmed;\n if (trimmed.includes(\"://\")) {\n try {\n host = new URL(trimmed).hostname;\n } catch {\n return undefined;\n }\n }\n return hostAddress(host).toLowerCase().replace(/\\.$/, \"\");\n}\n\n/**\n * Parses `OIDC_ALLOWED_PRIVATE_HOSTS` into a normalized, exact hostname list.\n *\n * Entries are comma-separated. Surrounding whitespace, case, IPv6 brackets,\n * and trailing dots are normalized; schemes are accepted for convenience.\n * Invalid/empty entries are ignored rather than failing startup, so a typo\n * cannot silently widen access to a different host.\n */\nexport function parseAllowedPrivateHosts(\n value: string | undefined,\n): string[] {\n if (!value) return [];\n const hosts = new Set<string>();\n for (const entry of value.split(\",\")) {\n const host = normalizeHostEntry(entry);\n if (host) hosts.add(host);\n }\n return [...hosts];\n}\n\nfunction isAllowedPrivateHost(\n hostname: string,\n options: EndpointSafetyOptions,\n): boolean {\n const host = normalizeHostEntry(hostname);\n if (!host) return false;\n return (options.allowedPrivateHosts ?? []).some(\n (entry) => normalizeHostEntry(entry) === host,\n );\n}\n\nfunction isLocalHostname(hostname: string): boolean {\n const host = hostAddress(hostname);\n return host === \"localhost\" || host === \"127.0.0.1\" || host === \"::1\";\n}\n\nfunction isPrivateIPv4(ip: string): boolean {\n const parts = ip.split(\".\").map(Number);\n if (parts.length !== 4 || parts.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) {\n return true;\n }\n const [a, b, c] = parts;\n return (\n a === 0 ||\n a === 10 ||\n a === 127 ||\n (a === 169 && b === 254) ||\n (a === 172 && b >= 16 && b <= 31) ||\n (a === 192 && b === 168) ||\n (a === 192 && b === 0 && c === 0) ||\n (a === 192 && b === 0 && c === 2) ||\n (a === 198 && b === 51 && c === 100) ||\n (a === 203 && b === 0 && c === 113) ||\n (a === 100 && b >= 64 && b <= 127) ||\n (a === 198 && (b === 18 || b === 19)) ||\n a >= 224\n );\n}\n\nconst IPV6_8_BITS = 8;\nconst IPV6_GROUPS = 8;\n\nfunction ipv6Bytes(ip: string): number[] | null {\n let value = ip.toLowerCase();\n const zone = value.indexOf(\"%\");\n if (zone !== -1) value = value.slice(0, zone);\n\n const lastColon = value.lastIndexOf(\":\");\n if (lastColon !== -1 && value.slice(lastColon + 1).includes(\".\")) {\n const ipv4 = value.slice(lastColon + 1).split(\".\").map(Number);\n if (\n ipv4.length !== 4 ||\n ipv4.some((n) => !Number.isInteger(n) || n < 0 || n > 255)\n ) {\n return null;\n }\n const high = ((ipv4[0] << 8) | ipv4[1]).toString(16);\n const low = ((ipv4[2] << 8) | ipv4[3]).toString(16);\n value = `${value.slice(0, lastColon + 1)}${high}:${low}`;\n }\n\n const halves = value.split(\"::\");\n if (halves.length > 2) return null;\n const head = halves[0] ? halves[0].split(\":\") : [];\n const tail = halves.length === 2 && halves[1] ? halves[1].split(\":\") : [];\n let groups: string[];\n if (halves.length === 2) {\n const missing = IPV6_GROUPS - head.length - tail.length;\n if (missing < 1) return null;\n groups = [...head, ...Array(missing).fill(\"0\"), ...tail];\n } else {\n groups = head;\n }\n if (groups.length !== IPV6_GROUPS) return null;\n\n const bytes: number[] = [];\n for (const group of groups) {\n if (!/^[0-9a-f]{1,4}$/.test(group)) return null;\n const part = Number.parseInt(group, 16);\n bytes.push((part >> IPV6_8_BITS) & 0xff, part & 0xff);\n }\n return bytes;\n}\n\nfunction hasPrefix(bytes: number[], prefix: number[]): boolean {\n return prefix.every((value, index) => bytes[index] === value);\n}\n\nfunction isPrivateIPv6(ip: string): boolean {\n const bytes = ipv6Bytes(ip);\n if (!bytes) return true;\n if (bytes.every((value) => value === 0)) return true;\n const loopback = bytes.slice(0, 15).every((value) => value === 0) && bytes[15] === 1;\n if (loopback) return true;\n if ((bytes[0] & 0xfe) === 0xfc) return true;\n if (bytes[0] === 0xfe && (bytes[1] & 0xc0) === 0x80) return true;\n if (bytes[0] === 0xff) return true;\n if (\n bytes[0] === 0 &&\n bytes[1] === 0 &&\n bytes[2] === 0 &&\n bytes[3] === 0 &&\n bytes[4] === 0 &&\n bytes[5] === 0 &&\n bytes[6] === 0 &&\n bytes[7] === 0 &&\n bytes[8] === 0 &&\n bytes[9] === 0 &&\n bytes[10] === 0xff &&\n bytes[11] === 0xff\n ) {\n return isPrivateIPv4(\n `${bytes[12]}.${bytes[13]}.${bytes[14]}.${bytes[15]}`,\n );\n }\n if (hasPrefix(bytes, [0x00, 0x64, 0xff, 0x9b, 0, 0, 0, 0, 0, 0, 0, 0])) {\n return true;\n }\n if (hasPrefix(bytes, [0x00, 0x64, 0xff, 0x9b, 0x00, 0x01])) return true;\n if (hasPrefix(bytes, [0x20, 0x02])) return true;\n if (hasPrefix(bytes, [0x20, 0x01, 0x00, 0x00])) return true;\n if (hasPrefix(bytes, [0x20, 0x01, 0x0d, 0xb8])) return true;\n if (hasPrefix(bytes, [0x20, 0x01, 0x00, 0x02, 0x00, 0x00])) return true;\n if (hasPrefix(bytes, [0x01, 0x00, 0, 0, 0, 0, 0, 0])) return true;\n return false;\n}\n\nfunction isPrivateAddress(ip: string): boolean {\n const version = isIP(ip);\n if (version === 4) return isPrivateIPv4(ip);\n if (version === 6) return isPrivateIPv6(ip);\n return true;\n}\n\n/**\n * Addresses that must never be reachable even through the private-host\n * allowlist: cloud metadata/link-local and the unspecified address.\n *\n * Loopback is intentionally excluded so an intentional `127.0.0.1` / `::1`\n * allowlist entry keeps working for local development.\n */\nexport function isLinkLocalOrMetadataAddress(address: string): boolean {\n const ip = hostAddress(address);\n const version = isIP(ip);\n if (version === 4) {\n const parts = ip.split(\".\").map(Number);\n if (\n parts.length !== 4 ||\n parts.some((n) => !Number.isInteger(n) || n < 0 || n > 255)\n ) {\n return false;\n }\n return parts[0] === 0 || (parts[0] === 169 && parts[1] === 254);\n }\n if (version !== 6) return false;\n const bytes = ipv6Bytes(ip);\n if (!bytes) return false;\n if (bytes.every((value) => value === 0)) return true;\n if (bytes[0] === 0xfe && (bytes[1] & 0xc0) === 0x80) return true;\n const ipv4Mapped =\n bytes.slice(0, 10).every((value) => value === 0) &&\n bytes[10] === 0xff &&\n bytes[11] === 0xff;\n if (ipv4Mapped) {\n return isLinkLocalOrMetadataAddress(\n `${bytes[12]}.${bytes[13]}.${bytes[14]}.${bytes[15]}`,\n );\n }\n return false;\n}\n\nexport function assertSafeEndpointSyntax(\n raw: string,\n options: EndpointSafetyOptions,\n): URL {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(\"OIDC endpoint must be an absolute URL\");\n }\n if (url.username || url.password) {\n throw new Error(\"OIDC endpoint must not contain userinfo\");\n }\n if (url.search || url.hash) {\n throw new Error(\"OIDC endpoint must not contain query or fragment\");\n }\n const host = hostAddress(url.hostname);\n if (url.protocol !== \"https:\") {\n const localMock =\n options.allowInsecure && url.protocol === \"http:\" && isLocalHostname(host);\n if (!localMock) throw new Error(\"OIDC endpoint must use https\");\n }\n if (isLocalHostname(host) && !options.allowInsecure) {\n throw new Error(\"OIDC endpoint must not target localhost\");\n }\n if (isIP(host) && isLinkLocalOrMetadataAddress(host)) {\n throw new Error(\n \"OIDC endpoint must not target a link-local or metadata address\",\n );\n }\n if (isIP(host) && isPrivateAddress(host)) {\n if (\n !(options.allowInsecure && isLocalHostname(host)) &&\n !isAllowedPrivateHost(host, options)\n ) {\n throw new Error(\"OIDC endpoint must not target a private address\");\n }\n }\n return url;\n}\n\nexport async function assertSafeOidcEndpoint(\n raw: string,\n options: EndpointSafetyOptions,\n): Promise<URL> {\n const url = assertSafeEndpointSyntax(raw, options);\n const host = hostAddress(url.hostname);\n const allowlisted = isAllowedPrivateHost(host, options);\n if ((options.allowInsecure && isLocalHostname(host)) || isIP(host)) {\n return url;\n }\n const addresses = await lookup(host, { all: true, verbatim: true });\n if (addresses.length === 0) throw new Error(\"OIDC endpoint host did not resolve\");\n for (const { address } of addresses) {\n if (isLinkLocalOrMetadataAddress(address)) {\n throw new Error(\n \"OIDC endpoint host resolves to a link-local or metadata address\",\n );\n }\n if (isPrivateAddress(address) && !allowlisted) {\n throw new Error(\"OIDC endpoint host resolves to a private address\");\n }\n }\n return url;\n}\n","import type { AuthProviderDescriptor } from \"@axiom-lattice/protocols\";\nimport { parseAllowedPrivateHosts } from \"./endpointSafety\";\n\nexport interface OidcConfig {\n issuer: string;\n clientId: string;\n clientSecret: string;\n redirectUri: string;\n postLoginRedirectUri: string;\n scopes: string;\n displayName: string;\n defaultTenantId?: string;\n jitProvisioning: boolean;\n autoLinkEmail: boolean;\n allowInsecure: boolean;\n insecureCookies: boolean;\n stateSecret: string;\n /** Exact hosts allowed to resolve to a private address (deployment-wide). */\n allowedPrivateHosts?: string[];\n}\n\nexport interface ReadOidcConfigOptions {\n stateSecret?: string;\n nodeEnv?: string;\n}\n\nfunction required(env: NodeJS.ProcessEnv, name: string): string {\n const value = env[name]?.trim();\n if (!value) throw new Error(`${name} is required when OIDC_ENABLED=true`);\n return value;\n}\n\nfunction boolean(value: string | undefined, fallback = false): boolean {\n if (value === undefined || value.trim() === \"\") return fallback;\n return value === \"true\";\n}\n\nfunction isLocalHost(hostname: string): boolean {\n const host = hostname.replace(/^\\[/, \"\").replace(/\\]$/, \"\");\n return host === \"localhost\" || host === \"127.0.0.1\" || host === \"::1\";\n}\n\nfunction assertNoCredentialsQueryOrFragment(url: URL, name: string): void {\n if (url.username || url.password) {\n throw new Error(`${name} must not contain userinfo`);\n }\n if (url.search || url.hash) {\n throw new Error(`${name} must not contain query or fragment`);\n }\n}\n\n/**\n * Rejects the Azure multi-tenant issuers that cannot be used for identity\n * binding. Shared by the env config reader and runtime store-provider\n * validation so both paths enforce the same rule.\n *\n * @param url - Parsed issuer URL.\n * @param name - Setting name used in the error message.\n */\nexport function assertTenantSpecificAzureIssuer(\n url: URL,\n name = \"OIDC_ISSUER\",\n): void {\n if (url.hostname.toLowerCase() !== \"login.microsoftonline.com\") return;\n const firstSegment = url.pathname.split(\"/\").filter(Boolean)[0]?.toLowerCase();\n if (firstSegment === \"common\" || firstSegment === \"organizations\") {\n throw new Error(\n `${name} must be tenant-specific; ` +\n \"login.microsoftonline.com/common and /organizations are not supported\",\n );\n }\n}\n\nfunction parseIssuer(raw: string, allowInsecure: boolean): string {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(\"OIDC_ISSUER must be an absolute URL\");\n }\n assertNoCredentialsQueryOrFragment(url, \"OIDC_ISSUER\");\n assertTenantSpecificAzureIssuer(url);\n if (url.protocol !== \"https:\") {\n if (!(allowInsecure && url.protocol === \"http:\" && isLocalHost(url.hostname))) {\n throw new Error(\"OIDC_ISSUER must use https\");\n }\n }\n return url.toString().replace(/\\/$/, \"\");\n}\n\nfunction parseRedirect(raw: string, nodeEnv: string): string {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(\"OIDC redirect URLs must be absolute URLs\");\n }\n assertNoCredentialsQueryOrFragment(url, \"OIDC redirect URLs\");\n if (url.protocol !== \"https:\") {\n const localAllowed =\n (nodeEnv === \"test\" || nodeEnv === \"development\") &&\n url.protocol === \"http:\" &&\n isLocalHost(url.hostname);\n if (!localAllowed) {\n throw new Error(\"OIDC redirect URLs must use https outside test/development\");\n }\n }\n return url.toString();\n}\n\nexport function readOidcConfig(\n env: NodeJS.ProcessEnv = process.env,\n options: ReadOidcConfigOptions = {},\n): OidcConfig | null {\n if (!boolean(env.OIDC_ENABLED)) return null;\n\n const nodeEnv = options.nodeEnv ?? env.NODE_ENV ?? \"production\";\n const allowInsecure = boolean(env.OIDC_ALLOW_INSECURE);\n if (allowInsecure && nodeEnv !== \"test\" && nodeEnv !== \"development\") {\n throw new Error(\"OIDC_ALLOW_INSECURE is only allowed when NODE_ENV=test|development\");\n }\n\n const issuer = parseIssuer(required(env, \"OIDC_ISSUER\"), allowInsecure);\n const redirectUri = parseRedirect(required(env, \"OIDC_REDIRECT_URI\"), nodeEnv);\n const postLoginRedirectUri = parseRedirect(\n required(env, \"OIDC_POST_LOGIN_REDIRECT_URI\"),\n nodeEnv,\n );\n const jitProvisioning = boolean(env.OIDC_JIT_PROVISIONING);\n const defaultTenantId = env.OIDC_DEFAULT_TENANT_ID?.trim() || undefined;\n\n const stateSecret = (\n options.stateSecret ??\n env.AUTH_TOKEN_SECRET ??\n env.JWT_SECRET\n )?.trim();\n if (!stateSecret) {\n throw new Error(\"AUTH_TOKEN_SECRET or JWT_SECRET is required for OIDC state signing\");\n }\n if (stateSecret.length < 16) {\n throw new Error(\n \"AUTH_TOKEN_SECRET or JWT_SECRET must be at least 16 characters for OIDC state signing\",\n );\n }\n\n const allowedPrivateHosts = parseAllowedPrivateHosts(\n env.OIDC_ALLOWED_PRIVATE_HOSTS,\n );\n\n return {\n issuer,\n clientId: required(env, \"OIDC_CLIENT_ID\"),\n clientSecret: required(env, \"OIDC_CLIENT_SECRET\"),\n redirectUri,\n postLoginRedirectUri,\n scopes: env.OIDC_SCOPES?.trim() || \"openid profile email\",\n displayName: env.OIDC_DISPLAY_NAME?.trim() || \"Microsoft\",\n ...(defaultTenantId ? { defaultTenantId } : {}),\n jitProvisioning,\n autoLinkEmail: boolean(env.OIDC_AUTO_LINK_EMAIL),\n allowInsecure,\n insecureCookies: redirectUri.startsWith(\"http://\"),\n stateSecret,\n ...(allowedPrivateHosts.length ? { allowedPrivateHosts } : {}),\n };\n}\n\nexport function oidcDescriptor(config: OidcConfig): AuthProviderDescriptor {\n return {\n id: \"oidc\",\n displayName: config.displayName,\n interaction: \"redirect\",\n surfaces: [\"console\"],\n };\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport type { AuthCookie } from \"@axiom-lattice/protocols\";\n\nexport const OIDC_STATE_COOKIE = \"axiom_oidc_state\";\n/**\n * Shared by every provider route so the same cookie is readable for both\n * `/api/auth/login/:providerId` and `/api/auth/login/:providerId/callback`.\n */\nexport const OIDC_STATE_PATH = \"/api/auth/login\";\n\nexport interface OidcStatePayload {\n state: string;\n codeVerifier: string;\n nonce: string;\n /** Provider that started the flow; the callback rejects a mismatch. */\n providerId: string;\n exp: number;\n}\n\nfunction isPayload(value: unknown): value is OidcStatePayload {\n if (typeof value !== \"object\" || value === null) return false;\n const payload = value as Record<string, unknown>;\n return (\n typeof payload.state === \"string\" &&\n typeof payload.codeVerifier === \"string\" &&\n typeof payload.nonce === \"string\" &&\n typeof payload.providerId === \"string\" &&\n payload.providerId.length > 0 &&\n typeof payload.exp === \"number\" &&\n Number.isFinite(payload.exp)\n );\n}\n\nfunction stateKey(secret: string): Buffer {\n return createHmac(\"sha256\", secret).update(\"axiom-oidc-state-v1\").digest();\n}\n\nexport function signStateCookie(\n payload: OidcStatePayload,\n secret: string,\n): string {\n const encoded = Buffer.from(JSON.stringify(payload)).toString(\"base64url\");\n const signature = createHmac(\"sha256\", stateKey(secret))\n .update(encoded)\n .digest(\"base64url\");\n return `${encoded}.${signature}`;\n}\n\nexport function verifyStateCookie(\n value: string,\n secret: string,\n now: number = Date.now(),\n): OidcStatePayload | null {\n const [encoded, signature, extra] = value.split(\".\");\n if (!encoded || !signature || extra !== undefined) return null;\n const expected = createHmac(\"sha256\", stateKey(secret)).update(encoded).digest();\n let actual: Buffer;\n try {\n actual = Buffer.from(signature, \"base64url\");\n } catch {\n return null;\n }\n if (\n actual.length !== expected.length ||\n actual.toString(\"base64url\") !== signature ||\n !timingSafeEqual(actual, expected)\n ) {\n return null;\n }\n try {\n const payload: unknown = JSON.parse(\n Buffer.from(encoded, \"base64url\").toString(\"utf8\"),\n );\n if (!isPayload(payload) || payload.exp < now) return null;\n return payload;\n } catch {\n return null;\n }\n}\n\nexport function stateCookie(\n value: string,\n options: { secure: boolean; maxAge?: number },\n): AuthCookie {\n return {\n name: OIDC_STATE_COOKIE,\n value,\n options: {\n httpOnly: true,\n secure: options.secure,\n sameSite: \"lax\",\n path: OIDC_STATE_PATH,\n maxAge: options.maxAge ?? 600,\n },\n };\n}\n\nexport function clearStateCookie(options: { secure: boolean }): AuthCookie {\n return {\n name: OIDC_STATE_COOKIE,\n value: \"\",\n options: {\n httpOnly: true,\n secure: options.secure,\n sameSite: \"lax\",\n path: OIDC_STATE_PATH,\n maxAge: 0,\n },\n };\n}\n\nexport function serializeCookie(cookie: AuthCookie): string {\n const parts = [`${cookie.name}=${encodeURIComponent(cookie.value)}`];\n if (cookie.options.httpOnly) parts.push(\"HttpOnly\");\n if (cookie.options.secure) parts.push(\"Secure\");\n if (cookie.options.sameSite) {\n parts.push(\n `SameSite=${cookie.options.sameSite[0].toUpperCase()}${cookie.options.sameSite.slice(1)}`,\n );\n }\n if (cookie.options.path) parts.push(`Path=${cookie.options.path}`);\n if (cookie.options.maxAge !== undefined) parts.push(`Max-Age=${cookie.options.maxAge}`);\n return parts.join(\"; \");\n}\n\nexport function readCookie(header: string | undefined, name: string): string | undefined {\n if (!header) return undefined;\n for (const part of header.split(\";\")) {\n const [rawName, ...rest] = part.trim().split(\"=\");\n if (rawName === name) {\n try {\n return decodeURIComponent(rest.join(\"=\"));\n } catch (error) {\n if (error instanceof URIError) return undefined;\n throw error;\n }\n }\n }\n return undefined;\n}\n","import type { Configuration } from \"openid-client\";\n\nexport interface OidcDiscoveryOptions {\n timeoutSeconds: number;\n allowInsecure: boolean;\n}\n\nexport interface OidcClientLike {\n discovery(\n issuer: URL,\n clientId: string,\n clientSecret: string,\n options: OidcDiscoveryOptions,\n ): Promise<Configuration>;\n randomState(): string;\n randomPKCECodeVerifier(): string;\n calculatePKCECodeChallenge(verifier: string): Promise<string>;\n randomNonce(): string;\n buildAuthorizationUrl(\n config: Configuration,\n parameters: Record<string, string>,\n ): URL;\n authorizationCodeGrant(\n config: Configuration,\n currentUrl: URL,\n checks: {\n pkceCodeVerifier: string;\n expectedState: string;\n expectedNonce: string;\n },\n ): Promise<{ claims(): Record<string, unknown> | undefined }>;\n}\n\n/** Load the ESM-only `openid-client` package from CJS consumers. */\nexport async function loadOpenidClient(): Promise<OidcClientLike> {\n const mod = await import(\"openid-client\");\n return {\n discovery: (issuer, clientId, clientSecret, options) => {\n const execute = options.allowInsecure ? [mod.allowInsecureRequests] : undefined;\n return mod.discovery(issuer, clientId, clientSecret, undefined, {\n timeout: options.timeoutSeconds,\n ...(execute ? { execute } : {}),\n });\n },\n randomState: () => mod.randomState(),\n randomPKCECodeVerifier: () => mod.randomPKCECodeVerifier(),\n calculatePKCECodeChallenge: (verifier) => mod.calculatePKCECodeChallenge(verifier),\n randomNonce: () => mod.randomNonce(),\n buildAuthorizationUrl: (config, parameters) =>\n mod.buildAuthorizationUrl(config, parameters),\n authorizationCodeGrant: async (config, currentUrl, checks) => {\n const tokens = await mod.authorizationCodeGrant(config, currentUrl, checks);\n return {\n claims: () => tokens.claims() as unknown as Record<string, unknown> | undefined,\n };\n },\n };\n}\n","import {\n assertSafeOidcEndpoint,\n type EndpointSafetyOptions,\n} from \"./endpointSafety\";\nimport { loadOpenidClient, type OidcClientLike } from \"./openidClient\";\n\n/** Input needed to run OIDC discovery without starting a login flow. */\nexport interface OidcProbeConfig {\n issuer: string;\n clientId: string;\n clientSecret: string;\n allowInsecure: boolean;\n /** Exact hostnames allowed to resolve to a private address. */\n allowedPrivateHosts?: string[];\n /** Injectable client for offline tests; defaults to `openid-client`. */\n client?: OidcClientLike;\n /** Injectable endpoint validator for offline tests. */\n validateEndpoint?: (\n endpoint: string,\n options: EndpointSafetyOptions,\n ) => Promise<URL>;\n}\n\n/** Safe metadata summary returned by {@link probeOidcProvider}. */\nexport interface OidcProviderMetadataSummary {\n issuer: string;\n authorizationEndpoint: string;\n tokenEndpoint: string;\n jwksUri: string;\n userinfoEndpoint?: string;\n scopesSupported?: string[];\n}\n\nfunction requiredMetadataString(\n metadata: Record<string, unknown>,\n key: string,\n): string {\n const value = metadata[key];\n if (typeof value !== \"string\" || !value) {\n throw new Error(\"OIDC discovery returned incomplete metadata\");\n }\n return value;\n}\n\n/**\n * Runs OIDC discovery and validates the issuer plus every endpoint the provider\n * advertises. This never sets cookies or touches session state.\n *\n * @param config - Issuer, client credentials, and optional test seams.\n * @returns A safe metadata summary for the admin test endpoint.\n */\nexport async function probeOidcProvider(\n config: OidcProbeConfig,\n): Promise<OidcProviderMetadataSummary> {\n const client = config.client ?? (await loadOpenidClient());\n const validateEndpoint = config.validateEndpoint ?? assertSafeOidcEndpoint;\n const safety = {\n allowInsecure: config.allowInsecure,\n allowedPrivateHosts: config.allowedPrivateHosts ?? [],\n };\n\n await validateEndpoint(config.issuer, safety);\n const discovered = await client.discovery(\n new URL(config.issuer),\n config.clientId,\n config.clientSecret,\n { timeoutSeconds: 5, allowInsecure: config.allowInsecure },\n );\n const metadata = discovered.serverMetadata() as unknown as Record<\n string,\n unknown\n >;\n const authorizationEndpoint = requiredMetadataString(\n metadata,\n \"authorization_endpoint\",\n );\n const tokenEndpoint = requiredMetadataString(metadata, \"token_endpoint\");\n const jwksUri = requiredMetadataString(metadata, \"jwks_uri\");\n const userinfoEndpoint =\n typeof metadata.userinfo_endpoint === \"string\" &&\n metadata.userinfo_endpoint\n ? metadata.userinfo_endpoint\n : undefined;\n\n for (const endpoint of [\n authorizationEndpoint,\n tokenEndpoint,\n jwksUri,\n ...(userinfoEndpoint ? [userinfoEndpoint] : []),\n ]) {\n await validateEndpoint(endpoint, safety);\n }\n\n const issuer =\n typeof metadata.issuer === \"string\" && metadata.issuer\n ? metadata.issuer\n : config.issuer;\n const scopesSupported = Array.isArray(metadata.scopes_supported)\n ? metadata.scopes_supported.filter(\n (scope): scope is string => typeof scope === \"string\",\n )\n : undefined;\n\n return {\n issuer,\n authorizationEndpoint,\n tokenEndpoint,\n jwksUri,\n ...(userinfoEndpoint ? { userinfoEndpoint } : {}),\n ...(scopesSupported ? { scopesSupported } : {}),\n };\n}\n","import {\n AuthError,\n type AuthChallenge,\n type AuthProvider,\n type AuthProviderDescriptor,\n type AuthRequestContext,\n type Principal,\n} from \"@axiom-lattice/protocols\";\nimport type { Configuration } from \"openid-client\";\nimport type { OidcConfig } from \"./config\";\nimport { oidcDescriptor } from \"./config\";\nimport {\n OIDC_STATE_COOKIE,\n signStateCookie,\n stateCookie,\n verifyStateCookie,\n} from \"./stateCookie\";\nimport { assertSafeOidcEndpoint } from \"./endpointSafety\";\nimport { loadOpenidClient, type OidcClientLike } from \"./openidClient\";\n\nexport interface OidcAuthProviderOptions {\n config: OidcConfig;\n /** Provider id that started the flow; signed into and verified from state. */\n providerId: string;\n client?: OidcClientLike;\n now?: () => number;\n /** Injectable for offline tests; defaults to the DNS-checking validator. */\n validateEndpoint?: (\n endpoint: string,\n options: { allowInsecure: boolean },\n ) => Promise<URL>;\n}\n\nfunction stringClaim(\n claims: Record<string, unknown>,\n ...names: string[]\n): string | undefined {\n for (const name of names) {\n const value = claims[name];\n if (typeof value === \"string\" && value.trim()) return value;\n }\n return undefined;\n}\n\nfunction providerError(message: string, cause: unknown): AuthError {\n const error = new AuthError(\"PROVIDER_ERROR\", 502, message);\n if (cause !== undefined) {\n Object.defineProperty(error, \"cause\", {\n value: cause,\n enumerable: false,\n configurable: true,\n writable: true,\n });\n }\n return error;\n}\n\nexport function principalFromClaims(\n claims: Record<string, unknown>,\n): Principal {\n const subject = stringClaim(claims, \"oid\", \"sub\");\n if (!subject) {\n throw new AuthError(\"PROVIDER_ERROR\", 502, \"OIDC ID token has no subject\");\n }\n const emailClaim = stringClaim(claims, \"email\");\n const rawEmail =\n emailClaim ?? stringClaim(claims, \"preferred_username\", \"upn\");\n const email = rawEmail?.trim().toLowerCase();\n const name = stringClaim(claims, \"name\");\n const picture = stringClaim(claims, \"picture\");\n return {\n subject,\n method: \"oidc\",\n ...(email ? { email } : {}),\n ...(email\n ? { emailVerified: emailClaim !== undefined && claims.email_verified === true }\n : {}),\n ...(name ? { name } : {}),\n ...(picture ? { picture } : {}),\n claims,\n };\n}\n\nexport class OidcAuthProvider implements AuthProvider {\n readonly descriptor: AuthProviderDescriptor;\n private configurationPromise?: Promise<Configuration>;\n\n constructor(private readonly options: OidcAuthProviderOptions) {\n this.descriptor = oidcDescriptor(options.config);\n }\n\n private async getClient(): Promise<OidcClientLike> {\n return this.options.client ?? (await loadOpenidClient());\n }\n\n private async getConfiguration(client: OidcClientLike): Promise<Configuration> {\n if (!this.configurationPromise) {\n this.configurationPromise = (async () => {\n try {\n const validateEndpoint =\n this.options.validateEndpoint ?? assertSafeOidcEndpoint;\n const safety = {\n allowInsecure: this.options.config.allowInsecure,\n allowedPrivateHosts:\n this.options.config.allowedPrivateHosts ?? [],\n };\n await validateEndpoint(this.options.config.issuer, safety);\n const discovered = await client.discovery(\n new URL(this.options.config.issuer),\n this.options.config.clientId,\n this.options.config.clientSecret,\n {\n timeoutSeconds: 5,\n allowInsecure: this.options.config.allowInsecure,\n },\n );\n const metadata = discovered.serverMetadata();\n const { authorization_endpoint, token_endpoint, jwks_uri } = metadata;\n if (\n typeof authorization_endpoint !== \"string\" ||\n !authorization_endpoint ||\n typeof token_endpoint !== \"string\" ||\n !token_endpoint ||\n typeof jwks_uri !== \"string\" ||\n !jwks_uri\n ) {\n throw new Error(\"OIDC discovery returned incomplete metadata\");\n }\n for (const endpoint of [\n authorization_endpoint,\n token_endpoint,\n jwks_uri,\n ]) {\n await validateEndpoint(endpoint, safety);\n }\n return discovered;\n } catch (error) {\n throw providerError(\"OIDC discovery failed\", error);\n }\n })().catch((error: unknown) => {\n this.configurationPromise = undefined;\n throw error;\n });\n }\n return this.configurationPromise;\n }\n\n async begin(): Promise<AuthChallenge> {\n try {\n const client = await this.getClient();\n const config = await this.getConfiguration(client);\n const state = client.randomState();\n const codeVerifier = client.randomPKCECodeVerifier();\n const codeChallenge = await client.calculatePKCECodeChallenge(codeVerifier);\n const nonce = client.randomNonce();\n const url = client.buildAuthorizationUrl(config, {\n redirect_uri: this.options.config.redirectUri,\n response_type: \"code\",\n scope: this.options.config.scopes,\n state,\n nonce,\n code_challenge: codeChallenge,\n code_challenge_method: \"S256\",\n });\n const now = this.options.now?.() ?? Date.now();\n const cookieValue = signStateCookie(\n {\n state,\n codeVerifier,\n nonce,\n providerId: this.options.providerId,\n exp: now + 600_000,\n },\n this.options.config.stateSecret,\n );\n return {\n kind: \"redirect\",\n url: url.href,\n cookies: [\n stateCookie(cookieValue, { secure: !this.options.config.insecureCookies }),\n ],\n };\n } catch (error) {\n if (error instanceof AuthError) throw error;\n throw providerError(\"OIDC authorization request failed\", error);\n }\n }\n\n async callback(ctx: AuthRequestContext): Promise<Principal> {\n try {\n const rawCookie = ctx.cookies?.[OIDC_STATE_COOKIE];\n if (!rawCookie) {\n throw new AuthError(\"INVALID_STATE\", 400, \"OIDC state cookie is missing\");\n }\n const now = this.options.now?.() ?? Date.now();\n const stored = verifyStateCookie(\n rawCookie,\n this.options.config.stateSecret,\n now,\n );\n if (!stored) {\n throw new AuthError(\n \"INVALID_STATE\",\n 400,\n \"OIDC state cookie is invalid or expired\",\n );\n }\n if (stored.providerId !== this.options.providerId) {\n throw new AuthError(\n \"INVALID_STATE\",\n 400,\n \"OIDC state provider does not match\",\n );\n }\n if (ctx.params?.state !== stored.state) {\n throw new AuthError(\"INVALID_STATE\", 400, \"OIDC state does not match\");\n }\n\n const client = await this.getClient();\n const config = await this.getConfiguration(client);\n const currentUrl = new URL(this.options.config.redirectUri);\n for (const [key, value] of Object.entries(ctx.params ?? {})) {\n currentUrl.searchParams.set(key, value);\n }\n\n const tokens = await client.authorizationCodeGrant(config, currentUrl, {\n pkceCodeVerifier: stored.codeVerifier,\n expectedState: stored.state,\n expectedNonce: stored.nonce,\n });\n const claims = tokens.claims();\n if (!claims) {\n throw new AuthError(\"PROVIDER_ERROR\", 502, \"OIDC ID token is missing\");\n }\n return principalFromClaims(claims);\n } catch (error) {\n if (error instanceof AuthError) throw error;\n throw providerError(\"OIDC token exchange failed\", error);\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/endpointSafety.ts","../src/config.ts","../src/stateCookie.ts","../src/openidClient.ts","../src/probe.ts","../src/OidcAuthProvider.ts"],"sourcesContent":["export * from \"./config\";\nexport * from \"./stateCookie\";\nexport * from \"./endpointSafety\";\nexport * from \"./openidClient\";\nexport * from \"./probe\";\nexport * from \"./OidcAuthProvider\";\n","import { lookup } from \"node:dns/promises\";\nimport { isIP } from \"node:net\";\n\nexport interface EndpointSafetyOptions {\n allowInsecure: boolean;\n /**\n * Exact hostnames allowed to resolve to a private address.\n *\n * This is intentionally narrower than the general SSRF guard: it does not\n * allow HTTP, userinfo, query strings, or other private hosts. Deployments\n * that must reach a private OIDC issuer through a VPN/fake-IP DNS should list\n * only that issuer host here.\n */\n allowedPrivateHosts?: readonly string[];\n}\n\nfunction hostAddress(hostname: string): string {\n return hostname.replace(/^\\[/, \"\").replace(/\\]$/, \"\");\n}\n\nfunction normalizeHostEntry(raw: string): string | undefined {\n const trimmed = raw.trim();\n if (!trimmed) return undefined;\n let host = trimmed;\n if (trimmed.includes(\"://\")) {\n try {\n host = new URL(trimmed).hostname;\n } catch {\n return undefined;\n }\n }\n return hostAddress(host).toLowerCase().replace(/\\.$/, \"\");\n}\n\n/**\n * Parses `OIDC_ALLOWED_PRIVATE_HOSTS` into a normalized, exact hostname list.\n *\n * Entries are comma-separated. Surrounding whitespace, case, IPv6 brackets,\n * and trailing dots are normalized; schemes are accepted for convenience.\n * Invalid/empty entries are ignored rather than failing startup, so a typo\n * cannot silently widen access to a different host.\n */\nexport function parseAllowedPrivateHosts(\n value: string | undefined,\n): string[] {\n if (!value) return [];\n const hosts = new Set<string>();\n for (const entry of value.split(\",\")) {\n const host = normalizeHostEntry(entry);\n if (host) hosts.add(host);\n }\n return [...hosts];\n}\n\nfunction isAllowedPrivateHost(\n hostname: string,\n options: EndpointSafetyOptions,\n): boolean {\n const host = normalizeHostEntry(hostname);\n if (!host) return false;\n return (options.allowedPrivateHosts ?? []).some(\n (entry) => normalizeHostEntry(entry) === host,\n );\n}\n\nfunction isLocalHostname(hostname: string): boolean {\n const host = hostAddress(hostname);\n return host === \"localhost\" || host === \"127.0.0.1\" || host === \"::1\";\n}\n\nfunction isPrivateIPv4(ip: string): boolean {\n const parts = ip.split(\".\").map(Number);\n if (parts.length !== 4 || parts.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) {\n return true;\n }\n const [a, b, c] = parts;\n return (\n a === 0 ||\n a === 10 ||\n a === 127 ||\n (a === 169 && b === 254) ||\n (a === 172 && b >= 16 && b <= 31) ||\n (a === 192 && b === 168) ||\n (a === 192 && b === 0 && c === 0) ||\n (a === 192 && b === 0 && c === 2) ||\n (a === 198 && b === 51 && c === 100) ||\n (a === 203 && b === 0 && c === 113) ||\n (a === 100 && b >= 64 && b <= 127) ||\n (a === 198 && (b === 18 || b === 19)) ||\n a >= 224\n );\n}\n\nconst IPV6_8_BITS = 8;\nconst IPV6_GROUPS = 8;\n\nfunction ipv6Bytes(ip: string): number[] | null {\n let value = ip.toLowerCase();\n const zone = value.indexOf(\"%\");\n if (zone !== -1) value = value.slice(0, zone);\n\n const lastColon = value.lastIndexOf(\":\");\n if (lastColon !== -1 && value.slice(lastColon + 1).includes(\".\")) {\n const ipv4 = value.slice(lastColon + 1).split(\".\").map(Number);\n if (\n ipv4.length !== 4 ||\n ipv4.some((n) => !Number.isInteger(n) || n < 0 || n > 255)\n ) {\n return null;\n }\n const high = ((ipv4[0] << 8) | ipv4[1]).toString(16);\n const low = ((ipv4[2] << 8) | ipv4[3]).toString(16);\n value = `${value.slice(0, lastColon + 1)}${high}:${low}`;\n }\n\n const halves = value.split(\"::\");\n if (halves.length > 2) return null;\n const head = halves[0] ? halves[0].split(\":\") : [];\n const tail = halves.length === 2 && halves[1] ? halves[1].split(\":\") : [];\n let groups: string[];\n if (halves.length === 2) {\n const missing = IPV6_GROUPS - head.length - tail.length;\n if (missing < 1) return null;\n groups = [...head, ...Array(missing).fill(\"0\"), ...tail];\n } else {\n groups = head;\n }\n if (groups.length !== IPV6_GROUPS) return null;\n\n const bytes: number[] = [];\n for (const group of groups) {\n if (!/^[0-9a-f]{1,4}$/.test(group)) return null;\n const part = Number.parseInt(group, 16);\n bytes.push((part >> IPV6_8_BITS) & 0xff, part & 0xff);\n }\n return bytes;\n}\n\nfunction hasPrefix(bytes: number[], prefix: number[]): boolean {\n return prefix.every((value, index) => bytes[index] === value);\n}\n\nfunction isPrivateIPv6(ip: string): boolean {\n const bytes = ipv6Bytes(ip);\n if (!bytes) return true;\n if (bytes.every((value) => value === 0)) return true;\n const loopback = bytes.slice(0, 15).every((value) => value === 0) && bytes[15] === 1;\n if (loopback) return true;\n if ((bytes[0] & 0xfe) === 0xfc) return true;\n if (bytes[0] === 0xfe && (bytes[1] & 0xc0) === 0x80) return true;\n if (bytes[0] === 0xff) return true;\n if (\n bytes[0] === 0 &&\n bytes[1] === 0 &&\n bytes[2] === 0 &&\n bytes[3] === 0 &&\n bytes[4] === 0 &&\n bytes[5] === 0 &&\n bytes[6] === 0 &&\n bytes[7] === 0 &&\n bytes[8] === 0 &&\n bytes[9] === 0 &&\n bytes[10] === 0xff &&\n bytes[11] === 0xff\n ) {\n return isPrivateIPv4(\n `${bytes[12]}.${bytes[13]}.${bytes[14]}.${bytes[15]}`,\n );\n }\n if (hasPrefix(bytes, [0x00, 0x64, 0xff, 0x9b, 0, 0, 0, 0, 0, 0, 0, 0])) {\n return true;\n }\n if (hasPrefix(bytes, [0x00, 0x64, 0xff, 0x9b, 0x00, 0x01])) return true;\n if (hasPrefix(bytes, [0x20, 0x02])) return true;\n if (hasPrefix(bytes, [0x20, 0x01, 0x00, 0x00])) return true;\n if (hasPrefix(bytes, [0x20, 0x01, 0x0d, 0xb8])) return true;\n if (hasPrefix(bytes, [0x20, 0x01, 0x00, 0x02, 0x00, 0x00])) return true;\n if (hasPrefix(bytes, [0x01, 0x00, 0, 0, 0, 0, 0, 0])) return true;\n return false;\n}\n\nfunction isPrivateAddress(ip: string): boolean {\n const version = isIP(ip);\n if (version === 4) return isPrivateIPv4(ip);\n if (version === 6) return isPrivateIPv6(ip);\n return true;\n}\n\n/**\n * Addresses that must never be reachable even through the private-host\n * allowlist: cloud metadata/link-local and the unspecified address.\n *\n * Loopback is intentionally excluded so an intentional `127.0.0.1` / `::1`\n * allowlist entry keeps working for local development.\n */\nexport function isLinkLocalOrMetadataAddress(address: string): boolean {\n const ip = hostAddress(address);\n const version = isIP(ip);\n if (version === 4) {\n const parts = ip.split(\".\").map(Number);\n if (\n parts.length !== 4 ||\n parts.some((n) => !Number.isInteger(n) || n < 0 || n > 255)\n ) {\n return false;\n }\n return parts[0] === 0 || (parts[0] === 169 && parts[1] === 254);\n }\n if (version !== 6) return false;\n const bytes = ipv6Bytes(ip);\n if (!bytes) return false;\n if (bytes.every((value) => value === 0)) return true;\n if (bytes[0] === 0xfe && (bytes[1] & 0xc0) === 0x80) return true;\n const ipv4Mapped =\n bytes.slice(0, 10).every((value) => value === 0) &&\n bytes[10] === 0xff &&\n bytes[11] === 0xff;\n if (ipv4Mapped) {\n return isLinkLocalOrMetadataAddress(\n `${bytes[12]}.${bytes[13]}.${bytes[14]}.${bytes[15]}`,\n );\n }\n return false;\n}\n\nexport function assertSafeEndpointSyntax(\n raw: string,\n options: EndpointSafetyOptions,\n): URL {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(\"OIDC endpoint must be an absolute URL\");\n }\n if (url.username || url.password) {\n throw new Error(\"OIDC endpoint must not contain userinfo\");\n }\n if (url.search || url.hash) {\n throw new Error(\"OIDC endpoint must not contain query or fragment\");\n }\n const host = hostAddress(url.hostname);\n if (url.protocol !== \"https:\") {\n const localMock =\n options.allowInsecure && url.protocol === \"http:\" && isLocalHostname(host);\n if (!localMock) throw new Error(\"OIDC endpoint must use https\");\n }\n if (isLocalHostname(host) && !options.allowInsecure) {\n throw new Error(\"OIDC endpoint must not target localhost\");\n }\n if (isIP(host) && isLinkLocalOrMetadataAddress(host)) {\n throw new Error(\n \"OIDC endpoint must not target a link-local or metadata address\",\n );\n }\n if (isIP(host) && isPrivateAddress(host)) {\n if (\n !(options.allowInsecure && isLocalHostname(host)) &&\n !isAllowedPrivateHost(host, options)\n ) {\n throw new Error(\"OIDC endpoint must not target a private address\");\n }\n }\n return url;\n}\n\nexport async function assertSafeOidcEndpoint(\n raw: string,\n options: EndpointSafetyOptions,\n): Promise<URL> {\n const url = assertSafeEndpointSyntax(raw, options);\n const host = hostAddress(url.hostname);\n const allowlisted = isAllowedPrivateHost(host, options);\n if ((options.allowInsecure && isLocalHostname(host)) || isIP(host)) {\n return url;\n }\n const addresses = await lookup(host, { all: true, verbatim: true });\n if (addresses.length === 0) throw new Error(\"OIDC endpoint host did not resolve\");\n for (const { address } of addresses) {\n if (isLinkLocalOrMetadataAddress(address)) {\n throw new Error(\n \"OIDC endpoint host resolves to a link-local or metadata address\",\n );\n }\n if (isPrivateAddress(address) && !allowlisted) {\n throw new Error(\"OIDC endpoint host resolves to a private address\");\n }\n }\n return url;\n}\n","import type { AuthProviderDescriptor } from \"@axiom-lattice/protocols\";\nimport { parseAllowedPrivateHosts } from \"./endpointSafety\";\n\nexport interface OidcConfig {\n issuer: string;\n clientId: string;\n clientSecret: string;\n redirectUri: string;\n postLoginRedirectUri: string;\n scopes: string;\n displayName: string;\n defaultTenantId?: string;\n jitProvisioning: boolean;\n autoLinkEmail: boolean;\n allowInsecure: boolean;\n insecureCookies: boolean;\n stateSecret: string;\n /** Exact hosts allowed to resolve to a private address (deployment-wide). */\n allowedPrivateHosts?: string[];\n}\n\nexport interface ReadOidcConfigOptions {\n stateSecret?: string;\n nodeEnv?: string;\n}\n\nfunction required(env: NodeJS.ProcessEnv, name: string): string {\n const value = env[name]?.trim();\n if (!value) throw new Error(`${name} is required when OIDC_ENABLED=true`);\n return value;\n}\n\nfunction boolean(value: string | undefined, fallback = false): boolean {\n if (value === undefined || value.trim() === \"\") return fallback;\n return value === \"true\";\n}\n\nfunction isLocalHost(hostname: string): boolean {\n const host = hostname.replace(/^\\[/, \"\").replace(/\\]$/, \"\");\n return host === \"localhost\" || host === \"127.0.0.1\" || host === \"::1\";\n}\n\nfunction assertNoCredentialsQueryOrFragment(url: URL, name: string): void {\n if (url.username || url.password) {\n throw new Error(`${name} must not contain userinfo`);\n }\n if (url.search || url.hash) {\n throw new Error(`${name} must not contain query or fragment`);\n }\n}\n\n/**\n * Rejects the Azure multi-tenant issuers that cannot be used for identity\n * binding. Shared by the env config reader and runtime store-provider\n * validation so both paths enforce the same rule.\n *\n * @param url - Parsed issuer URL.\n * @param name - Setting name used in the error message.\n */\nexport function assertTenantSpecificAzureIssuer(\n url: URL,\n name = \"OIDC_ISSUER\",\n): void {\n if (url.hostname.toLowerCase() !== \"login.microsoftonline.com\") return;\n const firstSegment = url.pathname.split(\"/\").filter(Boolean)[0]?.toLowerCase();\n if (firstSegment === \"common\" || firstSegment === \"organizations\") {\n throw new Error(\n `${name} must be tenant-specific; ` +\n \"login.microsoftonline.com/common and /organizations are not supported\",\n );\n }\n}\n\nfunction parseIssuer(raw: string, allowInsecure: boolean): string {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(\"OIDC_ISSUER must be an absolute URL\");\n }\n assertNoCredentialsQueryOrFragment(url, \"OIDC_ISSUER\");\n assertTenantSpecificAzureIssuer(url);\n if (url.protocol !== \"https:\") {\n if (!(allowInsecure && url.protocol === \"http:\" && isLocalHost(url.hostname))) {\n throw new Error(\"OIDC_ISSUER must use https\");\n }\n }\n return url.toString().replace(/\\/$/, \"\");\n}\n\nfunction parseRedirect(raw: string, nodeEnv: string): string {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(\"OIDC redirect URLs must be absolute URLs\");\n }\n assertNoCredentialsQueryOrFragment(url, \"OIDC redirect URLs\");\n if (url.protocol !== \"https:\") {\n const localAllowed =\n (nodeEnv === \"test\" || nodeEnv === \"development\") &&\n url.protocol === \"http:\" &&\n isLocalHost(url.hostname);\n if (!localAllowed) {\n throw new Error(\"OIDC redirect URLs must use https outside test/development\");\n }\n }\n return url.toString();\n}\n\nexport function readOidcConfig(\n env: NodeJS.ProcessEnv = process.env,\n options: ReadOidcConfigOptions = {},\n): OidcConfig | null {\n if (!boolean(env.OIDC_ENABLED)) return null;\n\n const nodeEnv = options.nodeEnv ?? env.NODE_ENV ?? \"production\";\n const allowInsecure = boolean(env.OIDC_ALLOW_INSECURE);\n if (allowInsecure && nodeEnv !== \"test\" && nodeEnv !== \"development\") {\n throw new Error(\"OIDC_ALLOW_INSECURE is only allowed when NODE_ENV=test|development\");\n }\n\n const issuer = parseIssuer(required(env, \"OIDC_ISSUER\"), allowInsecure);\n const redirectUri = parseRedirect(required(env, \"OIDC_REDIRECT_URI\"), nodeEnv);\n const postLoginRedirectUri = parseRedirect(\n required(env, \"OIDC_POST_LOGIN_REDIRECT_URI\"),\n nodeEnv,\n );\n const jitProvisioning = boolean(env.OIDC_JIT_PROVISIONING);\n const defaultTenantId = env.OIDC_DEFAULT_TENANT_ID?.trim() || undefined;\n\n const stateSecret = (\n options.stateSecret ??\n env.AUTH_TOKEN_SECRET ??\n env.JWT_SECRET\n )?.trim();\n if (!stateSecret) {\n throw new Error(\"AUTH_TOKEN_SECRET or JWT_SECRET is required for OIDC state signing\");\n }\n if (stateSecret.length < 16) {\n throw new Error(\n \"AUTH_TOKEN_SECRET or JWT_SECRET must be at least 16 characters for OIDC state signing\",\n );\n }\n\n const allowedPrivateHosts = parseAllowedPrivateHosts(\n env.OIDC_ALLOWED_PRIVATE_HOSTS,\n );\n\n return {\n issuer,\n clientId: required(env, \"OIDC_CLIENT_ID\"),\n clientSecret: env.OIDC_CLIENT_SECRET?.trim() ?? \"\",\n redirectUri,\n postLoginRedirectUri,\n scopes: env.OIDC_SCOPES?.trim() || \"openid profile email\",\n displayName: env.OIDC_DISPLAY_NAME?.trim() || \"Microsoft\",\n ...(defaultTenantId ? { defaultTenantId } : {}),\n jitProvisioning,\n autoLinkEmail: boolean(env.OIDC_AUTO_LINK_EMAIL),\n allowInsecure,\n insecureCookies: redirectUri.startsWith(\"http://\"),\n stateSecret,\n ...(allowedPrivateHosts.length ? { allowedPrivateHosts } : {}),\n };\n}\n\nexport function oidcDescriptor(config: OidcConfig): AuthProviderDescriptor {\n return {\n id: \"oidc\",\n displayName: config.displayName,\n interaction: \"redirect\",\n surfaces: [\"console\"],\n };\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport type { AuthCookie } from \"@axiom-lattice/protocols\";\n\nexport const OIDC_STATE_COOKIE = \"axiom_oidc_state\";\n/**\n * Shared by every provider route so the same cookie is readable for both\n * `/api/auth/login/:providerId` and `/api/auth/login/:providerId/callback`.\n */\nexport const OIDC_STATE_PATH = \"/api/auth/login\";\n\nexport interface OidcStatePayload {\n state: string;\n codeVerifier: string;\n nonce: string;\n /** Provider that started the flow; the callback rejects a mismatch. */\n providerId: string;\n exp: number;\n}\n\nfunction isPayload(value: unknown): value is OidcStatePayload {\n if (typeof value !== \"object\" || value === null) return false;\n const payload = value as Record<string, unknown>;\n return (\n typeof payload.state === \"string\" &&\n typeof payload.codeVerifier === \"string\" &&\n typeof payload.nonce === \"string\" &&\n typeof payload.providerId === \"string\" &&\n payload.providerId.length > 0 &&\n typeof payload.exp === \"number\" &&\n Number.isFinite(payload.exp)\n );\n}\n\nfunction stateKey(secret: string): Buffer {\n return createHmac(\"sha256\", secret).update(\"axiom-oidc-state-v1\").digest();\n}\n\nexport function signStateCookie(\n payload: OidcStatePayload,\n secret: string,\n): string {\n const encoded = Buffer.from(JSON.stringify(payload)).toString(\"base64url\");\n const signature = createHmac(\"sha256\", stateKey(secret))\n .update(encoded)\n .digest(\"base64url\");\n return `${encoded}.${signature}`;\n}\n\nexport function verifyStateCookie(\n value: string,\n secret: string,\n now: number = Date.now(),\n): OidcStatePayload | null {\n const [encoded, signature, extra] = value.split(\".\");\n if (!encoded || !signature || extra !== undefined) return null;\n const expected = createHmac(\"sha256\", stateKey(secret)).update(encoded).digest();\n let actual: Buffer;\n try {\n actual = Buffer.from(signature, \"base64url\");\n } catch {\n return null;\n }\n if (\n actual.length !== expected.length ||\n actual.toString(\"base64url\") !== signature ||\n !timingSafeEqual(actual, expected)\n ) {\n return null;\n }\n try {\n const payload: unknown = JSON.parse(\n Buffer.from(encoded, \"base64url\").toString(\"utf8\"),\n );\n if (!isPayload(payload) || payload.exp < now) return null;\n return payload;\n } catch {\n return null;\n }\n}\n\nexport function stateCookie(\n value: string,\n options: { secure: boolean; maxAge?: number },\n): AuthCookie {\n return {\n name: OIDC_STATE_COOKIE,\n value,\n options: {\n httpOnly: true,\n secure: options.secure,\n sameSite: \"lax\",\n path: OIDC_STATE_PATH,\n maxAge: options.maxAge ?? 600,\n },\n };\n}\n\nexport function clearStateCookie(options: { secure: boolean }): AuthCookie {\n return {\n name: OIDC_STATE_COOKIE,\n value: \"\",\n options: {\n httpOnly: true,\n secure: options.secure,\n sameSite: \"lax\",\n path: OIDC_STATE_PATH,\n maxAge: 0,\n },\n };\n}\n\nexport function serializeCookie(cookie: AuthCookie): string {\n const parts = [`${cookie.name}=${encodeURIComponent(cookie.value)}`];\n if (cookie.options.httpOnly) parts.push(\"HttpOnly\");\n if (cookie.options.secure) parts.push(\"Secure\");\n if (cookie.options.sameSite) {\n parts.push(\n `SameSite=${cookie.options.sameSite[0].toUpperCase()}${cookie.options.sameSite.slice(1)}`,\n );\n }\n if (cookie.options.path) parts.push(`Path=${cookie.options.path}`);\n if (cookie.options.maxAge !== undefined) parts.push(`Max-Age=${cookie.options.maxAge}`);\n return parts.join(\"; \");\n}\n\nexport function readCookie(header: string | undefined, name: string): string | undefined {\n if (!header) return undefined;\n for (const part of header.split(\";\")) {\n const [rawName, ...rest] = part.trim().split(\"=\");\n if (rawName === name) {\n try {\n return decodeURIComponent(rest.join(\"=\"));\n } catch (error) {\n if (error instanceof URIError) return undefined;\n throw error;\n }\n }\n }\n return undefined;\n}\n","import type { Configuration } from \"openid-client\";\n\nexport interface OidcDiscoveryOptions {\n timeoutSeconds: number;\n allowInsecure: boolean;\n}\n\nexport interface OidcClientLike {\n discovery(\n issuer: URL,\n clientId: string,\n clientSecret: string,\n options: OidcDiscoveryOptions,\n ): Promise<Configuration>;\n randomState(): string;\n randomPKCECodeVerifier(): string;\n calculatePKCECodeChallenge(verifier: string): Promise<string>;\n randomNonce(): string;\n buildAuthorizationUrl(\n config: Configuration,\n parameters: Record<string, string>,\n ): URL;\n authorizationCodeGrant(\n config: Configuration,\n currentUrl: URL,\n checks: {\n pkceCodeVerifier: string;\n expectedState: string;\n expectedNonce: string;\n },\n ): Promise<{ claims(): Record<string, unknown> | undefined }>;\n}\n\n/** Load the ESM-only `openid-client` package from CJS consumers. */\nexport async function loadOpenidClient(): Promise<OidcClientLike> {\n const mod = await import(\"openid-client\");\n return {\n discovery: (issuer, clientId, clientSecret, options) => {\n const execute = options.allowInsecure ? [mod.allowInsecureRequests] : undefined;\n return mod.discovery(issuer, clientId, clientSecret, undefined, {\n timeout: options.timeoutSeconds,\n ...(execute ? { execute } : {}),\n });\n },\n randomState: () => mod.randomState(),\n randomPKCECodeVerifier: () => mod.randomPKCECodeVerifier(),\n calculatePKCECodeChallenge: (verifier) => mod.calculatePKCECodeChallenge(verifier),\n randomNonce: () => mod.randomNonce(),\n buildAuthorizationUrl: (config, parameters) =>\n mod.buildAuthorizationUrl(config, parameters),\n authorizationCodeGrant: async (config, currentUrl, checks) => {\n const tokens = await mod.authorizationCodeGrant(config, currentUrl, checks);\n return {\n claims: () => tokens.claims() as unknown as Record<string, unknown> | undefined,\n };\n },\n };\n}\n","import {\n assertSafeOidcEndpoint,\n type EndpointSafetyOptions,\n} from \"./endpointSafety\";\nimport { loadOpenidClient, type OidcClientLike } from \"./openidClient\";\n\n/** Input needed to run OIDC discovery without starting a login flow. */\nexport interface OidcProbeConfig {\n issuer: string;\n clientId: string;\n clientSecret: string;\n allowInsecure: boolean;\n /** Exact hostnames allowed to resolve to a private address. */\n allowedPrivateHosts?: string[];\n /** Injectable client for offline tests; defaults to `openid-client`. */\n client?: OidcClientLike;\n /** Injectable endpoint validator for offline tests. */\n validateEndpoint?: (\n endpoint: string,\n options: EndpointSafetyOptions,\n ) => Promise<URL>;\n}\n\n/** Safe metadata summary returned by {@link probeOidcProvider}. */\nexport interface OidcProviderMetadataSummary {\n issuer: string;\n authorizationEndpoint: string;\n tokenEndpoint: string;\n jwksUri: string;\n userinfoEndpoint?: string;\n scopesSupported?: string[];\n}\n\nfunction requiredMetadataString(\n metadata: Record<string, unknown>,\n key: string,\n): string {\n const value = metadata[key];\n if (typeof value !== \"string\" || !value) {\n throw new Error(\"OIDC discovery returned incomplete metadata\");\n }\n return value;\n}\n\n/**\n * Runs OIDC discovery and validates the issuer plus every endpoint the provider\n * advertises. This never sets cookies or touches session state.\n *\n * @param config - Issuer, client credentials, and optional test seams.\n * @returns A safe metadata summary for the admin test endpoint.\n */\nexport async function probeOidcProvider(\n config: OidcProbeConfig,\n): Promise<OidcProviderMetadataSummary> {\n const client = config.client ?? (await loadOpenidClient());\n const validateEndpoint = config.validateEndpoint ?? assertSafeOidcEndpoint;\n const safety = {\n allowInsecure: config.allowInsecure,\n allowedPrivateHosts: config.allowedPrivateHosts ?? [],\n };\n\n await validateEndpoint(config.issuer, safety);\n const discovered = await client.discovery(\n new URL(config.issuer),\n config.clientId,\n config.clientSecret,\n { timeoutSeconds: 5, allowInsecure: config.allowInsecure },\n );\n const metadata = discovered.serverMetadata() as unknown as Record<\n string,\n unknown\n >;\n const authorizationEndpoint = requiredMetadataString(\n metadata,\n \"authorization_endpoint\",\n );\n const tokenEndpoint = requiredMetadataString(metadata, \"token_endpoint\");\n const jwksUri = requiredMetadataString(metadata, \"jwks_uri\");\n const userinfoEndpoint =\n typeof metadata.userinfo_endpoint === \"string\" &&\n metadata.userinfo_endpoint\n ? metadata.userinfo_endpoint\n : undefined;\n\n for (const endpoint of [\n authorizationEndpoint,\n tokenEndpoint,\n jwksUri,\n ...(userinfoEndpoint ? [userinfoEndpoint] : []),\n ]) {\n await validateEndpoint(endpoint, safety);\n }\n\n const issuer =\n typeof metadata.issuer === \"string\" && metadata.issuer\n ? metadata.issuer\n : config.issuer;\n const scopesSupported = Array.isArray(metadata.scopes_supported)\n ? metadata.scopes_supported.filter(\n (scope): scope is string => typeof scope === \"string\",\n )\n : undefined;\n\n return {\n issuer,\n authorizationEndpoint,\n tokenEndpoint,\n jwksUri,\n ...(userinfoEndpoint ? { userinfoEndpoint } : {}),\n ...(scopesSupported ? { scopesSupported } : {}),\n };\n}\n","import {\n AuthError,\n type AuthChallenge,\n type AuthProvider,\n type AuthProviderDescriptor,\n type AuthRequestContext,\n type Principal,\n} from \"@axiom-lattice/protocols\";\nimport type { Configuration } from \"openid-client\";\nimport type { OidcConfig } from \"./config\";\nimport { oidcDescriptor } from \"./config\";\nimport {\n OIDC_STATE_COOKIE,\n signStateCookie,\n stateCookie,\n verifyStateCookie,\n} from \"./stateCookie\";\nimport { assertSafeOidcEndpoint } from \"./endpointSafety\";\nimport { loadOpenidClient, type OidcClientLike } from \"./openidClient\";\n\nexport interface OidcAuthProviderOptions {\n config: OidcConfig;\n /** Provider id that started the flow; signed into and verified from state. */\n providerId: string;\n client?: OidcClientLike;\n now?: () => number;\n /** Injectable for offline tests; defaults to the DNS-checking validator. */\n validateEndpoint?: (\n endpoint: string,\n options: { allowInsecure: boolean },\n ) => Promise<URL>;\n}\n\nfunction stringClaim(\n claims: Record<string, unknown>,\n ...names: string[]\n): string | undefined {\n for (const name of names) {\n const value = claims[name];\n if (typeof value === \"string\" && value.trim()) return value;\n }\n return undefined;\n}\n\nfunction providerError(message: string, cause: unknown): AuthError {\n const error = new AuthError(\"PROVIDER_ERROR\", 502, message);\n if (cause !== undefined) {\n Object.defineProperty(error, \"cause\", {\n value: cause,\n enumerable: false,\n configurable: true,\n writable: true,\n });\n }\n return error;\n}\n\nexport function principalFromClaims(\n claims: Record<string, unknown>,\n): Principal {\n const subject = stringClaim(claims, \"oid\", \"sub\");\n if (!subject) {\n throw new AuthError(\"PROVIDER_ERROR\", 502, \"OIDC ID token has no subject\");\n }\n const emailClaim = stringClaim(claims, \"email\");\n const rawEmail =\n emailClaim ?? stringClaim(claims, \"preferred_username\", \"upn\");\n const email = rawEmail?.trim().toLowerCase();\n const name = stringClaim(claims, \"name\");\n const picture = stringClaim(claims, \"picture\");\n return {\n subject,\n method: \"oidc\",\n ...(email ? { email } : {}),\n ...(email\n ? { emailVerified: emailClaim !== undefined && claims.email_verified === true }\n : {}),\n ...(name ? { name } : {}),\n ...(picture ? { picture } : {}),\n claims,\n };\n}\n\nexport class OidcAuthProvider implements AuthProvider {\n readonly descriptor: AuthProviderDescriptor;\n private configurationPromise?: Promise<Configuration>;\n\n constructor(private readonly options: OidcAuthProviderOptions) {\n this.descriptor = oidcDescriptor(options.config);\n }\n\n private async getClient(): Promise<OidcClientLike> {\n return this.options.client ?? (await loadOpenidClient());\n }\n\n private async getConfiguration(client: OidcClientLike): Promise<Configuration> {\n if (!this.configurationPromise) {\n this.configurationPromise = (async () => {\n try {\n const validateEndpoint =\n this.options.validateEndpoint ?? assertSafeOidcEndpoint;\n const safety = {\n allowInsecure: this.options.config.allowInsecure,\n allowedPrivateHosts:\n this.options.config.allowedPrivateHosts ?? [],\n };\n await validateEndpoint(this.options.config.issuer, safety);\n const discovered = await client.discovery(\n new URL(this.options.config.issuer),\n this.options.config.clientId,\n this.options.config.clientSecret,\n {\n timeoutSeconds: 5,\n allowInsecure: this.options.config.allowInsecure,\n },\n );\n const metadata = discovered.serverMetadata();\n const { authorization_endpoint, token_endpoint, jwks_uri } = metadata;\n if (\n typeof authorization_endpoint !== \"string\" ||\n !authorization_endpoint ||\n typeof token_endpoint !== \"string\" ||\n !token_endpoint ||\n typeof jwks_uri !== \"string\" ||\n !jwks_uri\n ) {\n throw new Error(\"OIDC discovery returned incomplete metadata\");\n }\n for (const endpoint of [\n authorization_endpoint,\n token_endpoint,\n jwks_uri,\n ]) {\n await validateEndpoint(endpoint, safety);\n }\n return discovered;\n } catch (error) {\n throw providerError(\"OIDC discovery failed\", error);\n }\n })().catch((error: unknown) => {\n this.configurationPromise = undefined;\n throw error;\n });\n }\n return this.configurationPromise;\n }\n\n async begin(): Promise<AuthChallenge> {\n try {\n const client = await this.getClient();\n const config = await this.getConfiguration(client);\n const state = client.randomState();\n const codeVerifier = client.randomPKCECodeVerifier();\n const codeChallenge = await client.calculatePKCECodeChallenge(codeVerifier);\n const nonce = client.randomNonce();\n const url = client.buildAuthorizationUrl(config, {\n redirect_uri: this.options.config.redirectUri,\n response_type: \"code\",\n scope: this.options.config.scopes,\n state,\n nonce,\n code_challenge: codeChallenge,\n code_challenge_method: \"S256\",\n });\n const now = this.options.now?.() ?? Date.now();\n const cookieValue = signStateCookie(\n {\n state,\n codeVerifier,\n nonce,\n providerId: this.options.providerId,\n exp: now + 600_000,\n },\n this.options.config.stateSecret,\n );\n return {\n kind: \"redirect\",\n url: url.href,\n cookies: [\n stateCookie(cookieValue, { secure: !this.options.config.insecureCookies }),\n ],\n };\n } catch (error) {\n if (error instanceof AuthError) throw error;\n throw providerError(\"OIDC authorization request failed\", error);\n }\n }\n\n async callback(ctx: AuthRequestContext): Promise<Principal> {\n try {\n const rawCookie = ctx.cookies?.[OIDC_STATE_COOKIE];\n if (!rawCookie) {\n throw new AuthError(\"INVALID_STATE\", 400, \"OIDC state cookie is missing\");\n }\n const now = this.options.now?.() ?? Date.now();\n const stored = verifyStateCookie(\n rawCookie,\n this.options.config.stateSecret,\n now,\n );\n if (!stored) {\n throw new AuthError(\n \"INVALID_STATE\",\n 400,\n \"OIDC state cookie is invalid or expired\",\n );\n }\n if (stored.providerId !== this.options.providerId) {\n throw new AuthError(\n \"INVALID_STATE\",\n 400,\n \"OIDC state provider does not match\",\n );\n }\n if (ctx.params?.state !== stored.state) {\n throw new AuthError(\"INVALID_STATE\", 400, \"OIDC state does not match\");\n }\n\n const client = await this.getClient();\n const config = await this.getConfiguration(client);\n const currentUrl = new URL(this.options.config.redirectUri);\n for (const [key, value] of Object.entries(ctx.params ?? {})) {\n currentUrl.searchParams.set(key, value);\n }\n\n const tokens = await client.authorizationCodeGrant(config, currentUrl, {\n pkceCodeVerifier: stored.codeVerifier,\n expectedState: stored.state,\n expectedNonce: stored.nonce,\n });\n const claims = tokens.claims();\n if (!claims) {\n throw new AuthError(\"PROVIDER_ERROR\", 502, \"OIDC ID token is missing\");\n }\n return principalFromClaims(claims);\n } catch (error) {\n if (error instanceof AuthError) throw error;\n throw providerError(\"OIDC token exchange failed\", error);\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -287,7 +287,7 @@ function readOidcConfig(env = process.env, options = {}) {
|
|
|
287
287
|
return {
|
|
288
288
|
issuer,
|
|
289
289
|
clientId: required(env, "OIDC_CLIENT_ID"),
|
|
290
|
-
clientSecret:
|
|
290
|
+
clientSecret: env.OIDC_CLIENT_SECRET?.trim() ?? "",
|
|
291
291
|
redirectUri,
|
|
292
292
|
postLoginRedirectUri,
|
|
293
293
|
scopes: env.OIDC_SCOPES?.trim() || "openid profile email",
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/endpointSafety.ts","../src/config.ts","../src/stateCookie.ts","../src/openidClient.ts","../src/probe.ts","../src/OidcAuthProvider.ts"],"sourcesContent":["import { lookup } from \"node:dns/promises\";\nimport { isIP } from \"node:net\";\n\nexport interface EndpointSafetyOptions {\n allowInsecure: boolean;\n /**\n * Exact hostnames allowed to resolve to a private address.\n *\n * This is intentionally narrower than the general SSRF guard: it does not\n * allow HTTP, userinfo, query strings, or other private hosts. Deployments\n * that must reach a private OIDC issuer through a VPN/fake-IP DNS should list\n * only that issuer host here.\n */\n allowedPrivateHosts?: readonly string[];\n}\n\nfunction hostAddress(hostname: string): string {\n return hostname.replace(/^\\[/, \"\").replace(/\\]$/, \"\");\n}\n\nfunction normalizeHostEntry(raw: string): string | undefined {\n const trimmed = raw.trim();\n if (!trimmed) return undefined;\n let host = trimmed;\n if (trimmed.includes(\"://\")) {\n try {\n host = new URL(trimmed).hostname;\n } catch {\n return undefined;\n }\n }\n return hostAddress(host).toLowerCase().replace(/\\.$/, \"\");\n}\n\n/**\n * Parses `OIDC_ALLOWED_PRIVATE_HOSTS` into a normalized, exact hostname list.\n *\n * Entries are comma-separated. Surrounding whitespace, case, IPv6 brackets,\n * and trailing dots are normalized; schemes are accepted for convenience.\n * Invalid/empty entries are ignored rather than failing startup, so a typo\n * cannot silently widen access to a different host.\n */\nexport function parseAllowedPrivateHosts(\n value: string | undefined,\n): string[] {\n if (!value) return [];\n const hosts = new Set<string>();\n for (const entry of value.split(\",\")) {\n const host = normalizeHostEntry(entry);\n if (host) hosts.add(host);\n }\n return [...hosts];\n}\n\nfunction isAllowedPrivateHost(\n hostname: string,\n options: EndpointSafetyOptions,\n): boolean {\n const host = normalizeHostEntry(hostname);\n if (!host) return false;\n return (options.allowedPrivateHosts ?? []).some(\n (entry) => normalizeHostEntry(entry) === host,\n );\n}\n\nfunction isLocalHostname(hostname: string): boolean {\n const host = hostAddress(hostname);\n return host === \"localhost\" || host === \"127.0.0.1\" || host === \"::1\";\n}\n\nfunction isPrivateIPv4(ip: string): boolean {\n const parts = ip.split(\".\").map(Number);\n if (parts.length !== 4 || parts.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) {\n return true;\n }\n const [a, b, c] = parts;\n return (\n a === 0 ||\n a === 10 ||\n a === 127 ||\n (a === 169 && b === 254) ||\n (a === 172 && b >= 16 && b <= 31) ||\n (a === 192 && b === 168) ||\n (a === 192 && b === 0 && c === 0) ||\n (a === 192 && b === 0 && c === 2) ||\n (a === 198 && b === 51 && c === 100) ||\n (a === 203 && b === 0 && c === 113) ||\n (a === 100 && b >= 64 && b <= 127) ||\n (a === 198 && (b === 18 || b === 19)) ||\n a >= 224\n );\n}\n\nconst IPV6_8_BITS = 8;\nconst IPV6_GROUPS = 8;\n\nfunction ipv6Bytes(ip: string): number[] | null {\n let value = ip.toLowerCase();\n const zone = value.indexOf(\"%\");\n if (zone !== -1) value = value.slice(0, zone);\n\n const lastColon = value.lastIndexOf(\":\");\n if (lastColon !== -1 && value.slice(lastColon + 1).includes(\".\")) {\n const ipv4 = value.slice(lastColon + 1).split(\".\").map(Number);\n if (\n ipv4.length !== 4 ||\n ipv4.some((n) => !Number.isInteger(n) || n < 0 || n > 255)\n ) {\n return null;\n }\n const high = ((ipv4[0] << 8) | ipv4[1]).toString(16);\n const low = ((ipv4[2] << 8) | ipv4[3]).toString(16);\n value = `${value.slice(0, lastColon + 1)}${high}:${low}`;\n }\n\n const halves = value.split(\"::\");\n if (halves.length > 2) return null;\n const head = halves[0] ? halves[0].split(\":\") : [];\n const tail = halves.length === 2 && halves[1] ? halves[1].split(\":\") : [];\n let groups: string[];\n if (halves.length === 2) {\n const missing = IPV6_GROUPS - head.length - tail.length;\n if (missing < 1) return null;\n groups = [...head, ...Array(missing).fill(\"0\"), ...tail];\n } else {\n groups = head;\n }\n if (groups.length !== IPV6_GROUPS) return null;\n\n const bytes: number[] = [];\n for (const group of groups) {\n if (!/^[0-9a-f]{1,4}$/.test(group)) return null;\n const part = Number.parseInt(group, 16);\n bytes.push((part >> IPV6_8_BITS) & 0xff, part & 0xff);\n }\n return bytes;\n}\n\nfunction hasPrefix(bytes: number[], prefix: number[]): boolean {\n return prefix.every((value, index) => bytes[index] === value);\n}\n\nfunction isPrivateIPv6(ip: string): boolean {\n const bytes = ipv6Bytes(ip);\n if (!bytes) return true;\n if (bytes.every((value) => value === 0)) return true;\n const loopback = bytes.slice(0, 15).every((value) => value === 0) && bytes[15] === 1;\n if (loopback) return true;\n if ((bytes[0] & 0xfe) === 0xfc) return true;\n if (bytes[0] === 0xfe && (bytes[1] & 0xc0) === 0x80) return true;\n if (bytes[0] === 0xff) return true;\n if (\n bytes[0] === 0 &&\n bytes[1] === 0 &&\n bytes[2] === 0 &&\n bytes[3] === 0 &&\n bytes[4] === 0 &&\n bytes[5] === 0 &&\n bytes[6] === 0 &&\n bytes[7] === 0 &&\n bytes[8] === 0 &&\n bytes[9] === 0 &&\n bytes[10] === 0xff &&\n bytes[11] === 0xff\n ) {\n return isPrivateIPv4(\n `${bytes[12]}.${bytes[13]}.${bytes[14]}.${bytes[15]}`,\n );\n }\n if (hasPrefix(bytes, [0x00, 0x64, 0xff, 0x9b, 0, 0, 0, 0, 0, 0, 0, 0])) {\n return true;\n }\n if (hasPrefix(bytes, [0x00, 0x64, 0xff, 0x9b, 0x00, 0x01])) return true;\n if (hasPrefix(bytes, [0x20, 0x02])) return true;\n if (hasPrefix(bytes, [0x20, 0x01, 0x00, 0x00])) return true;\n if (hasPrefix(bytes, [0x20, 0x01, 0x0d, 0xb8])) return true;\n if (hasPrefix(bytes, [0x20, 0x01, 0x00, 0x02, 0x00, 0x00])) return true;\n if (hasPrefix(bytes, [0x01, 0x00, 0, 0, 0, 0, 0, 0])) return true;\n return false;\n}\n\nfunction isPrivateAddress(ip: string): boolean {\n const version = isIP(ip);\n if (version === 4) return isPrivateIPv4(ip);\n if (version === 6) return isPrivateIPv6(ip);\n return true;\n}\n\n/**\n * Addresses that must never be reachable even through the private-host\n * allowlist: cloud metadata/link-local and the unspecified address.\n *\n * Loopback is intentionally excluded so an intentional `127.0.0.1` / `::1`\n * allowlist entry keeps working for local development.\n */\nexport function isLinkLocalOrMetadataAddress(address: string): boolean {\n const ip = hostAddress(address);\n const version = isIP(ip);\n if (version === 4) {\n const parts = ip.split(\".\").map(Number);\n if (\n parts.length !== 4 ||\n parts.some((n) => !Number.isInteger(n) || n < 0 || n > 255)\n ) {\n return false;\n }\n return parts[0] === 0 || (parts[0] === 169 && parts[1] === 254);\n }\n if (version !== 6) return false;\n const bytes = ipv6Bytes(ip);\n if (!bytes) return false;\n if (bytes.every((value) => value === 0)) return true;\n if (bytes[0] === 0xfe && (bytes[1] & 0xc0) === 0x80) return true;\n const ipv4Mapped =\n bytes.slice(0, 10).every((value) => value === 0) &&\n bytes[10] === 0xff &&\n bytes[11] === 0xff;\n if (ipv4Mapped) {\n return isLinkLocalOrMetadataAddress(\n `${bytes[12]}.${bytes[13]}.${bytes[14]}.${bytes[15]}`,\n );\n }\n return false;\n}\n\nexport function assertSafeEndpointSyntax(\n raw: string,\n options: EndpointSafetyOptions,\n): URL {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(\"OIDC endpoint must be an absolute URL\");\n }\n if (url.username || url.password) {\n throw new Error(\"OIDC endpoint must not contain userinfo\");\n }\n if (url.search || url.hash) {\n throw new Error(\"OIDC endpoint must not contain query or fragment\");\n }\n const host = hostAddress(url.hostname);\n if (url.protocol !== \"https:\") {\n const localMock =\n options.allowInsecure && url.protocol === \"http:\" && isLocalHostname(host);\n if (!localMock) throw new Error(\"OIDC endpoint must use https\");\n }\n if (isLocalHostname(host) && !options.allowInsecure) {\n throw new Error(\"OIDC endpoint must not target localhost\");\n }\n if (isIP(host) && isLinkLocalOrMetadataAddress(host)) {\n throw new Error(\n \"OIDC endpoint must not target a link-local or metadata address\",\n );\n }\n if (isIP(host) && isPrivateAddress(host)) {\n if (\n !(options.allowInsecure && isLocalHostname(host)) &&\n !isAllowedPrivateHost(host, options)\n ) {\n throw new Error(\"OIDC endpoint must not target a private address\");\n }\n }\n return url;\n}\n\nexport async function assertSafeOidcEndpoint(\n raw: string,\n options: EndpointSafetyOptions,\n): Promise<URL> {\n const url = assertSafeEndpointSyntax(raw, options);\n const host = hostAddress(url.hostname);\n const allowlisted = isAllowedPrivateHost(host, options);\n if ((options.allowInsecure && isLocalHostname(host)) || isIP(host)) {\n return url;\n }\n const addresses = await lookup(host, { all: true, verbatim: true });\n if (addresses.length === 0) throw new Error(\"OIDC endpoint host did not resolve\");\n for (const { address } of addresses) {\n if (isLinkLocalOrMetadataAddress(address)) {\n throw new Error(\n \"OIDC endpoint host resolves to a link-local or metadata address\",\n );\n }\n if (isPrivateAddress(address) && !allowlisted) {\n throw new Error(\"OIDC endpoint host resolves to a private address\");\n }\n }\n return url;\n}\n","import type { AuthProviderDescriptor } from \"@axiom-lattice/protocols\";\nimport { parseAllowedPrivateHosts } from \"./endpointSafety\";\n\nexport interface OidcConfig {\n issuer: string;\n clientId: string;\n clientSecret: string;\n redirectUri: string;\n postLoginRedirectUri: string;\n scopes: string;\n displayName: string;\n defaultTenantId?: string;\n jitProvisioning: boolean;\n autoLinkEmail: boolean;\n allowInsecure: boolean;\n insecureCookies: boolean;\n stateSecret: string;\n /** Exact hosts allowed to resolve to a private address (deployment-wide). */\n allowedPrivateHosts?: string[];\n}\n\nexport interface ReadOidcConfigOptions {\n stateSecret?: string;\n nodeEnv?: string;\n}\n\nfunction required(env: NodeJS.ProcessEnv, name: string): string {\n const value = env[name]?.trim();\n if (!value) throw new Error(`${name} is required when OIDC_ENABLED=true`);\n return value;\n}\n\nfunction boolean(value: string | undefined, fallback = false): boolean {\n if (value === undefined || value.trim() === \"\") return fallback;\n return value === \"true\";\n}\n\nfunction isLocalHost(hostname: string): boolean {\n const host = hostname.replace(/^\\[/, \"\").replace(/\\]$/, \"\");\n return host === \"localhost\" || host === \"127.0.0.1\" || host === \"::1\";\n}\n\nfunction assertNoCredentialsQueryOrFragment(url: URL, name: string): void {\n if (url.username || url.password) {\n throw new Error(`${name} must not contain userinfo`);\n }\n if (url.search || url.hash) {\n throw new Error(`${name} must not contain query or fragment`);\n }\n}\n\n/**\n * Rejects the Azure multi-tenant issuers that cannot be used for identity\n * binding. Shared by the env config reader and runtime store-provider\n * validation so both paths enforce the same rule.\n *\n * @param url - Parsed issuer URL.\n * @param name - Setting name used in the error message.\n */\nexport function assertTenantSpecificAzureIssuer(\n url: URL,\n name = \"OIDC_ISSUER\",\n): void {\n if (url.hostname.toLowerCase() !== \"login.microsoftonline.com\") return;\n const firstSegment = url.pathname.split(\"/\").filter(Boolean)[0]?.toLowerCase();\n if (firstSegment === \"common\" || firstSegment === \"organizations\") {\n throw new Error(\n `${name} must be tenant-specific; ` +\n \"login.microsoftonline.com/common and /organizations are not supported\",\n );\n }\n}\n\nfunction parseIssuer(raw: string, allowInsecure: boolean): string {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(\"OIDC_ISSUER must be an absolute URL\");\n }\n assertNoCredentialsQueryOrFragment(url, \"OIDC_ISSUER\");\n assertTenantSpecificAzureIssuer(url);\n if (url.protocol !== \"https:\") {\n if (!(allowInsecure && url.protocol === \"http:\" && isLocalHost(url.hostname))) {\n throw new Error(\"OIDC_ISSUER must use https\");\n }\n }\n return url.toString().replace(/\\/$/, \"\");\n}\n\nfunction parseRedirect(raw: string, nodeEnv: string): string {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(\"OIDC redirect URLs must be absolute URLs\");\n }\n assertNoCredentialsQueryOrFragment(url, \"OIDC redirect URLs\");\n if (url.protocol !== \"https:\") {\n const localAllowed =\n (nodeEnv === \"test\" || nodeEnv === \"development\") &&\n url.protocol === \"http:\" &&\n isLocalHost(url.hostname);\n if (!localAllowed) {\n throw new Error(\"OIDC redirect URLs must use https outside test/development\");\n }\n }\n return url.toString();\n}\n\nexport function readOidcConfig(\n env: NodeJS.ProcessEnv = process.env,\n options: ReadOidcConfigOptions = {},\n): OidcConfig | null {\n if (!boolean(env.OIDC_ENABLED)) return null;\n\n const nodeEnv = options.nodeEnv ?? env.NODE_ENV ?? \"production\";\n const allowInsecure = boolean(env.OIDC_ALLOW_INSECURE);\n if (allowInsecure && nodeEnv !== \"test\" && nodeEnv !== \"development\") {\n throw new Error(\"OIDC_ALLOW_INSECURE is only allowed when NODE_ENV=test|development\");\n }\n\n const issuer = parseIssuer(required(env, \"OIDC_ISSUER\"), allowInsecure);\n const redirectUri = parseRedirect(required(env, \"OIDC_REDIRECT_URI\"), nodeEnv);\n const postLoginRedirectUri = parseRedirect(\n required(env, \"OIDC_POST_LOGIN_REDIRECT_URI\"),\n nodeEnv,\n );\n const jitProvisioning = boolean(env.OIDC_JIT_PROVISIONING);\n const defaultTenantId = env.OIDC_DEFAULT_TENANT_ID?.trim() || undefined;\n\n const stateSecret = (\n options.stateSecret ??\n env.AUTH_TOKEN_SECRET ??\n env.JWT_SECRET\n )?.trim();\n if (!stateSecret) {\n throw new Error(\"AUTH_TOKEN_SECRET or JWT_SECRET is required for OIDC state signing\");\n }\n if (stateSecret.length < 16) {\n throw new Error(\n \"AUTH_TOKEN_SECRET or JWT_SECRET must be at least 16 characters for OIDC state signing\",\n );\n }\n\n const allowedPrivateHosts = parseAllowedPrivateHosts(\n env.OIDC_ALLOWED_PRIVATE_HOSTS,\n );\n\n return {\n issuer,\n clientId: required(env, \"OIDC_CLIENT_ID\"),\n clientSecret: required(env, \"OIDC_CLIENT_SECRET\"),\n redirectUri,\n postLoginRedirectUri,\n scopes: env.OIDC_SCOPES?.trim() || \"openid profile email\",\n displayName: env.OIDC_DISPLAY_NAME?.trim() || \"Microsoft\",\n ...(defaultTenantId ? { defaultTenantId } : {}),\n jitProvisioning,\n autoLinkEmail: boolean(env.OIDC_AUTO_LINK_EMAIL),\n allowInsecure,\n insecureCookies: redirectUri.startsWith(\"http://\"),\n stateSecret,\n ...(allowedPrivateHosts.length ? { allowedPrivateHosts } : {}),\n };\n}\n\nexport function oidcDescriptor(config: OidcConfig): AuthProviderDescriptor {\n return {\n id: \"oidc\",\n displayName: config.displayName,\n interaction: \"redirect\",\n surfaces: [\"console\"],\n };\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport type { AuthCookie } from \"@axiom-lattice/protocols\";\n\nexport const OIDC_STATE_COOKIE = \"axiom_oidc_state\";\n/**\n * Shared by every provider route so the same cookie is readable for both\n * `/api/auth/login/:providerId` and `/api/auth/login/:providerId/callback`.\n */\nexport const OIDC_STATE_PATH = \"/api/auth/login\";\n\nexport interface OidcStatePayload {\n state: string;\n codeVerifier: string;\n nonce: string;\n /** Provider that started the flow; the callback rejects a mismatch. */\n providerId: string;\n exp: number;\n}\n\nfunction isPayload(value: unknown): value is OidcStatePayload {\n if (typeof value !== \"object\" || value === null) return false;\n const payload = value as Record<string, unknown>;\n return (\n typeof payload.state === \"string\" &&\n typeof payload.codeVerifier === \"string\" &&\n typeof payload.nonce === \"string\" &&\n typeof payload.providerId === \"string\" &&\n payload.providerId.length > 0 &&\n typeof payload.exp === \"number\" &&\n Number.isFinite(payload.exp)\n );\n}\n\nfunction stateKey(secret: string): Buffer {\n return createHmac(\"sha256\", secret).update(\"axiom-oidc-state-v1\").digest();\n}\n\nexport function signStateCookie(\n payload: OidcStatePayload,\n secret: string,\n): string {\n const encoded = Buffer.from(JSON.stringify(payload)).toString(\"base64url\");\n const signature = createHmac(\"sha256\", stateKey(secret))\n .update(encoded)\n .digest(\"base64url\");\n return `${encoded}.${signature}`;\n}\n\nexport function verifyStateCookie(\n value: string,\n secret: string,\n now: number = Date.now(),\n): OidcStatePayload | null {\n const [encoded, signature, extra] = value.split(\".\");\n if (!encoded || !signature || extra !== undefined) return null;\n const expected = createHmac(\"sha256\", stateKey(secret)).update(encoded).digest();\n let actual: Buffer;\n try {\n actual = Buffer.from(signature, \"base64url\");\n } catch {\n return null;\n }\n if (\n actual.length !== expected.length ||\n actual.toString(\"base64url\") !== signature ||\n !timingSafeEqual(actual, expected)\n ) {\n return null;\n }\n try {\n const payload: unknown = JSON.parse(\n Buffer.from(encoded, \"base64url\").toString(\"utf8\"),\n );\n if (!isPayload(payload) || payload.exp < now) return null;\n return payload;\n } catch {\n return null;\n }\n}\n\nexport function stateCookie(\n value: string,\n options: { secure: boolean; maxAge?: number },\n): AuthCookie {\n return {\n name: OIDC_STATE_COOKIE,\n value,\n options: {\n httpOnly: true,\n secure: options.secure,\n sameSite: \"lax\",\n path: OIDC_STATE_PATH,\n maxAge: options.maxAge ?? 600,\n },\n };\n}\n\nexport function clearStateCookie(options: { secure: boolean }): AuthCookie {\n return {\n name: OIDC_STATE_COOKIE,\n value: \"\",\n options: {\n httpOnly: true,\n secure: options.secure,\n sameSite: \"lax\",\n path: OIDC_STATE_PATH,\n maxAge: 0,\n },\n };\n}\n\nexport function serializeCookie(cookie: AuthCookie): string {\n const parts = [`${cookie.name}=${encodeURIComponent(cookie.value)}`];\n if (cookie.options.httpOnly) parts.push(\"HttpOnly\");\n if (cookie.options.secure) parts.push(\"Secure\");\n if (cookie.options.sameSite) {\n parts.push(\n `SameSite=${cookie.options.sameSite[0].toUpperCase()}${cookie.options.sameSite.slice(1)}`,\n );\n }\n if (cookie.options.path) parts.push(`Path=${cookie.options.path}`);\n if (cookie.options.maxAge !== undefined) parts.push(`Max-Age=${cookie.options.maxAge}`);\n return parts.join(\"; \");\n}\n\nexport function readCookie(header: string | undefined, name: string): string | undefined {\n if (!header) return undefined;\n for (const part of header.split(\";\")) {\n const [rawName, ...rest] = part.trim().split(\"=\");\n if (rawName === name) {\n try {\n return decodeURIComponent(rest.join(\"=\"));\n } catch (error) {\n if (error instanceof URIError) return undefined;\n throw error;\n }\n }\n }\n return undefined;\n}\n","import type { Configuration } from \"openid-client\";\n\nexport interface OidcDiscoveryOptions {\n timeoutSeconds: number;\n allowInsecure: boolean;\n}\n\nexport interface OidcClientLike {\n discovery(\n issuer: URL,\n clientId: string,\n clientSecret: string,\n options: OidcDiscoveryOptions,\n ): Promise<Configuration>;\n randomState(): string;\n randomPKCECodeVerifier(): string;\n calculatePKCECodeChallenge(verifier: string): Promise<string>;\n randomNonce(): string;\n buildAuthorizationUrl(\n config: Configuration,\n parameters: Record<string, string>,\n ): URL;\n authorizationCodeGrant(\n config: Configuration,\n currentUrl: URL,\n checks: {\n pkceCodeVerifier: string;\n expectedState: string;\n expectedNonce: string;\n },\n ): Promise<{ claims(): Record<string, unknown> | undefined }>;\n}\n\n/** Load the ESM-only `openid-client` package from CJS consumers. */\nexport async function loadOpenidClient(): Promise<OidcClientLike> {\n const mod = await import(\"openid-client\");\n return {\n discovery: (issuer, clientId, clientSecret, options) => {\n const execute = options.allowInsecure ? [mod.allowInsecureRequests] : undefined;\n return mod.discovery(issuer, clientId, clientSecret, undefined, {\n timeout: options.timeoutSeconds,\n ...(execute ? { execute } : {}),\n });\n },\n randomState: () => mod.randomState(),\n randomPKCECodeVerifier: () => mod.randomPKCECodeVerifier(),\n calculatePKCECodeChallenge: (verifier) => mod.calculatePKCECodeChallenge(verifier),\n randomNonce: () => mod.randomNonce(),\n buildAuthorizationUrl: (config, parameters) =>\n mod.buildAuthorizationUrl(config, parameters),\n authorizationCodeGrant: async (config, currentUrl, checks) => {\n const tokens = await mod.authorizationCodeGrant(config, currentUrl, checks);\n return {\n claims: () => tokens.claims() as unknown as Record<string, unknown> | undefined,\n };\n },\n };\n}\n","import {\n assertSafeOidcEndpoint,\n type EndpointSafetyOptions,\n} from \"./endpointSafety\";\nimport { loadOpenidClient, type OidcClientLike } from \"./openidClient\";\n\n/** Input needed to run OIDC discovery without starting a login flow. */\nexport interface OidcProbeConfig {\n issuer: string;\n clientId: string;\n clientSecret: string;\n allowInsecure: boolean;\n /** Exact hostnames allowed to resolve to a private address. */\n allowedPrivateHosts?: string[];\n /** Injectable client for offline tests; defaults to `openid-client`. */\n client?: OidcClientLike;\n /** Injectable endpoint validator for offline tests. */\n validateEndpoint?: (\n endpoint: string,\n options: EndpointSafetyOptions,\n ) => Promise<URL>;\n}\n\n/** Safe metadata summary returned by {@link probeOidcProvider}. */\nexport interface OidcProviderMetadataSummary {\n issuer: string;\n authorizationEndpoint: string;\n tokenEndpoint: string;\n jwksUri: string;\n userinfoEndpoint?: string;\n scopesSupported?: string[];\n}\n\nfunction requiredMetadataString(\n metadata: Record<string, unknown>,\n key: string,\n): string {\n const value = metadata[key];\n if (typeof value !== \"string\" || !value) {\n throw new Error(\"OIDC discovery returned incomplete metadata\");\n }\n return value;\n}\n\n/**\n * Runs OIDC discovery and validates the issuer plus every endpoint the provider\n * advertises. This never sets cookies or touches session state.\n *\n * @param config - Issuer, client credentials, and optional test seams.\n * @returns A safe metadata summary for the admin test endpoint.\n */\nexport async function probeOidcProvider(\n config: OidcProbeConfig,\n): Promise<OidcProviderMetadataSummary> {\n const client = config.client ?? (await loadOpenidClient());\n const validateEndpoint = config.validateEndpoint ?? assertSafeOidcEndpoint;\n const safety = {\n allowInsecure: config.allowInsecure,\n allowedPrivateHosts: config.allowedPrivateHosts ?? [],\n };\n\n await validateEndpoint(config.issuer, safety);\n const discovered = await client.discovery(\n new URL(config.issuer),\n config.clientId,\n config.clientSecret,\n { timeoutSeconds: 5, allowInsecure: config.allowInsecure },\n );\n const metadata = discovered.serverMetadata() as unknown as Record<\n string,\n unknown\n >;\n const authorizationEndpoint = requiredMetadataString(\n metadata,\n \"authorization_endpoint\",\n );\n const tokenEndpoint = requiredMetadataString(metadata, \"token_endpoint\");\n const jwksUri = requiredMetadataString(metadata, \"jwks_uri\");\n const userinfoEndpoint =\n typeof metadata.userinfo_endpoint === \"string\" &&\n metadata.userinfo_endpoint\n ? metadata.userinfo_endpoint\n : undefined;\n\n for (const endpoint of [\n authorizationEndpoint,\n tokenEndpoint,\n jwksUri,\n ...(userinfoEndpoint ? [userinfoEndpoint] : []),\n ]) {\n await validateEndpoint(endpoint, safety);\n }\n\n const issuer =\n typeof metadata.issuer === \"string\" && metadata.issuer\n ? metadata.issuer\n : config.issuer;\n const scopesSupported = Array.isArray(metadata.scopes_supported)\n ? metadata.scopes_supported.filter(\n (scope): scope is string => typeof scope === \"string\",\n )\n : undefined;\n\n return {\n issuer,\n authorizationEndpoint,\n tokenEndpoint,\n jwksUri,\n ...(userinfoEndpoint ? { userinfoEndpoint } : {}),\n ...(scopesSupported ? { scopesSupported } : {}),\n };\n}\n","import {\n AuthError,\n type AuthChallenge,\n type AuthProvider,\n type AuthProviderDescriptor,\n type AuthRequestContext,\n type Principal,\n} from \"@axiom-lattice/protocols\";\nimport type { Configuration } from \"openid-client\";\nimport type { OidcConfig } from \"./config\";\nimport { oidcDescriptor } from \"./config\";\nimport {\n OIDC_STATE_COOKIE,\n signStateCookie,\n stateCookie,\n verifyStateCookie,\n} from \"./stateCookie\";\nimport { assertSafeOidcEndpoint } from \"./endpointSafety\";\nimport { loadOpenidClient, type OidcClientLike } from \"./openidClient\";\n\nexport interface OidcAuthProviderOptions {\n config: OidcConfig;\n /** Provider id that started the flow; signed into and verified from state. */\n providerId: string;\n client?: OidcClientLike;\n now?: () => number;\n /** Injectable for offline tests; defaults to the DNS-checking validator. */\n validateEndpoint?: (\n endpoint: string,\n options: { allowInsecure: boolean },\n ) => Promise<URL>;\n}\n\nfunction stringClaim(\n claims: Record<string, unknown>,\n ...names: string[]\n): string | undefined {\n for (const name of names) {\n const value = claims[name];\n if (typeof value === \"string\" && value.trim()) return value;\n }\n return undefined;\n}\n\nfunction providerError(message: string, cause: unknown): AuthError {\n const error = new AuthError(\"PROVIDER_ERROR\", 502, message);\n if (cause !== undefined) {\n Object.defineProperty(error, \"cause\", {\n value: cause,\n enumerable: false,\n configurable: true,\n writable: true,\n });\n }\n return error;\n}\n\nexport function principalFromClaims(\n claims: Record<string, unknown>,\n): Principal {\n const subject = stringClaim(claims, \"oid\", \"sub\");\n if (!subject) {\n throw new AuthError(\"PROVIDER_ERROR\", 502, \"OIDC ID token has no subject\");\n }\n const emailClaim = stringClaim(claims, \"email\");\n const rawEmail =\n emailClaim ?? stringClaim(claims, \"preferred_username\", \"upn\");\n const email = rawEmail?.trim().toLowerCase();\n const name = stringClaim(claims, \"name\");\n const picture = stringClaim(claims, \"picture\");\n return {\n subject,\n method: \"oidc\",\n ...(email ? { email } : {}),\n ...(email\n ? { emailVerified: emailClaim !== undefined && claims.email_verified === true }\n : {}),\n ...(name ? { name } : {}),\n ...(picture ? { picture } : {}),\n claims,\n };\n}\n\nexport class OidcAuthProvider implements AuthProvider {\n readonly descriptor: AuthProviderDescriptor;\n private configurationPromise?: Promise<Configuration>;\n\n constructor(private readonly options: OidcAuthProviderOptions) {\n this.descriptor = oidcDescriptor(options.config);\n }\n\n private async getClient(): Promise<OidcClientLike> {\n return this.options.client ?? (await loadOpenidClient());\n }\n\n private async getConfiguration(client: OidcClientLike): Promise<Configuration> {\n if (!this.configurationPromise) {\n this.configurationPromise = (async () => {\n try {\n const validateEndpoint =\n this.options.validateEndpoint ?? assertSafeOidcEndpoint;\n const safety = {\n allowInsecure: this.options.config.allowInsecure,\n allowedPrivateHosts:\n this.options.config.allowedPrivateHosts ?? [],\n };\n await validateEndpoint(this.options.config.issuer, safety);\n const discovered = await client.discovery(\n new URL(this.options.config.issuer),\n this.options.config.clientId,\n this.options.config.clientSecret,\n {\n timeoutSeconds: 5,\n allowInsecure: this.options.config.allowInsecure,\n },\n );\n const metadata = discovered.serverMetadata();\n const { authorization_endpoint, token_endpoint, jwks_uri } = metadata;\n if (\n typeof authorization_endpoint !== \"string\" ||\n !authorization_endpoint ||\n typeof token_endpoint !== \"string\" ||\n !token_endpoint ||\n typeof jwks_uri !== \"string\" ||\n !jwks_uri\n ) {\n throw new Error(\"OIDC discovery returned incomplete metadata\");\n }\n for (const endpoint of [\n authorization_endpoint,\n token_endpoint,\n jwks_uri,\n ]) {\n await validateEndpoint(endpoint, safety);\n }\n return discovered;\n } catch (error) {\n throw providerError(\"OIDC discovery failed\", error);\n }\n })().catch((error: unknown) => {\n this.configurationPromise = undefined;\n throw error;\n });\n }\n return this.configurationPromise;\n }\n\n async begin(): Promise<AuthChallenge> {\n try {\n const client = await this.getClient();\n const config = await this.getConfiguration(client);\n const state = client.randomState();\n const codeVerifier = client.randomPKCECodeVerifier();\n const codeChallenge = await client.calculatePKCECodeChallenge(codeVerifier);\n const nonce = client.randomNonce();\n const url = client.buildAuthorizationUrl(config, {\n redirect_uri: this.options.config.redirectUri,\n response_type: \"code\",\n scope: this.options.config.scopes,\n state,\n nonce,\n code_challenge: codeChallenge,\n code_challenge_method: \"S256\",\n });\n const now = this.options.now?.() ?? Date.now();\n const cookieValue = signStateCookie(\n {\n state,\n codeVerifier,\n nonce,\n providerId: this.options.providerId,\n exp: now + 600_000,\n },\n this.options.config.stateSecret,\n );\n return {\n kind: \"redirect\",\n url: url.href,\n cookies: [\n stateCookie(cookieValue, { secure: !this.options.config.insecureCookies }),\n ],\n };\n } catch (error) {\n if (error instanceof AuthError) throw error;\n throw providerError(\"OIDC authorization request failed\", error);\n }\n }\n\n async callback(ctx: AuthRequestContext): Promise<Principal> {\n try {\n const rawCookie = ctx.cookies?.[OIDC_STATE_COOKIE];\n if (!rawCookie) {\n throw new AuthError(\"INVALID_STATE\", 400, \"OIDC state cookie is missing\");\n }\n const now = this.options.now?.() ?? Date.now();\n const stored = verifyStateCookie(\n rawCookie,\n this.options.config.stateSecret,\n now,\n );\n if (!stored) {\n throw new AuthError(\n \"INVALID_STATE\",\n 400,\n \"OIDC state cookie is invalid or expired\",\n );\n }\n if (stored.providerId !== this.options.providerId) {\n throw new AuthError(\n \"INVALID_STATE\",\n 400,\n \"OIDC state provider does not match\",\n );\n }\n if (ctx.params?.state !== stored.state) {\n throw new AuthError(\"INVALID_STATE\", 400, \"OIDC state does not match\");\n }\n\n const client = await this.getClient();\n const config = await this.getConfiguration(client);\n const currentUrl = new URL(this.options.config.redirectUri);\n for (const [key, value] of Object.entries(ctx.params ?? {})) {\n currentUrl.searchParams.set(key, value);\n }\n\n const tokens = await client.authorizationCodeGrant(config, currentUrl, {\n pkceCodeVerifier: stored.codeVerifier,\n expectedState: stored.state,\n expectedNonce: stored.nonce,\n });\n const claims = tokens.claims();\n if (!claims) {\n throw new AuthError(\"PROVIDER_ERROR\", 502, \"OIDC ID token is missing\");\n }\n return principalFromClaims(claims);\n } catch (error) {\n if (error instanceof AuthError) throw error;\n throw providerError(\"OIDC token exchange failed\", error);\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/endpointSafety.ts","../src/config.ts","../src/stateCookie.ts","../src/openidClient.ts","../src/probe.ts","../src/OidcAuthProvider.ts"],"sourcesContent":["import { lookup } from \"node:dns/promises\";\nimport { isIP } from \"node:net\";\n\nexport interface EndpointSafetyOptions {\n allowInsecure: boolean;\n /**\n * Exact hostnames allowed to resolve to a private address.\n *\n * This is intentionally narrower than the general SSRF guard: it does not\n * allow HTTP, userinfo, query strings, or other private hosts. Deployments\n * that must reach a private OIDC issuer through a VPN/fake-IP DNS should list\n * only that issuer host here.\n */\n allowedPrivateHosts?: readonly string[];\n}\n\nfunction hostAddress(hostname: string): string {\n return hostname.replace(/^\\[/, \"\").replace(/\\]$/, \"\");\n}\n\nfunction normalizeHostEntry(raw: string): string | undefined {\n const trimmed = raw.trim();\n if (!trimmed) return undefined;\n let host = trimmed;\n if (trimmed.includes(\"://\")) {\n try {\n host = new URL(trimmed).hostname;\n } catch {\n return undefined;\n }\n }\n return hostAddress(host).toLowerCase().replace(/\\.$/, \"\");\n}\n\n/**\n * Parses `OIDC_ALLOWED_PRIVATE_HOSTS` into a normalized, exact hostname list.\n *\n * Entries are comma-separated. Surrounding whitespace, case, IPv6 brackets,\n * and trailing dots are normalized; schemes are accepted for convenience.\n * Invalid/empty entries are ignored rather than failing startup, so a typo\n * cannot silently widen access to a different host.\n */\nexport function parseAllowedPrivateHosts(\n value: string | undefined,\n): string[] {\n if (!value) return [];\n const hosts = new Set<string>();\n for (const entry of value.split(\",\")) {\n const host = normalizeHostEntry(entry);\n if (host) hosts.add(host);\n }\n return [...hosts];\n}\n\nfunction isAllowedPrivateHost(\n hostname: string,\n options: EndpointSafetyOptions,\n): boolean {\n const host = normalizeHostEntry(hostname);\n if (!host) return false;\n return (options.allowedPrivateHosts ?? []).some(\n (entry) => normalizeHostEntry(entry) === host,\n );\n}\n\nfunction isLocalHostname(hostname: string): boolean {\n const host = hostAddress(hostname);\n return host === \"localhost\" || host === \"127.0.0.1\" || host === \"::1\";\n}\n\nfunction isPrivateIPv4(ip: string): boolean {\n const parts = ip.split(\".\").map(Number);\n if (parts.length !== 4 || parts.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) {\n return true;\n }\n const [a, b, c] = parts;\n return (\n a === 0 ||\n a === 10 ||\n a === 127 ||\n (a === 169 && b === 254) ||\n (a === 172 && b >= 16 && b <= 31) ||\n (a === 192 && b === 168) ||\n (a === 192 && b === 0 && c === 0) ||\n (a === 192 && b === 0 && c === 2) ||\n (a === 198 && b === 51 && c === 100) ||\n (a === 203 && b === 0 && c === 113) ||\n (a === 100 && b >= 64 && b <= 127) ||\n (a === 198 && (b === 18 || b === 19)) ||\n a >= 224\n );\n}\n\nconst IPV6_8_BITS = 8;\nconst IPV6_GROUPS = 8;\n\nfunction ipv6Bytes(ip: string): number[] | null {\n let value = ip.toLowerCase();\n const zone = value.indexOf(\"%\");\n if (zone !== -1) value = value.slice(0, zone);\n\n const lastColon = value.lastIndexOf(\":\");\n if (lastColon !== -1 && value.slice(lastColon + 1).includes(\".\")) {\n const ipv4 = value.slice(lastColon + 1).split(\".\").map(Number);\n if (\n ipv4.length !== 4 ||\n ipv4.some((n) => !Number.isInteger(n) || n < 0 || n > 255)\n ) {\n return null;\n }\n const high = ((ipv4[0] << 8) | ipv4[1]).toString(16);\n const low = ((ipv4[2] << 8) | ipv4[3]).toString(16);\n value = `${value.slice(0, lastColon + 1)}${high}:${low}`;\n }\n\n const halves = value.split(\"::\");\n if (halves.length > 2) return null;\n const head = halves[0] ? halves[0].split(\":\") : [];\n const tail = halves.length === 2 && halves[1] ? halves[1].split(\":\") : [];\n let groups: string[];\n if (halves.length === 2) {\n const missing = IPV6_GROUPS - head.length - tail.length;\n if (missing < 1) return null;\n groups = [...head, ...Array(missing).fill(\"0\"), ...tail];\n } else {\n groups = head;\n }\n if (groups.length !== IPV6_GROUPS) return null;\n\n const bytes: number[] = [];\n for (const group of groups) {\n if (!/^[0-9a-f]{1,4}$/.test(group)) return null;\n const part = Number.parseInt(group, 16);\n bytes.push((part >> IPV6_8_BITS) & 0xff, part & 0xff);\n }\n return bytes;\n}\n\nfunction hasPrefix(bytes: number[], prefix: number[]): boolean {\n return prefix.every((value, index) => bytes[index] === value);\n}\n\nfunction isPrivateIPv6(ip: string): boolean {\n const bytes = ipv6Bytes(ip);\n if (!bytes) return true;\n if (bytes.every((value) => value === 0)) return true;\n const loopback = bytes.slice(0, 15).every((value) => value === 0) && bytes[15] === 1;\n if (loopback) return true;\n if ((bytes[0] & 0xfe) === 0xfc) return true;\n if (bytes[0] === 0xfe && (bytes[1] & 0xc0) === 0x80) return true;\n if (bytes[0] === 0xff) return true;\n if (\n bytes[0] === 0 &&\n bytes[1] === 0 &&\n bytes[2] === 0 &&\n bytes[3] === 0 &&\n bytes[4] === 0 &&\n bytes[5] === 0 &&\n bytes[6] === 0 &&\n bytes[7] === 0 &&\n bytes[8] === 0 &&\n bytes[9] === 0 &&\n bytes[10] === 0xff &&\n bytes[11] === 0xff\n ) {\n return isPrivateIPv4(\n `${bytes[12]}.${bytes[13]}.${bytes[14]}.${bytes[15]}`,\n );\n }\n if (hasPrefix(bytes, [0x00, 0x64, 0xff, 0x9b, 0, 0, 0, 0, 0, 0, 0, 0])) {\n return true;\n }\n if (hasPrefix(bytes, [0x00, 0x64, 0xff, 0x9b, 0x00, 0x01])) return true;\n if (hasPrefix(bytes, [0x20, 0x02])) return true;\n if (hasPrefix(bytes, [0x20, 0x01, 0x00, 0x00])) return true;\n if (hasPrefix(bytes, [0x20, 0x01, 0x0d, 0xb8])) return true;\n if (hasPrefix(bytes, [0x20, 0x01, 0x00, 0x02, 0x00, 0x00])) return true;\n if (hasPrefix(bytes, [0x01, 0x00, 0, 0, 0, 0, 0, 0])) return true;\n return false;\n}\n\nfunction isPrivateAddress(ip: string): boolean {\n const version = isIP(ip);\n if (version === 4) return isPrivateIPv4(ip);\n if (version === 6) return isPrivateIPv6(ip);\n return true;\n}\n\n/**\n * Addresses that must never be reachable even through the private-host\n * allowlist: cloud metadata/link-local and the unspecified address.\n *\n * Loopback is intentionally excluded so an intentional `127.0.0.1` / `::1`\n * allowlist entry keeps working for local development.\n */\nexport function isLinkLocalOrMetadataAddress(address: string): boolean {\n const ip = hostAddress(address);\n const version = isIP(ip);\n if (version === 4) {\n const parts = ip.split(\".\").map(Number);\n if (\n parts.length !== 4 ||\n parts.some((n) => !Number.isInteger(n) || n < 0 || n > 255)\n ) {\n return false;\n }\n return parts[0] === 0 || (parts[0] === 169 && parts[1] === 254);\n }\n if (version !== 6) return false;\n const bytes = ipv6Bytes(ip);\n if (!bytes) return false;\n if (bytes.every((value) => value === 0)) return true;\n if (bytes[0] === 0xfe && (bytes[1] & 0xc0) === 0x80) return true;\n const ipv4Mapped =\n bytes.slice(0, 10).every((value) => value === 0) &&\n bytes[10] === 0xff &&\n bytes[11] === 0xff;\n if (ipv4Mapped) {\n return isLinkLocalOrMetadataAddress(\n `${bytes[12]}.${bytes[13]}.${bytes[14]}.${bytes[15]}`,\n );\n }\n return false;\n}\n\nexport function assertSafeEndpointSyntax(\n raw: string,\n options: EndpointSafetyOptions,\n): URL {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(\"OIDC endpoint must be an absolute URL\");\n }\n if (url.username || url.password) {\n throw new Error(\"OIDC endpoint must not contain userinfo\");\n }\n if (url.search || url.hash) {\n throw new Error(\"OIDC endpoint must not contain query or fragment\");\n }\n const host = hostAddress(url.hostname);\n if (url.protocol !== \"https:\") {\n const localMock =\n options.allowInsecure && url.protocol === \"http:\" && isLocalHostname(host);\n if (!localMock) throw new Error(\"OIDC endpoint must use https\");\n }\n if (isLocalHostname(host) && !options.allowInsecure) {\n throw new Error(\"OIDC endpoint must not target localhost\");\n }\n if (isIP(host) && isLinkLocalOrMetadataAddress(host)) {\n throw new Error(\n \"OIDC endpoint must not target a link-local or metadata address\",\n );\n }\n if (isIP(host) && isPrivateAddress(host)) {\n if (\n !(options.allowInsecure && isLocalHostname(host)) &&\n !isAllowedPrivateHost(host, options)\n ) {\n throw new Error(\"OIDC endpoint must not target a private address\");\n }\n }\n return url;\n}\n\nexport async function assertSafeOidcEndpoint(\n raw: string,\n options: EndpointSafetyOptions,\n): Promise<URL> {\n const url = assertSafeEndpointSyntax(raw, options);\n const host = hostAddress(url.hostname);\n const allowlisted = isAllowedPrivateHost(host, options);\n if ((options.allowInsecure && isLocalHostname(host)) || isIP(host)) {\n return url;\n }\n const addresses = await lookup(host, { all: true, verbatim: true });\n if (addresses.length === 0) throw new Error(\"OIDC endpoint host did not resolve\");\n for (const { address } of addresses) {\n if (isLinkLocalOrMetadataAddress(address)) {\n throw new Error(\n \"OIDC endpoint host resolves to a link-local or metadata address\",\n );\n }\n if (isPrivateAddress(address) && !allowlisted) {\n throw new Error(\"OIDC endpoint host resolves to a private address\");\n }\n }\n return url;\n}\n","import type { AuthProviderDescriptor } from \"@axiom-lattice/protocols\";\nimport { parseAllowedPrivateHosts } from \"./endpointSafety\";\n\nexport interface OidcConfig {\n issuer: string;\n clientId: string;\n clientSecret: string;\n redirectUri: string;\n postLoginRedirectUri: string;\n scopes: string;\n displayName: string;\n defaultTenantId?: string;\n jitProvisioning: boolean;\n autoLinkEmail: boolean;\n allowInsecure: boolean;\n insecureCookies: boolean;\n stateSecret: string;\n /** Exact hosts allowed to resolve to a private address (deployment-wide). */\n allowedPrivateHosts?: string[];\n}\n\nexport interface ReadOidcConfigOptions {\n stateSecret?: string;\n nodeEnv?: string;\n}\n\nfunction required(env: NodeJS.ProcessEnv, name: string): string {\n const value = env[name]?.trim();\n if (!value) throw new Error(`${name} is required when OIDC_ENABLED=true`);\n return value;\n}\n\nfunction boolean(value: string | undefined, fallback = false): boolean {\n if (value === undefined || value.trim() === \"\") return fallback;\n return value === \"true\";\n}\n\nfunction isLocalHost(hostname: string): boolean {\n const host = hostname.replace(/^\\[/, \"\").replace(/\\]$/, \"\");\n return host === \"localhost\" || host === \"127.0.0.1\" || host === \"::1\";\n}\n\nfunction assertNoCredentialsQueryOrFragment(url: URL, name: string): void {\n if (url.username || url.password) {\n throw new Error(`${name} must not contain userinfo`);\n }\n if (url.search || url.hash) {\n throw new Error(`${name} must not contain query or fragment`);\n }\n}\n\n/**\n * Rejects the Azure multi-tenant issuers that cannot be used for identity\n * binding. Shared by the env config reader and runtime store-provider\n * validation so both paths enforce the same rule.\n *\n * @param url - Parsed issuer URL.\n * @param name - Setting name used in the error message.\n */\nexport function assertTenantSpecificAzureIssuer(\n url: URL,\n name = \"OIDC_ISSUER\",\n): void {\n if (url.hostname.toLowerCase() !== \"login.microsoftonline.com\") return;\n const firstSegment = url.pathname.split(\"/\").filter(Boolean)[0]?.toLowerCase();\n if (firstSegment === \"common\" || firstSegment === \"organizations\") {\n throw new Error(\n `${name} must be tenant-specific; ` +\n \"login.microsoftonline.com/common and /organizations are not supported\",\n );\n }\n}\n\nfunction parseIssuer(raw: string, allowInsecure: boolean): string {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(\"OIDC_ISSUER must be an absolute URL\");\n }\n assertNoCredentialsQueryOrFragment(url, \"OIDC_ISSUER\");\n assertTenantSpecificAzureIssuer(url);\n if (url.protocol !== \"https:\") {\n if (!(allowInsecure && url.protocol === \"http:\" && isLocalHost(url.hostname))) {\n throw new Error(\"OIDC_ISSUER must use https\");\n }\n }\n return url.toString().replace(/\\/$/, \"\");\n}\n\nfunction parseRedirect(raw: string, nodeEnv: string): string {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(\"OIDC redirect URLs must be absolute URLs\");\n }\n assertNoCredentialsQueryOrFragment(url, \"OIDC redirect URLs\");\n if (url.protocol !== \"https:\") {\n const localAllowed =\n (nodeEnv === \"test\" || nodeEnv === \"development\") &&\n url.protocol === \"http:\" &&\n isLocalHost(url.hostname);\n if (!localAllowed) {\n throw new Error(\"OIDC redirect URLs must use https outside test/development\");\n }\n }\n return url.toString();\n}\n\nexport function readOidcConfig(\n env: NodeJS.ProcessEnv = process.env,\n options: ReadOidcConfigOptions = {},\n): OidcConfig | null {\n if (!boolean(env.OIDC_ENABLED)) return null;\n\n const nodeEnv = options.nodeEnv ?? env.NODE_ENV ?? \"production\";\n const allowInsecure = boolean(env.OIDC_ALLOW_INSECURE);\n if (allowInsecure && nodeEnv !== \"test\" && nodeEnv !== \"development\") {\n throw new Error(\"OIDC_ALLOW_INSECURE is only allowed when NODE_ENV=test|development\");\n }\n\n const issuer = parseIssuer(required(env, \"OIDC_ISSUER\"), allowInsecure);\n const redirectUri = parseRedirect(required(env, \"OIDC_REDIRECT_URI\"), nodeEnv);\n const postLoginRedirectUri = parseRedirect(\n required(env, \"OIDC_POST_LOGIN_REDIRECT_URI\"),\n nodeEnv,\n );\n const jitProvisioning = boolean(env.OIDC_JIT_PROVISIONING);\n const defaultTenantId = env.OIDC_DEFAULT_TENANT_ID?.trim() || undefined;\n\n const stateSecret = (\n options.stateSecret ??\n env.AUTH_TOKEN_SECRET ??\n env.JWT_SECRET\n )?.trim();\n if (!stateSecret) {\n throw new Error(\"AUTH_TOKEN_SECRET or JWT_SECRET is required for OIDC state signing\");\n }\n if (stateSecret.length < 16) {\n throw new Error(\n \"AUTH_TOKEN_SECRET or JWT_SECRET must be at least 16 characters for OIDC state signing\",\n );\n }\n\n const allowedPrivateHosts = parseAllowedPrivateHosts(\n env.OIDC_ALLOWED_PRIVATE_HOSTS,\n );\n\n return {\n issuer,\n clientId: required(env, \"OIDC_CLIENT_ID\"),\n clientSecret: env.OIDC_CLIENT_SECRET?.trim() ?? \"\",\n redirectUri,\n postLoginRedirectUri,\n scopes: env.OIDC_SCOPES?.trim() || \"openid profile email\",\n displayName: env.OIDC_DISPLAY_NAME?.trim() || \"Microsoft\",\n ...(defaultTenantId ? { defaultTenantId } : {}),\n jitProvisioning,\n autoLinkEmail: boolean(env.OIDC_AUTO_LINK_EMAIL),\n allowInsecure,\n insecureCookies: redirectUri.startsWith(\"http://\"),\n stateSecret,\n ...(allowedPrivateHosts.length ? { allowedPrivateHosts } : {}),\n };\n}\n\nexport function oidcDescriptor(config: OidcConfig): AuthProviderDescriptor {\n return {\n id: \"oidc\",\n displayName: config.displayName,\n interaction: \"redirect\",\n surfaces: [\"console\"],\n };\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport type { AuthCookie } from \"@axiom-lattice/protocols\";\n\nexport const OIDC_STATE_COOKIE = \"axiom_oidc_state\";\n/**\n * Shared by every provider route so the same cookie is readable for both\n * `/api/auth/login/:providerId` and `/api/auth/login/:providerId/callback`.\n */\nexport const OIDC_STATE_PATH = \"/api/auth/login\";\n\nexport interface OidcStatePayload {\n state: string;\n codeVerifier: string;\n nonce: string;\n /** Provider that started the flow; the callback rejects a mismatch. */\n providerId: string;\n exp: number;\n}\n\nfunction isPayload(value: unknown): value is OidcStatePayload {\n if (typeof value !== \"object\" || value === null) return false;\n const payload = value as Record<string, unknown>;\n return (\n typeof payload.state === \"string\" &&\n typeof payload.codeVerifier === \"string\" &&\n typeof payload.nonce === \"string\" &&\n typeof payload.providerId === \"string\" &&\n payload.providerId.length > 0 &&\n typeof payload.exp === \"number\" &&\n Number.isFinite(payload.exp)\n );\n}\n\nfunction stateKey(secret: string): Buffer {\n return createHmac(\"sha256\", secret).update(\"axiom-oidc-state-v1\").digest();\n}\n\nexport function signStateCookie(\n payload: OidcStatePayload,\n secret: string,\n): string {\n const encoded = Buffer.from(JSON.stringify(payload)).toString(\"base64url\");\n const signature = createHmac(\"sha256\", stateKey(secret))\n .update(encoded)\n .digest(\"base64url\");\n return `${encoded}.${signature}`;\n}\n\nexport function verifyStateCookie(\n value: string,\n secret: string,\n now: number = Date.now(),\n): OidcStatePayload | null {\n const [encoded, signature, extra] = value.split(\".\");\n if (!encoded || !signature || extra !== undefined) return null;\n const expected = createHmac(\"sha256\", stateKey(secret)).update(encoded).digest();\n let actual: Buffer;\n try {\n actual = Buffer.from(signature, \"base64url\");\n } catch {\n return null;\n }\n if (\n actual.length !== expected.length ||\n actual.toString(\"base64url\") !== signature ||\n !timingSafeEqual(actual, expected)\n ) {\n return null;\n }\n try {\n const payload: unknown = JSON.parse(\n Buffer.from(encoded, \"base64url\").toString(\"utf8\"),\n );\n if (!isPayload(payload) || payload.exp < now) return null;\n return payload;\n } catch {\n return null;\n }\n}\n\nexport function stateCookie(\n value: string,\n options: { secure: boolean; maxAge?: number },\n): AuthCookie {\n return {\n name: OIDC_STATE_COOKIE,\n value,\n options: {\n httpOnly: true,\n secure: options.secure,\n sameSite: \"lax\",\n path: OIDC_STATE_PATH,\n maxAge: options.maxAge ?? 600,\n },\n };\n}\n\nexport function clearStateCookie(options: { secure: boolean }): AuthCookie {\n return {\n name: OIDC_STATE_COOKIE,\n value: \"\",\n options: {\n httpOnly: true,\n secure: options.secure,\n sameSite: \"lax\",\n path: OIDC_STATE_PATH,\n maxAge: 0,\n },\n };\n}\n\nexport function serializeCookie(cookie: AuthCookie): string {\n const parts = [`${cookie.name}=${encodeURIComponent(cookie.value)}`];\n if (cookie.options.httpOnly) parts.push(\"HttpOnly\");\n if (cookie.options.secure) parts.push(\"Secure\");\n if (cookie.options.sameSite) {\n parts.push(\n `SameSite=${cookie.options.sameSite[0].toUpperCase()}${cookie.options.sameSite.slice(1)}`,\n );\n }\n if (cookie.options.path) parts.push(`Path=${cookie.options.path}`);\n if (cookie.options.maxAge !== undefined) parts.push(`Max-Age=${cookie.options.maxAge}`);\n return parts.join(\"; \");\n}\n\nexport function readCookie(header: string | undefined, name: string): string | undefined {\n if (!header) return undefined;\n for (const part of header.split(\";\")) {\n const [rawName, ...rest] = part.trim().split(\"=\");\n if (rawName === name) {\n try {\n return decodeURIComponent(rest.join(\"=\"));\n } catch (error) {\n if (error instanceof URIError) return undefined;\n throw error;\n }\n }\n }\n return undefined;\n}\n","import type { Configuration } from \"openid-client\";\n\nexport interface OidcDiscoveryOptions {\n timeoutSeconds: number;\n allowInsecure: boolean;\n}\n\nexport interface OidcClientLike {\n discovery(\n issuer: URL,\n clientId: string,\n clientSecret: string,\n options: OidcDiscoveryOptions,\n ): Promise<Configuration>;\n randomState(): string;\n randomPKCECodeVerifier(): string;\n calculatePKCECodeChallenge(verifier: string): Promise<string>;\n randomNonce(): string;\n buildAuthorizationUrl(\n config: Configuration,\n parameters: Record<string, string>,\n ): URL;\n authorizationCodeGrant(\n config: Configuration,\n currentUrl: URL,\n checks: {\n pkceCodeVerifier: string;\n expectedState: string;\n expectedNonce: string;\n },\n ): Promise<{ claims(): Record<string, unknown> | undefined }>;\n}\n\n/** Load the ESM-only `openid-client` package from CJS consumers. */\nexport async function loadOpenidClient(): Promise<OidcClientLike> {\n const mod = await import(\"openid-client\");\n return {\n discovery: (issuer, clientId, clientSecret, options) => {\n const execute = options.allowInsecure ? [mod.allowInsecureRequests] : undefined;\n return mod.discovery(issuer, clientId, clientSecret, undefined, {\n timeout: options.timeoutSeconds,\n ...(execute ? { execute } : {}),\n });\n },\n randomState: () => mod.randomState(),\n randomPKCECodeVerifier: () => mod.randomPKCECodeVerifier(),\n calculatePKCECodeChallenge: (verifier) => mod.calculatePKCECodeChallenge(verifier),\n randomNonce: () => mod.randomNonce(),\n buildAuthorizationUrl: (config, parameters) =>\n mod.buildAuthorizationUrl(config, parameters),\n authorizationCodeGrant: async (config, currentUrl, checks) => {\n const tokens = await mod.authorizationCodeGrant(config, currentUrl, checks);\n return {\n claims: () => tokens.claims() as unknown as Record<string, unknown> | undefined,\n };\n },\n };\n}\n","import {\n assertSafeOidcEndpoint,\n type EndpointSafetyOptions,\n} from \"./endpointSafety\";\nimport { loadOpenidClient, type OidcClientLike } from \"./openidClient\";\n\n/** Input needed to run OIDC discovery without starting a login flow. */\nexport interface OidcProbeConfig {\n issuer: string;\n clientId: string;\n clientSecret: string;\n allowInsecure: boolean;\n /** Exact hostnames allowed to resolve to a private address. */\n allowedPrivateHosts?: string[];\n /** Injectable client for offline tests; defaults to `openid-client`. */\n client?: OidcClientLike;\n /** Injectable endpoint validator for offline tests. */\n validateEndpoint?: (\n endpoint: string,\n options: EndpointSafetyOptions,\n ) => Promise<URL>;\n}\n\n/** Safe metadata summary returned by {@link probeOidcProvider}. */\nexport interface OidcProviderMetadataSummary {\n issuer: string;\n authorizationEndpoint: string;\n tokenEndpoint: string;\n jwksUri: string;\n userinfoEndpoint?: string;\n scopesSupported?: string[];\n}\n\nfunction requiredMetadataString(\n metadata: Record<string, unknown>,\n key: string,\n): string {\n const value = metadata[key];\n if (typeof value !== \"string\" || !value) {\n throw new Error(\"OIDC discovery returned incomplete metadata\");\n }\n return value;\n}\n\n/**\n * Runs OIDC discovery and validates the issuer plus every endpoint the provider\n * advertises. This never sets cookies or touches session state.\n *\n * @param config - Issuer, client credentials, and optional test seams.\n * @returns A safe metadata summary for the admin test endpoint.\n */\nexport async function probeOidcProvider(\n config: OidcProbeConfig,\n): Promise<OidcProviderMetadataSummary> {\n const client = config.client ?? (await loadOpenidClient());\n const validateEndpoint = config.validateEndpoint ?? assertSafeOidcEndpoint;\n const safety = {\n allowInsecure: config.allowInsecure,\n allowedPrivateHosts: config.allowedPrivateHosts ?? [],\n };\n\n await validateEndpoint(config.issuer, safety);\n const discovered = await client.discovery(\n new URL(config.issuer),\n config.clientId,\n config.clientSecret,\n { timeoutSeconds: 5, allowInsecure: config.allowInsecure },\n );\n const metadata = discovered.serverMetadata() as unknown as Record<\n string,\n unknown\n >;\n const authorizationEndpoint = requiredMetadataString(\n metadata,\n \"authorization_endpoint\",\n );\n const tokenEndpoint = requiredMetadataString(metadata, \"token_endpoint\");\n const jwksUri = requiredMetadataString(metadata, \"jwks_uri\");\n const userinfoEndpoint =\n typeof metadata.userinfo_endpoint === \"string\" &&\n metadata.userinfo_endpoint\n ? metadata.userinfo_endpoint\n : undefined;\n\n for (const endpoint of [\n authorizationEndpoint,\n tokenEndpoint,\n jwksUri,\n ...(userinfoEndpoint ? [userinfoEndpoint] : []),\n ]) {\n await validateEndpoint(endpoint, safety);\n }\n\n const issuer =\n typeof metadata.issuer === \"string\" && metadata.issuer\n ? metadata.issuer\n : config.issuer;\n const scopesSupported = Array.isArray(metadata.scopes_supported)\n ? metadata.scopes_supported.filter(\n (scope): scope is string => typeof scope === \"string\",\n )\n : undefined;\n\n return {\n issuer,\n authorizationEndpoint,\n tokenEndpoint,\n jwksUri,\n ...(userinfoEndpoint ? { userinfoEndpoint } : {}),\n ...(scopesSupported ? { scopesSupported } : {}),\n };\n}\n","import {\n AuthError,\n type AuthChallenge,\n type AuthProvider,\n type AuthProviderDescriptor,\n type AuthRequestContext,\n type Principal,\n} from \"@axiom-lattice/protocols\";\nimport type { Configuration } from \"openid-client\";\nimport type { OidcConfig } from \"./config\";\nimport { oidcDescriptor } from \"./config\";\nimport {\n OIDC_STATE_COOKIE,\n signStateCookie,\n stateCookie,\n verifyStateCookie,\n} from \"./stateCookie\";\nimport { assertSafeOidcEndpoint } from \"./endpointSafety\";\nimport { loadOpenidClient, type OidcClientLike } from \"./openidClient\";\n\nexport interface OidcAuthProviderOptions {\n config: OidcConfig;\n /** Provider id that started the flow; signed into and verified from state. */\n providerId: string;\n client?: OidcClientLike;\n now?: () => number;\n /** Injectable for offline tests; defaults to the DNS-checking validator. */\n validateEndpoint?: (\n endpoint: string,\n options: { allowInsecure: boolean },\n ) => Promise<URL>;\n}\n\nfunction stringClaim(\n claims: Record<string, unknown>,\n ...names: string[]\n): string | undefined {\n for (const name of names) {\n const value = claims[name];\n if (typeof value === \"string\" && value.trim()) return value;\n }\n return undefined;\n}\n\nfunction providerError(message: string, cause: unknown): AuthError {\n const error = new AuthError(\"PROVIDER_ERROR\", 502, message);\n if (cause !== undefined) {\n Object.defineProperty(error, \"cause\", {\n value: cause,\n enumerable: false,\n configurable: true,\n writable: true,\n });\n }\n return error;\n}\n\nexport function principalFromClaims(\n claims: Record<string, unknown>,\n): Principal {\n const subject = stringClaim(claims, \"oid\", \"sub\");\n if (!subject) {\n throw new AuthError(\"PROVIDER_ERROR\", 502, \"OIDC ID token has no subject\");\n }\n const emailClaim = stringClaim(claims, \"email\");\n const rawEmail =\n emailClaim ?? stringClaim(claims, \"preferred_username\", \"upn\");\n const email = rawEmail?.trim().toLowerCase();\n const name = stringClaim(claims, \"name\");\n const picture = stringClaim(claims, \"picture\");\n return {\n subject,\n method: \"oidc\",\n ...(email ? { email } : {}),\n ...(email\n ? { emailVerified: emailClaim !== undefined && claims.email_verified === true }\n : {}),\n ...(name ? { name } : {}),\n ...(picture ? { picture } : {}),\n claims,\n };\n}\n\nexport class OidcAuthProvider implements AuthProvider {\n readonly descriptor: AuthProviderDescriptor;\n private configurationPromise?: Promise<Configuration>;\n\n constructor(private readonly options: OidcAuthProviderOptions) {\n this.descriptor = oidcDescriptor(options.config);\n }\n\n private async getClient(): Promise<OidcClientLike> {\n return this.options.client ?? (await loadOpenidClient());\n }\n\n private async getConfiguration(client: OidcClientLike): Promise<Configuration> {\n if (!this.configurationPromise) {\n this.configurationPromise = (async () => {\n try {\n const validateEndpoint =\n this.options.validateEndpoint ?? assertSafeOidcEndpoint;\n const safety = {\n allowInsecure: this.options.config.allowInsecure,\n allowedPrivateHosts:\n this.options.config.allowedPrivateHosts ?? [],\n };\n await validateEndpoint(this.options.config.issuer, safety);\n const discovered = await client.discovery(\n new URL(this.options.config.issuer),\n this.options.config.clientId,\n this.options.config.clientSecret,\n {\n timeoutSeconds: 5,\n allowInsecure: this.options.config.allowInsecure,\n },\n );\n const metadata = discovered.serverMetadata();\n const { authorization_endpoint, token_endpoint, jwks_uri } = metadata;\n if (\n typeof authorization_endpoint !== \"string\" ||\n !authorization_endpoint ||\n typeof token_endpoint !== \"string\" ||\n !token_endpoint ||\n typeof jwks_uri !== \"string\" ||\n !jwks_uri\n ) {\n throw new Error(\"OIDC discovery returned incomplete metadata\");\n }\n for (const endpoint of [\n authorization_endpoint,\n token_endpoint,\n jwks_uri,\n ]) {\n await validateEndpoint(endpoint, safety);\n }\n return discovered;\n } catch (error) {\n throw providerError(\"OIDC discovery failed\", error);\n }\n })().catch((error: unknown) => {\n this.configurationPromise = undefined;\n throw error;\n });\n }\n return this.configurationPromise;\n }\n\n async begin(): Promise<AuthChallenge> {\n try {\n const client = await this.getClient();\n const config = await this.getConfiguration(client);\n const state = client.randomState();\n const codeVerifier = client.randomPKCECodeVerifier();\n const codeChallenge = await client.calculatePKCECodeChallenge(codeVerifier);\n const nonce = client.randomNonce();\n const url = client.buildAuthorizationUrl(config, {\n redirect_uri: this.options.config.redirectUri,\n response_type: \"code\",\n scope: this.options.config.scopes,\n state,\n nonce,\n code_challenge: codeChallenge,\n code_challenge_method: \"S256\",\n });\n const now = this.options.now?.() ?? Date.now();\n const cookieValue = signStateCookie(\n {\n state,\n codeVerifier,\n nonce,\n providerId: this.options.providerId,\n exp: now + 600_000,\n },\n this.options.config.stateSecret,\n );\n return {\n kind: \"redirect\",\n url: url.href,\n cookies: [\n stateCookie(cookieValue, { secure: !this.options.config.insecureCookies }),\n ],\n };\n } catch (error) {\n if (error instanceof AuthError) throw error;\n throw providerError(\"OIDC authorization request failed\", error);\n }\n }\n\n async callback(ctx: AuthRequestContext): Promise<Principal> {\n try {\n const rawCookie = ctx.cookies?.[OIDC_STATE_COOKIE];\n if (!rawCookie) {\n throw new AuthError(\"INVALID_STATE\", 400, \"OIDC state cookie is missing\");\n }\n const now = this.options.now?.() ?? Date.now();\n const stored = verifyStateCookie(\n rawCookie,\n this.options.config.stateSecret,\n now,\n );\n if (!stored) {\n throw new AuthError(\n \"INVALID_STATE\",\n 400,\n \"OIDC state cookie is invalid or expired\",\n );\n }\n if (stored.providerId !== this.options.providerId) {\n throw new AuthError(\n \"INVALID_STATE\",\n 400,\n \"OIDC state provider does not match\",\n );\n }\n if (ctx.params?.state !== stored.state) {\n throw new AuthError(\"INVALID_STATE\", 400, \"OIDC state does not match\");\n }\n\n const client = await this.getClient();\n const config = await this.getConfiguration(client);\n const currentUrl = new URL(this.options.config.redirectUri);\n for (const [key, value] of Object.entries(ctx.params ?? {})) {\n currentUrl.searchParams.set(key, value);\n }\n\n const tokens = await client.authorizationCodeGrant(config, currentUrl, {\n pkceCodeVerifier: stored.codeVerifier,\n expectedState: stored.state,\n expectedNonce: stored.nonce,\n });\n const claims = tokens.claims();\n if (!claims) {\n throw new AuthError(\"PROVIDER_ERROR\", 502, \"OIDC ID token is missing\");\n }\n return principalFromClaims(claims);\n } catch (error) {\n if (error instanceof AuthError) throw error;\n throw providerError(\"OIDC token exchange failed\", error);\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axiom-lattice/auth-oidc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Generic OpenID Connect authentication provider for Axiom Lattice",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"openid-client": "^6.8.9",
|
|
21
|
-
"@axiom-lattice/protocols": "7.
|
|
21
|
+
"@axiom-lattice/protocols": "7.4.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/jest": "^29.5.14",
|