@lalternative/auth 1.0.0 → 1.0.1

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/urbangate.js CHANGED
@@ -79,7 +79,9 @@ var Exchange = class {
79
79
  const form = new URLSearchParams({
80
80
  grant_type: JWT_BEARER,
81
81
  assertion: answer.assertion,
82
- audience: this.config.product
82
+ audience: this.config.product,
83
+ client_id: this.config.admin.clientId,
84
+ client_secret: this.config.admin.clientSecret
83
85
  });
84
86
  let res;
85
87
  try {
@@ -88,11 +90,7 @@ var Exchange = class {
88
90
  {
89
91
  method: "POST",
90
92
  headers: {
91
- "content-type": "application/x-www-form-urlencoded",
92
- authorization: basic(
93
- this.config.admin.clientId,
94
- this.config.admin.clientSecret
95
- )
93
+ "content-type": "application/x-www-form-urlencoded"
96
94
  },
97
95
  body: form
98
96
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/urbangate/cookies.ts","../src/urbangate/exchange.ts","../src/urbangate/kratos.ts","../src/urbangate/server.ts"],"sourcesContent":["export interface CookieOptions {\n maxAge: number;\n secure: boolean;\n}\n\nexport function readCookie(headers: Headers, name: string): string | undefined {\n const raw = headers.get(\"cookie\") ?? \"\";\n for (const part of raw.split(\";\")) {\n const [k, ...v] = part.trim().split(\"=\");\n if (k === name) return decodeURIComponent(v.join(\"=\"));\n }\n return undefined;\n}\n\nexport function serializeCookie(\n name: string,\n value: string,\n o: CookieOptions,\n): string {\n const attrs = [\n `${name}=${encodeURIComponent(value)}`,\n \"Path=/\",\n \"HttpOnly\",\n \"SameSite=Lax\",\n `Max-Age=${value ? o.maxAge : 0}`,\n ];\n if (o.secure) attrs.push(\"Secure\");\n return attrs.join(\"; \");\n}\n\nexport function clearCookie(name: string, secure: boolean): string {\n return serializeCookie(name, \"\", { maxAge: 0, secure });\n}\n","import type { Fetch } from \"./kratos\";\n\nexport interface ExchangeConfig {\n product: string;\n issuerUrl: string;\n provisioner: { clientId: string; clientSecret: string };\n admin: { clientId: string; clientSecret: string };\n}\n\nexport interface PersonToken {\n accessToken: string;\n expiresAt: number;\n identityId: string;\n roles: Array<string>;\n}\n\nexport type ExchangeOutcome =\n | { status: \"ok\"; token: PersonToken }\n | { status: \"session_gone\" }\n | { status: \"inactive\" }\n | { status: \"unavailable\"; reason: string };\n\nconst JWT_BEARER = \"urn:ietf:params:oauth:grant-type:jwt-bearer\";\nconst EXCHANGE_SCOPE = \"urbangate:sessions:exchange\";\nconst REFRESH_MARGIN_MS = 60_000;\n\nfunction basic(id: string, secret: string) {\n return \"Basic \" + btoa(`${id}:${secret}`);\n}\n\n/**\n * Turns a Kratos session token into the person's Hydra access token, in the\n * three calls ADR 0009 of urbangate describes: a machine token for the\n * provisioner, the assertion from urbangate, the token from Hydra with the\n * product's own admin client. Hydra issues no refresh token on this grant,\n * so the caller exchanges again when the token nears its end.\n */\nexport class Exchange {\n private machine: { token: string; expiresAt: number } | null = null;\n\n private readonly config: ExchangeConfig;\n private readonly fetchImpl: Fetch;\n\n constructor(config: ExchangeConfig, fetchImpl: Fetch = fetch) {\n this.config = config;\n this.fetchImpl = fetchImpl;\n }\n\n needsRefresh(token: PersonToken | null, now = Date.now()): boolean {\n return !token || token.expiresAt - now < REFRESH_MARGIN_MS;\n }\n\n async exchange(sessionToken: string): Promise<ExchangeOutcome> {\n const machine = await this.machineToken();\n if (!machine) return { status: \"unavailable\", reason: \"machine_token\" };\n const url = new URL(\n \"/api/machine/sessions/exchange\",\n this.config.issuerUrl,\n );\n let res: Response;\n try {\n res = await this.fetchImpl(url, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${machine}`,\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({ session_token: sessionToken }),\n });\n } catch {\n return { status: \"unavailable\", reason: \"exchange\" };\n }\n if (res.status === 401) {\n const body = (await res.json().catch(() => ({}))) as { error?: string };\n // Our own credential was refused: fetch a fresh one next time.\n if (body.error === \"invalid_token\") this.machine = null;\n return body.error === \"session\"\n ? { status: \"session_gone\" }\n : { status: \"unavailable\", reason: \"exchange_401\" };\n }\n if (res.status === 403) return { status: \"inactive\" };\n if (!res.ok)\n return { status: \"unavailable\", reason: `exchange_${res.status}` };\n const answer = (await res.json()) as {\n assertion: string;\n identity_id: string;\n roles: Array<string>;\n };\n return this.hydraToken(answer);\n }\n\n private async hydraToken(answer: {\n assertion: string;\n identity_id: string;\n roles: Array<string>;\n }): Promise<ExchangeOutcome> {\n const form = new URLSearchParams({\n grant_type: JWT_BEARER,\n assertion: answer.assertion,\n audience: this.config.product,\n });\n let res: Response;\n try {\n res = await this.fetchImpl(\n new URL(\"/oauth2/token\", this.config.issuerUrl),\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/x-www-form-urlencoded\",\n authorization: basic(\n this.config.admin.clientId,\n this.config.admin.clientSecret,\n ),\n },\n body: form,\n },\n );\n } catch {\n return { status: \"unavailable\", reason: \"hydra\" };\n }\n if (!res.ok)\n return { status: \"unavailable\", reason: `hydra_${res.status}` };\n const body = (await res.json()) as {\n access_token: string;\n expires_in: number;\n };\n return {\n status: \"ok\",\n token: {\n accessToken: body.access_token,\n expiresAt: Date.now() + body.expires_in * 1000,\n identityId: answer.identity_id,\n roles: answer.roles,\n },\n };\n }\n\n private async machineToken(): Promise<string | null> {\n if (\n this.machine &&\n this.machine.expiresAt - Date.now() > REFRESH_MARGIN_MS\n ) {\n return this.machine.token;\n }\n let res: Response;\n try {\n res = await this.fetchImpl(\n new URL(\"/oauth2/token\", this.config.issuerUrl),\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/x-www-form-urlencoded\",\n authorization: basic(\n this.config.provisioner.clientId,\n this.config.provisioner.clientSecret,\n ),\n },\n body: new URLSearchParams({\n grant_type: \"client_credentials\",\n scope: EXCHANGE_SCOPE,\n audience: \"urbangate\",\n }),\n },\n );\n } catch {\n return null;\n }\n if (!res.ok) return null;\n const body = (await res.json()) as {\n access_token: string;\n expires_in: number;\n };\n this.machine = {\n token: body.access_token,\n expiresAt: Date.now() + body.expires_in * 1000,\n };\n return this.machine.token;\n }\n}\n\nexport function decodeToken(\n raw: string,\n): { identityId: string; roles: Array<string>; expiresAt: number } | null {\n const parts = raw.split(\".\");\n if (parts.length !== 3) return null;\n try {\n const b64 = parts[1].replace(/-/g, \"+\").replace(/_/g, \"/\");\n const payload = JSON.parse(decodeURIComponent(escape(atob(b64)))) as {\n sub?: string;\n roles?: unknown;\n exp?: number;\n };\n if (!payload.sub || !payload.exp) return null;\n return {\n identityId: payload.sub,\n roles: Array.isArray(payload.roles)\n ? payload.roles.filter((r): r is string => typeof r === \"string\")\n : [],\n expiresAt: payload.exp * 1000,\n };\n } catch {\n return null;\n }\n}\n","export type Fetch = typeof fetch;\n\nexport interface KratosFlow {\n id: string;\n ui?: {\n messages?: Array<KratosMessage>;\n nodes?: Array<{\n attributes?: { name?: string; value?: unknown };\n messages?: Array<KratosMessage>;\n }>;\n };\n state?: string;\n}\n\nexport interface KratosMessage {\n id?: number;\n text?: string;\n type?: string;\n}\n\nexport interface KratosSession {\n id: string;\n active?: boolean;\n expires_at?: string;\n authenticated_at?: string;\n identity?: {\n id: string;\n state?: string;\n traits?: { email?: string; name?: string };\n verifiable_addresses?: Array<{ value: string; verified: boolean }>;\n };\n}\n\nexport interface KratosSessionResult {\n session_token?: string;\n session?: KratosSession;\n continue_with?: Array<ContinueWith>;\n}\n\nexport type ContinueWith =\n | { action: \"show_verification_ui\"; flow: { id: string } }\n | { action: \"show_settings_ui\"; flow: { id: string } }\n | { action: \"set_ory_session_token\"; ory_session_token: string }\n | { action: string };\n\nexport type KratosFailure =\n | { status: \"invalid_credentials\" }\n | { status: \"invalid_code\" }\n | { status: \"email_not_verified\" }\n | { status: \"second_factor_required\" }\n | { status: \"account_disabled\" }\n | { status: \"already_registered\" }\n | { status: \"password_refused\"; message: string }\n | { status: \"invalid_input\"; message: string }\n | { status: \"flow_expired\" }\n | { status: \"unavailable\" };\n\nexport class KratosError extends Error {\n failure: KratosFailure;\n constructor(failure: KratosFailure) {\n super(failure.status);\n this.failure = failure;\n }\n}\n\n// Kratos' message ids, from its text catalogue. A wrong password and an\n// unknown address share one id on purpose.\nconst INVALID_CREDENTIALS = 4000006;\nconst INVALID_CODE = new Set([4010008, 4060006, 4070006]);\nconst CODE_SENT = new Set([1010014, 1040005, 1060003, 1070003, 1080003]);\nconst ALREADY_REGISTERED = 4000007;\nconst PASSWORD_POLICY = new Set([4000005, 4000031, 4000032, 4000033, 4000034]);\nconst FLOW_EXPIRED = new Set([4010001, 4040001, 4060005, 4070005]);\n\nexport function failureOf(status: number, body: unknown): KratosFailure {\n const b = (body ?? {}) as {\n error?: { id?: string; code?: number; message?: string };\n ui?: KratosFlow[\"ui\"];\n };\n if (status === 410 || FLOW_EXPIRED.has(b.error?.code ?? -1)) {\n return { status: \"flow_expired\" };\n }\n if (b.error?.id === \"session_aal2_required\") {\n return { status: \"second_factor_required\" };\n }\n if (b.error?.id === \"self_service_flow_expired\")\n return { status: \"flow_expired\" };\n if (status >= 500 || status === 0) return { status: \"unavailable\" };\n const messages = [\n ...(b.ui?.messages ?? []),\n ...(b.ui?.nodes ?? []).flatMap((n) => n.messages ?? []),\n ].filter((m) => m.type === \"error\");\n for (const m of messages) {\n if (m.id === INVALID_CREDENTIALS) return { status: \"invalid_credentials\" };\n if (INVALID_CODE.has(m.id ?? -1)) return { status: \"invalid_code\" };\n if (m.id === ALREADY_REGISTERED) return { status: \"already_registered\" };\n if (PASSWORD_POLICY.has(m.id ?? -1)) {\n return { status: \"password_refused\", message: m.text ?? \"\" };\n }\n if (FLOW_EXPIRED.has(m.id ?? -1)) return { status: \"flow_expired\" };\n }\n if (b.error?.id === \"account_disabled\" || status === 401) {\n return { status: \"account_disabled\" };\n }\n const first = messages[0]?.text ?? b.error?.message ?? \"\";\n return { status: \"invalid_input\", message: first };\n}\n\nexport function codeWasSent(flow: KratosFlow): boolean {\n return (flow.ui?.messages ?? []).some((m) => CODE_SENT.has(m.id ?? -1));\n}\n\nexport function csrfOf(flow: KratosFlow): string | undefined {\n const node = flow.ui?.nodes?.find((n) => n.attributes?.name === \"csrf_token\");\n return typeof node?.attributes?.value === \"string\"\n ? node.attributes.value\n : undefined;\n}\n\nexport type FlowKind =\n \"login\" | \"registration\" | \"verification\" | \"recovery\" | \"settings\";\n\n/**\n * Kratos' native (API) flows, the ones an app that renders its own screens\n * drives from its server: no cookies, no CSRF, a session token in the answer.\n */\nexport class KratosFlows {\n private readonly baseUrl: string;\n private readonly fetchImpl: Fetch;\n\n constructor(baseUrl: string, fetchImpl: Fetch = fetch) {\n this.baseUrl = baseUrl;\n this.fetchImpl = fetchImpl;\n }\n\n async start(\n kind: FlowKind,\n sessionToken?: string,\n params: Record<string, string> = {},\n ): Promise<KratosFlow> {\n const url = new URL(`/self-service/${kind}/api`, this.baseUrl);\n for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);\n const res = await this.call(url, { method: \"GET\" }, sessionToken);\n return (await this.body(res)) as KratosFlow;\n }\n\n async submit<T = KratosFlow>(\n kind: FlowKind,\n flowId: string,\n body: Record<string, unknown>,\n sessionToken?: string,\n ): Promise<T> {\n const url = new URL(`/self-service/${kind}`, this.baseUrl);\n url.searchParams.set(\"flow\", flowId);\n const res = await this.call(\n url,\n {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n },\n sessionToken,\n );\n return (await this.body(res)) as T;\n }\n\n async whoami(sessionToken: string): Promise<KratosSession | null> {\n const url = new URL(\"/sessions/whoami\", this.baseUrl);\n let res: Response;\n try {\n res = await this.fetchImpl(url, {\n headers: { \"x-session-token\": sessionToken },\n });\n } catch {\n throw new KratosError({ status: \"unavailable\" });\n }\n if (res.status === 401 || res.status === 403) return null;\n if (!res.ok) throw new KratosError({ status: \"unavailable\" });\n return (await res.json()) as KratosSession;\n }\n\n async logout(sessionToken: string): Promise<void> {\n const url = new URL(\"/self-service/logout/api\", this.baseUrl);\n try {\n await this.fetchImpl(url, {\n method: \"DELETE\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ session_token: sessionToken }),\n });\n } catch {\n // The cookie is dropped regardless; a token Kratos still knows expires\n // on its own, and the person is signed out of this app either way.\n }\n }\n\n private async call(\n url: URL,\n init: RequestInit,\n sessionToken?: string,\n ): Promise<Response> {\n const headers = new Headers(init.headers);\n headers.set(\"accept\", \"application/json\");\n if (sessionToken) headers.set(\"x-session-token\", sessionToken);\n try {\n return await this.fetchImpl(url, { ...init, headers });\n } catch {\n throw new KratosError({ status: \"unavailable\" });\n }\n }\n\n private async body(res: Response): Promise<unknown> {\n const body = await res.json().catch(() => null);\n // A recovery code that verified answers 422 with what to do next, which\n // is a success shaped as a refusal.\n if (\n res.ok ||\n (res.status === 422 && body && \"continue_with\" in (body as object))\n ) {\n return body;\n }\n // A submitted flow that still has errors comes back as the flow itself\n // with a 400, and the messages say which field.\n throw new KratosError(failureOf(res.status, body));\n }\n}\n","import { forwardHeaders } from \"../core-proxy.ts\";\nimport { clearCookie, readCookie, serializeCookie } from \"./cookies.ts\";\nimport { Exchange, decodeToken } from \"./exchange.ts\";\nimport type { ExchangeConfig, PersonToken } from \"./exchange.ts\";\nimport { KratosError, KratosFlows, codeWasSent } from \"./kratos.ts\";\nimport type {\n ContinueWith,\n Fetch,\n KratosFailure,\n KratosSession,\n KratosSessionResult,\n} from \"./kratos.ts\";\n\nexport interface UrbangateAuthConfig {\n product: string;\n /** Kratos' public API as the server reaches it, e.g. http://kratos:4433 or https://id.urbangate.dev. */\n kratosUrl: string;\n urbangate: Omit<ExchangeConfig, \"product\">;\n cookie?: { name?: string; secure?: boolean };\n fetch?: Fetch;\n}\n\nexport interface UrbangateUser {\n id: string;\n identityId: string;\n email: string;\n emailVerified: boolean;\n name: string;\n role: \"admin\" | \"user\";\n}\n\nexport interface UrbangateSession {\n user: UrbangateUser;\n session: { expiresAt: string };\n}\n\nexport interface AccessToken {\n token: string;\n setCookie?: string;\n}\n\nexport interface UrbangateCoreProxyOptions {\n coreUrl: string;\n adminOnly?: boolean;\n}\n\nexport interface UrbangateAuth {\n handler(request: Request): Promise<Response>;\n getSession(headers: Headers): Promise<UrbangateSession | null>;\n accessToken(headers: Headers): Promise<AccessToken | null>;\n coreProxy(\n options: UrbangateCoreProxyOptions,\n ): (request: Request) => Promise<Response>;\n}\n\nconst SESSION_MAX_AGE = 30 * 24 * 60 * 60;\nconst FLOW_MAX_AGE = 600;\nconst TOKEN_MAX_AGE = 15 * 60;\n\ntype OtpType = \"email-verification\" | \"forget-password\" | \"sign-in\";\n\nconst FLOW_OF_TYPE: Record<OtpType, \"verification\" | \"recovery\" | \"login\"> = {\n \"email-verification\": \"verification\",\n \"forget-password\": \"recovery\",\n \"sign-in\": \"login\",\n};\n\nconst FAILURE_STATUS: Record<KratosFailure[\"status\"], number> = {\n invalid_credentials: 401,\n invalid_code: 400,\n email_not_verified: 403,\n second_factor_required: 403,\n account_disabled: 403,\n already_registered: 409,\n password_refused: 422,\n invalid_input: 400,\n flow_expired: 410,\n unavailable: 503,\n};\n\nfunction json(\n status: number,\n body: unknown,\n cookies: Array<string> = [],\n): Response {\n const headers = new Headers({ \"content-type\": \"application/json\" });\n for (const c of cookies) headers.append(\"set-cookie\", c);\n return new Response(JSON.stringify(body), { status, headers });\n}\n\nfunction failure(code: string, status: number, message?: string): Response {\n return json(status, {\n error: { code, status, ...(message ? { message } : {}) },\n });\n}\n\nfunction failed(error: unknown): Response {\n if (error instanceof KratosError) {\n const f = error.failure;\n return failure(\n f.status,\n FAILURE_STATUS[f.status],\n \"message\" in f ? f.message : undefined,\n );\n }\n return failure(\"unavailable\", 503);\n}\n\nfunction userOf(\n session: KratosSession,\n roles: Array<string>,\n product: string,\n): UrbangateUser | null {\n const identity = session.identity;\n if (!identity) return null;\n const email = identity.traits?.email ?? \"\";\n const address = identity.verifiable_addresses?.find((a) => a.value === email);\n return {\n id: identity.id,\n identityId: identity.id,\n email,\n emailVerified: address?.verified ?? false,\n name: identity.traits?.name ?? \"\",\n role: roles.includes(`${product}:admin`) ? \"admin\" : \"user\",\n };\n}\n\nfunction continueWith<A extends ContinueWith[\"action\"]>(\n list: Array<ContinueWith> | undefined,\n action: A,\n): Extract<ContinueWith, { action: A }> | undefined {\n return list?.find(\n (c): c is Extract<ContinueWith, { action: A }> => c.action === action,\n );\n}\n\nexport function createUrbangateAuth(\n config: UrbangateAuthConfig,\n): UrbangateAuth {\n const product = config.product;\n const kratos = new KratosFlows(config.kratosUrl, config.fetch);\n const exchange = new Exchange({ ...config.urbangate, product }, config.fetch);\n const names = {\n session: config.cookie?.name ?? `${product}_session`,\n token: `${product}_token`,\n flow: `${product}_flow`,\n };\n const secure =\n config.cookie?.secure ?? config.urbangate.issuerUrl.startsWith(\"https://\");\n\n const cookie = (name: string, value: string, maxAge: number) =>\n serializeCookie(name, value, { maxAge, secure });\n\n const signedIn = (result: KratosSessionResult): Array<string> => {\n const token = result.session_token;\n if (!token) return [];\n return [\n cookie(names.session, token, SESSION_MAX_AGE),\n clearCookie(names.flow, secure),\n ];\n };\n\n const readToken = (headers: Headers): PersonToken | null => {\n const raw = readCookie(headers, names.token);\n const decoded = raw ? decodeToken(raw) : null;\n return raw && decoded ? { accessToken: raw, ...decoded } : null;\n };\n\n async function accessToken(headers: Headers): Promise<AccessToken | null> {\n const sessionToken = readCookie(headers, names.session);\n if (!sessionToken) return null;\n const held = readToken(headers);\n if (held && !exchange.needsRefresh(held))\n return { token: held.accessToken };\n const outcome = await exchange.exchange(sessionToken);\n if (outcome.status === \"session_gone\" || outcome.status === \"inactive\")\n return null;\n if (outcome.status === \"unavailable\")\n throw new KratosError({ status: \"unavailable\" });\n return {\n token: outcome.token.accessToken,\n setCookie: cookie(names.token, outcome.token.accessToken, TOKEN_MAX_AGE),\n };\n }\n\n async function getSession(\n headers: Headers,\n ): Promise<UrbangateSession | null> {\n const sessionToken = readCookie(headers, names.session);\n if (!sessionToken) return null;\n const session = await kratos.whoami(sessionToken);\n if (!session?.active) return null;\n const held = readToken(headers);\n const user = userOf(session, held?.roles ?? [], product);\n if (!user) return null;\n return { user, session: { expiresAt: session.expires_at ?? \"\" } };\n }\n\n async function body(request: Request): Promise<Record<string, unknown>> {\n const raw = (await request.json().catch(() => null)) as Record<\n string,\n unknown\n > | null;\n return raw ?? {};\n }\n\n const str = (b: Record<string, unknown>, key: string) =>\n typeof b[key] === \"string\" ? (b[key] as string).trim() : \"\";\n\n async function signInEmail(request: Request): Promise<Response> {\n const b = await body(request);\n const email = str(b, \"email\");\n const password = typeof b.password === \"string\" ? b.password : \"\";\n if (!email || !password)\n return failure(\"invalid_input\", 400, \"email and password are required\");\n const flow = await kratos.start(\"login\");\n const result = await kratos.submit<KratosSessionResult>(\"login\", flow.id, {\n method: \"password\",\n identifier: email,\n password,\n });\n return json(\n 200,\n { user: result.session ? userOf(result.session, [], product) : null },\n signedIn(result),\n );\n }\n\n async function signUpEmail(request: Request): Promise<Response> {\n const b = await body(request);\n const email = str(b, \"email\");\n const password = typeof b.password === \"string\" ? b.password : \"\";\n if (!email || !password)\n return failure(\"invalid_input\", 400, \"email and password are required\");\n const flow = await kratos.start(\"registration\");\n const result = await kratos.submit<KratosSessionResult>(\n \"registration\",\n flow.id,\n {\n method: \"password\",\n traits: { email, ...(str(b, \"name\") ? { name: str(b, \"name\") } : {}) },\n password,\n },\n );\n const verification = continueWith(\n result.continue_with,\n \"show_verification_ui\",\n );\n const cookies = signedIn(result);\n if (verification)\n cookies.push(\n cookie(\n names.flow,\n `verification:${verification.flow.id}`,\n FLOW_MAX_AGE,\n ),\n );\n return json(\n 200,\n {\n user: result.session ? userOf(result.session, [], product) : null,\n ...(verification\n ? { verification: { flowId: verification.flow.id } }\n : {}),\n },\n cookies,\n );\n }\n\n async function sendOtp(request: Request): Promise<Response> {\n const b = await body(request);\n const email = str(b, \"email\");\n const type = str(b, \"type\") as OtpType;\n const kind = FLOW_OF_TYPE[type];\n if (!email || !kind)\n return failure(\"invalid_input\", 400, \"email and type are required\");\n const flow = await kratos.start(kind);\n const submitted = await kratos.submit(kind, flow.id, {\n method: \"code\",\n ...(kind === \"login\" ? { identifier: email } : { email }),\n });\n if (!codeWasSent(submitted) && submitted.state !== \"sent_email\") {\n return failure(\"invalid_input\", 400, \"the code could not be sent\");\n }\n return json(200, { sent: true }, [\n cookie(names.flow, `${kind}:${flow.id}`, FLOW_MAX_AGE),\n ]);\n }\n\n function pendingFlow(headers: Headers, kind: string): string | null {\n const raw = readCookie(headers, names.flow) ?? \"\";\n const [k, id] = raw.split(\":\");\n return k === kind && id ? id : null;\n }\n\n async function signInOtp(request: Request): Promise<Response> {\n const b = await body(request);\n const email = str(b, \"email\");\n const code = str(b, \"otp\");\n const flowId = pendingFlow(request.headers, \"login\");\n if (!flowId) return failure(\"flow_expired\", 410);\n if (!email || !code)\n return failure(\"invalid_input\", 400, \"email and otp are required\");\n const result = await kratos.submit<KratosSessionResult>(\"login\", flowId, {\n method: \"code\",\n identifier: email,\n code,\n });\n return json(\n 200,\n { user: result.session ? userOf(result.session, [], product) : null },\n signedIn(result),\n );\n }\n\n async function verifyEmail(request: Request): Promise<Response> {\n const b = await body(request);\n const code = str(b, \"otp\");\n const flowId = pendingFlow(request.headers, \"verification\");\n if (!flowId) return failure(\"flow_expired\", 410);\n if (!code) return failure(\"invalid_input\", 400, \"otp is required\");\n const flow = await kratos.submit(\"verification\", flowId, {\n method: \"code\",\n code,\n });\n if (flow.state !== \"passed_challenge\") return failure(\"invalid_code\", 400);\n return json(200, { verified: true }, [clearCookie(names.flow, secure)]);\n }\n\n async function resetPassword(request: Request): Promise<Response> {\n const b = await body(request);\n const code = str(b, \"otp\");\n const password = typeof b.password === \"string\" ? b.password : \"\";\n const flowId = pendingFlow(request.headers, \"recovery\");\n if (!flowId) return failure(\"flow_expired\", 410);\n if (!code || !password)\n return failure(\"invalid_input\", 400, \"otp and password are required\");\n const recovered = await kratos.submit<KratosSessionResult>(\n \"recovery\",\n flowId,\n { method: \"code\", code },\n );\n const token = continueWith(\n recovered.continue_with,\n \"set_ory_session_token\",\n )?.ory_session_token;\n const settings = continueWith(recovered.continue_with, \"show_settings_ui\");\n if (!token || !settings) return failure(\"invalid_code\", 400);\n await kratos.submit(\n \"settings\",\n settings.flow.id,\n { method: \"password\", password },\n token,\n );\n return json(200, { reset: true }, [\n cookie(names.session, token, SESSION_MAX_AGE),\n clearCookie(names.flow, secure),\n ]);\n }\n\n async function signOut(request: Request): Promise<Response> {\n const token = readCookie(request.headers, names.session);\n if (token) await kratos.logout(token);\n return json(200, { signedOut: true }, [\n clearCookie(names.session, secure),\n clearCookie(names.token, secure),\n clearCookie(names.flow, secure),\n ]);\n }\n\n async function session(request: Request): Promise<Response> {\n const s = await getSession(request.headers);\n return json(200, s);\n }\n\n const routes: Record<string, (request: Request) => Promise<Response>> = {\n \"POST sign-in/email\": signInEmail,\n \"POST sign-in/email-otp\": signInOtp,\n \"POST sign-up/email\": signUpEmail,\n \"POST email-otp/send-verification-otp\": sendOtp,\n \"POST email-otp/verify-email\": verifyEmail,\n \"POST email-otp/reset-password\": resetPassword,\n \"POST sign-out\": signOut,\n \"GET get-session\": session,\n };\n\n async function handler(request: Request): Promise<Response> {\n const path = new URL(request.url).pathname.replace(/\\/$/, \"\");\n const suffix = path.slice(path.indexOf(\"/api/auth/\") + \"/api/auth/\".length);\n if (suffix === \"sign-in/social\") return failure(\"not_supported\", 501);\n const route = routes[`${request.method} ${suffix}`];\n if (!route) return failure(\"not_found\", 404);\n try {\n return await route(request);\n } catch (error) {\n return failed(error);\n }\n }\n\n function coreProxy(options: UrbangateCoreProxyOptions) {\n const coreUrl = options.coreUrl.replace(/\\/$/, \"\");\n return async (request: Request): Promise<Response> => {\n let s: UrbangateSession | null;\n let token: AccessToken | null;\n try {\n token = await accessToken(request.headers);\n s = token\n ? await getSession(\n new Headers({ ...cookieHeader(request.headers, names, token) }),\n )\n : null;\n } catch {\n return failure(\"identity_provider_unavailable\", 503);\n }\n if (!token || !s) return failure(\"sign_in_required\", 401);\n if (options.adminOnly && s.user.role !== \"admin\")\n return failure(\"forbidden\", 403);\n const url = new URL(request.url);\n const headers = forwardHeaders(request.headers);\n headers.set(\"authorization\", `Bearer ${token.token}`);\n const upstream = await fetch(`${coreUrl}${url.pathname}${url.search}`, {\n method: request.method,\n headers,\n body:\n request.method === \"GET\" || request.method === \"HEAD\"\n ? undefined\n : request.body,\n duplex: \"half\",\n redirect: \"manual\",\n } as RequestInit);\n const out = forwardHeaders(upstream.headers);\n if (token.setCookie) out.append(\"set-cookie\", token.setCookie);\n return new Response(upstream.body, {\n status: upstream.status,\n headers: out,\n });\n };\n }\n\n return { handler, getSession, accessToken, coreProxy };\n}\n\n// A token just exchanged is not in the request's cookie yet; the session\n// lookup reads the roles off it as if it were.\nfunction cookieHeader(\n headers: Headers,\n names: { session: string; token: string },\n token: AccessToken,\n) {\n const session = readCookie(headers, names.session) ?? \"\";\n return {\n cookie: `${names.session}=${encodeURIComponent(session)}; ${names.token}=${encodeURIComponent(token.token)}`,\n };\n}\n"],"mappings":";;;;;AAKO,SAAS,WAAW,SAAkB,MAAkC;AAC7E,QAAM,MAAM,QAAQ,IAAI,QAAQ,KAAK;AACrC,aAAW,QAAQ,IAAI,MAAM,GAAG,GAAG;AACjC,UAAM,CAAC,GAAG,GAAG,CAAC,IAAI,KAAK,KAAK,EAAE,MAAM,GAAG;AACvC,QAAI,MAAM,KAAM,QAAO,mBAAmB,EAAE,KAAK,GAAG,CAAC;AAAA,EACvD;AACA,SAAO;AACT;AAEO,SAAS,gBACd,MACA,OACA,GACQ;AACR,QAAM,QAAQ;AAAA,IACZ,GAAG,IAAI,IAAI,mBAAmB,KAAK,CAAC;AAAA,IACpC;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,QAAQ,EAAE,SAAS,CAAC;AAAA,EACjC;AACA,MAAI,EAAE,OAAQ,OAAM,KAAK,QAAQ;AACjC,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,SAAS,YAAY,MAAc,QAAyB;AACjE,SAAO,gBAAgB,MAAM,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;AACxD;;;ACVA,IAAM,aAAa;AACnB,IAAM,iBAAiB;AACvB,IAAM,oBAAoB;AAE1B,SAAS,MAAM,IAAY,QAAgB;AACzC,SAAO,WAAW,KAAK,GAAG,EAAE,IAAI,MAAM,EAAE;AAC1C;AASO,IAAM,WAAN,MAAe;AAAA,EACZ,UAAuD;AAAA,EAE9C;AAAA,EACA;AAAA,EAEjB,YAAY,QAAwB,YAAmB,OAAO;AAC5D,SAAK,SAAS;AACd,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,aAAa,OAA2B,MAAM,KAAK,IAAI,GAAY;AACjE,WAAO,CAAC,SAAS,MAAM,YAAY,MAAM;AAAA,EAC3C;AAAA,EAEA,MAAM,SAAS,cAAgD;AAC7D,UAAM,UAAU,MAAM,KAAK,aAAa;AACxC,QAAI,CAAC,QAAS,QAAO,EAAE,QAAQ,eAAe,QAAQ,gBAAgB;AACtE,UAAM,MAAM,IAAI;AAAA,MACd;AAAA,MACA,KAAK,OAAO;AAAA,IACd;AACA,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,KAAK,UAAU,KAAK;AAAA,QAC9B,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,eAAe,UAAU,OAAO;AAAA,UAChC,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU,EAAE,eAAe,aAAa,CAAC;AAAA,MACtD,CAAC;AAAA,IACH,QAAQ;AACN,aAAO,EAAE,QAAQ,eAAe,QAAQ,WAAW;AAAA,IACrD;AACA,QAAI,IAAI,WAAW,KAAK;AACtB,YAAM,OAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAE/C,UAAI,KAAK,UAAU,gBAAiB,MAAK,UAAU;AACnD,aAAO,KAAK,UAAU,YAClB,EAAE,QAAQ,eAAe,IACzB,EAAE,QAAQ,eAAe,QAAQ,eAAe;AAAA,IACtD;AACA,QAAI,IAAI,WAAW,IAAK,QAAO,EAAE,QAAQ,WAAW;AACpD,QAAI,CAAC,IAAI;AACP,aAAO,EAAE,QAAQ,eAAe,QAAQ,YAAY,IAAI,MAAM,GAAG;AACnE,UAAM,SAAU,MAAM,IAAI,KAAK;AAK/B,WAAO,KAAK,WAAW,MAAM;AAAA,EAC/B;AAAA,EAEA,MAAc,WAAW,QAII;AAC3B,UAAM,OAAO,IAAI,gBAAgB;AAAA,MAC/B,YAAY;AAAA,MACZ,WAAW,OAAO;AAAA,MAClB,UAAU,KAAK,OAAO;AAAA,IACxB,CAAC;AACD,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,KAAK;AAAA,QACf,IAAI,IAAI,iBAAiB,KAAK,OAAO,SAAS;AAAA,QAC9C;AAAA,UACE,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,eAAe;AAAA,cACb,KAAK,OAAO,MAAM;AAAA,cAClB,KAAK,OAAO,MAAM;AAAA,YACpB;AAAA,UACF;AAAA,UACA,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF,QAAQ;AACN,aAAO,EAAE,QAAQ,eAAe,QAAQ,QAAQ;AAAA,IAClD;AACA,QAAI,CAAC,IAAI;AACP,aAAO,EAAE,QAAQ,eAAe,QAAQ,SAAS,IAAI,MAAM,GAAG;AAChE,UAAM,OAAQ,MAAM,IAAI,KAAK;AAI7B,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,OAAO;AAAA,QACL,aAAa,KAAK;AAAA,QAClB,WAAW,KAAK,IAAI,IAAI,KAAK,aAAa;AAAA,QAC1C,YAAY,OAAO;AAAA,QACnB,OAAO,OAAO;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,eAAuC;AACnD,QACE,KAAK,WACL,KAAK,QAAQ,YAAY,KAAK,IAAI,IAAI,mBACtC;AACA,aAAO,KAAK,QAAQ;AAAA,IACtB;AACA,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,KAAK;AAAA,QACf,IAAI,IAAI,iBAAiB,KAAK,OAAO,SAAS;AAAA,QAC9C;AAAA,UACE,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,eAAe;AAAA,cACb,KAAK,OAAO,YAAY;AAAA,cACxB,KAAK,OAAO,YAAY;AAAA,YAC1B;AAAA,UACF;AAAA,UACA,MAAM,IAAI,gBAAgB;AAAA,YACxB,YAAY;AAAA,YACZ,OAAO;AAAA,YACP,UAAU;AAAA,UACZ,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,OAAQ,MAAM,IAAI,KAAK;AAI7B,SAAK,UAAU;AAAA,MACb,OAAO,KAAK;AAAA,MACZ,WAAW,KAAK,IAAI,IAAI,KAAK,aAAa;AAAA,IAC5C;AACA,WAAO,KAAK,QAAQ;AAAA,EACtB;AACF;AAEO,SAAS,YACd,KACwE;AACxE,QAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,MAAI;AACF,UAAM,MAAM,MAAM,CAAC,EAAE,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AACzD,UAAM,UAAU,KAAK,MAAM,mBAAmB,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC;AAKhE,QAAI,CAAC,QAAQ,OAAO,CAAC,QAAQ,IAAK,QAAO;AACzC,WAAO;AAAA,MACL,YAAY,QAAQ;AAAA,MACpB,OAAO,MAAM,QAAQ,QAAQ,KAAK,IAC9B,QAAQ,MAAM,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ,IAC9D,CAAC;AAAA,MACL,WAAW,QAAQ,MAAM;AAAA,IAC3B;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AClJO,IAAM,cAAN,cAA0B,MAAM;AAAA,EACrC;AAAA,EACA,YAAYA,UAAwB;AAClC,UAAMA,SAAQ,MAAM;AACpB,SAAK,UAAUA;AAAA,EACjB;AACF;AAIA,IAAM,sBAAsB;AAC5B,IAAM,eAAe,oBAAI,IAAI,CAAC,SAAS,SAAS,OAAO,CAAC;AACxD,IAAM,YAAY,oBAAI,IAAI,CAAC,SAAS,SAAS,SAAS,SAAS,OAAO,CAAC;AACvE,IAAM,qBAAqB;AAC3B,IAAM,kBAAkB,oBAAI,IAAI,CAAC,SAAS,SAAS,SAAS,SAAS,OAAO,CAAC;AAC7E,IAAM,eAAe,oBAAI,IAAI,CAAC,SAAS,SAAS,SAAS,OAAO,CAAC;AAE1D,SAAS,UAAU,QAAgB,MAA8B;AACtE,QAAM,IAAK,QAAQ,CAAC;AAIpB,MAAI,WAAW,OAAO,aAAa,IAAI,EAAE,OAAO,QAAQ,EAAE,GAAG;AAC3D,WAAO,EAAE,QAAQ,eAAe;AAAA,EAClC;AACA,MAAI,EAAE,OAAO,OAAO,yBAAyB;AAC3C,WAAO,EAAE,QAAQ,yBAAyB;AAAA,EAC5C;AACA,MAAI,EAAE,OAAO,OAAO;AAClB,WAAO,EAAE,QAAQ,eAAe;AAClC,MAAI,UAAU,OAAO,WAAW,EAAG,QAAO,EAAE,QAAQ,cAAc;AAClE,QAAM,WAAW;AAAA,IACf,GAAI,EAAE,IAAI,YAAY,CAAC;AAAA,IACvB,IAAI,EAAE,IAAI,SAAS,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAAA,EACxD,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO;AAClC,aAAW,KAAK,UAAU;AACxB,QAAI,EAAE,OAAO,oBAAqB,QAAO,EAAE,QAAQ,sBAAsB;AACzE,QAAI,aAAa,IAAI,EAAE,MAAM,EAAE,EAAG,QAAO,EAAE,QAAQ,eAAe;AAClE,QAAI,EAAE,OAAO,mBAAoB,QAAO,EAAE,QAAQ,qBAAqB;AACvE,QAAI,gBAAgB,IAAI,EAAE,MAAM,EAAE,GAAG;AACnC,aAAO,EAAE,QAAQ,oBAAoB,SAAS,EAAE,QAAQ,GAAG;AAAA,IAC7D;AACA,QAAI,aAAa,IAAI,EAAE,MAAM,EAAE,EAAG,QAAO,EAAE,QAAQ,eAAe;AAAA,EACpE;AACA,MAAI,EAAE,OAAO,OAAO,sBAAsB,WAAW,KAAK;AACxD,WAAO,EAAE,QAAQ,mBAAmB;AAAA,EACtC;AACA,QAAM,QAAQ,SAAS,CAAC,GAAG,QAAQ,EAAE,OAAO,WAAW;AACvD,SAAO,EAAE,QAAQ,iBAAiB,SAAS,MAAM;AACnD;AAEO,SAAS,YAAY,MAA2B;AACrD,UAAQ,KAAK,IAAI,YAAY,CAAC,GAAG,KAAK,CAAC,MAAM,UAAU,IAAI,EAAE,MAAM,EAAE,CAAC;AACxE;AAgBO,IAAM,cAAN,MAAkB;AAAA,EACN;AAAA,EACA;AAAA,EAEjB,YAAY,SAAiB,YAAmB,OAAO;AACrD,SAAK,UAAU;AACf,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,MACJ,MACA,cACA,SAAiC,CAAC,GACb;AACrB,UAAM,MAAM,IAAI,IAAI,iBAAiB,IAAI,QAAQ,KAAK,OAAO;AAC7D,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,EAAG,KAAI,aAAa,IAAI,GAAG,CAAC;AACtE,UAAM,MAAM,MAAM,KAAK,KAAK,KAAK,EAAE,QAAQ,MAAM,GAAG,YAAY;AAChE,WAAQ,MAAM,KAAK,KAAK,GAAG;AAAA,EAC7B;AAAA,EAEA,MAAM,OACJ,MACA,QACA,MACA,cACY;AACZ,UAAM,MAAM,IAAI,IAAI,iBAAiB,IAAI,IAAI,KAAK,OAAO;AACzD,QAAI,aAAa,IAAI,QAAQ,MAAM;AACnC,UAAM,MAAM,MAAM,KAAK;AAAA,MACrB;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,MAC3B;AAAA,MACA;AAAA,IACF;AACA,WAAQ,MAAM,KAAK,KAAK,GAAG;AAAA,EAC7B;AAAA,EAEA,MAAM,OAAO,cAAqD;AAChE,UAAM,MAAM,IAAI,IAAI,oBAAoB,KAAK,OAAO;AACpD,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,KAAK,UAAU,KAAK;AAAA,QAC9B,SAAS,EAAE,mBAAmB,aAAa;AAAA,MAC7C,CAAC;AAAA,IACH,QAAQ;AACN,YAAM,IAAI,YAAY,EAAE,QAAQ,cAAc,CAAC;AAAA,IACjD;AACA,QAAI,IAAI,WAAW,OAAO,IAAI,WAAW,IAAK,QAAO;AACrD,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,YAAY,EAAE,QAAQ,cAAc,CAAC;AAC5D,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB;AAAA,EAEA,MAAM,OAAO,cAAqC;AAChD,UAAM,MAAM,IAAI,IAAI,4BAA4B,KAAK,OAAO;AAC5D,QAAI;AACF,YAAM,KAAK,UAAU,KAAK;AAAA,QACxB,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,eAAe,aAAa,CAAC;AAAA,MACtD,CAAC;AAAA,IACH,QAAQ;AAAA,IAGR;AAAA,EACF;AAAA,EAEA,MAAc,KACZ,KACA,MACA,cACmB;AACnB,UAAM,UAAU,IAAI,QAAQ,KAAK,OAAO;AACxC,YAAQ,IAAI,UAAU,kBAAkB;AACxC,QAAI,aAAc,SAAQ,IAAI,mBAAmB,YAAY;AAC7D,QAAI;AACF,aAAO,MAAM,KAAK,UAAU,KAAK,EAAE,GAAG,MAAM,QAAQ,CAAC;AAAA,IACvD,QAAQ;AACN,YAAM,IAAI,YAAY,EAAE,QAAQ,cAAc,CAAC;AAAA,IACjD;AAAA,EACF;AAAA,EAEA,MAAc,KAAK,KAAiC;AAClD,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AAG9C,QACE,IAAI,MACH,IAAI,WAAW,OAAO,QAAQ,mBAAoB,MACnD;AACA,aAAO;AAAA,IACT;AAGA,UAAM,IAAI,YAAY,UAAU,IAAI,QAAQ,IAAI,CAAC;AAAA,EACnD;AACF;;;ACzKA,IAAM,kBAAkB,KAAK,KAAK,KAAK;AACvC,IAAM,eAAe;AACrB,IAAM,gBAAgB,KAAK;AAI3B,IAAM,eAAuE;AAAA,EAC3E,sBAAsB;AAAA,EACtB,mBAAmB;AAAA,EACnB,WAAW;AACb;AAEA,IAAM,iBAA0D;AAAA,EAC9D,qBAAqB;AAAA,EACrB,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EACxB,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,aAAa;AACf;AAEA,SAAS,KACP,QACA,MACA,UAAyB,CAAC,GAChB;AACV,QAAM,UAAU,IAAI,QAAQ,EAAE,gBAAgB,mBAAmB,CAAC;AAClE,aAAW,KAAK,QAAS,SAAQ,OAAO,cAAc,CAAC;AACvD,SAAO,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG,EAAE,QAAQ,QAAQ,CAAC;AAC/D;AAEA,SAAS,QAAQ,MAAc,QAAgB,SAA4B;AACzE,SAAO,KAAK,QAAQ;AAAA,IAClB,OAAO,EAAE,MAAM,QAAQ,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG;AAAA,EACzD,CAAC;AACH;AAEA,SAAS,OAAO,OAA0B;AACxC,MAAI,iBAAiB,aAAa;AAChC,UAAM,IAAI,MAAM;AAChB,WAAO;AAAA,MACL,EAAE;AAAA,MACF,eAAe,EAAE,MAAM;AAAA,MACvB,aAAa,IAAI,EAAE,UAAU;AAAA,IAC/B;AAAA,EACF;AACA,SAAO,QAAQ,eAAe,GAAG;AACnC;AAEA,SAAS,OACP,SACA,OACA,SACsB;AACtB,QAAM,WAAW,QAAQ;AACzB,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,QAAQ,SAAS,QAAQ,SAAS;AACxC,QAAM,UAAU,SAAS,sBAAsB,KAAK,CAAC,MAAM,EAAE,UAAU,KAAK;AAC5E,SAAO;AAAA,IACL,IAAI,SAAS;AAAA,IACb,YAAY,SAAS;AAAA,IACrB;AAAA,IACA,eAAe,SAAS,YAAY;AAAA,IACpC,MAAM,SAAS,QAAQ,QAAQ;AAAA,IAC/B,MAAM,MAAM,SAAS,GAAG,OAAO,QAAQ,IAAI,UAAU;AAAA,EACvD;AACF;AAEA,SAAS,aACP,MACA,QACkD;AAClD,SAAO,MAAM;AAAA,IACX,CAAC,MAAiD,EAAE,WAAW;AAAA,EACjE;AACF;AAEO,SAAS,oBACd,QACe;AACf,QAAM,UAAU,OAAO;AACvB,QAAM,SAAS,IAAI,YAAY,OAAO,WAAW,OAAO,KAAK;AAC7D,QAAM,WAAW,IAAI,SAAS,EAAE,GAAG,OAAO,WAAW,QAAQ,GAAG,OAAO,KAAK;AAC5E,QAAM,QAAQ;AAAA,IACZ,SAAS,OAAO,QAAQ,QAAQ,GAAG,OAAO;AAAA,IAC1C,OAAO,GAAG,OAAO;AAAA,IACjB,MAAM,GAAG,OAAO;AAAA,EAClB;AACA,QAAM,SACJ,OAAO,QAAQ,UAAU,OAAO,UAAU,UAAU,WAAW,UAAU;AAE3E,QAAM,SAAS,CAAC,MAAc,OAAe,WAC3C,gBAAgB,MAAM,OAAO,EAAE,QAAQ,OAAO,CAAC;AAEjD,QAAM,WAAW,CAAC,WAA+C;AAC/D,UAAM,QAAQ,OAAO;AACrB,QAAI,CAAC,MAAO,QAAO,CAAC;AACpB,WAAO;AAAA,MACL,OAAO,MAAM,SAAS,OAAO,eAAe;AAAA,MAC5C,YAAY,MAAM,MAAM,MAAM;AAAA,IAChC;AAAA,EACF;AAEA,QAAM,YAAY,CAAC,YAAyC;AAC1D,UAAM,MAAM,WAAW,SAAS,MAAM,KAAK;AAC3C,UAAM,UAAU,MAAM,YAAY,GAAG,IAAI;AACzC,WAAO,OAAO,UAAU,EAAE,aAAa,KAAK,GAAG,QAAQ,IAAI;AAAA,EAC7D;AAEA,iBAAe,YAAY,SAA+C;AACxE,UAAM,eAAe,WAAW,SAAS,MAAM,OAAO;AACtD,QAAI,CAAC,aAAc,QAAO;AAC1B,UAAM,OAAO,UAAU,OAAO;AAC9B,QAAI,QAAQ,CAAC,SAAS,aAAa,IAAI;AACrC,aAAO,EAAE,OAAO,KAAK,YAAY;AACnC,UAAM,UAAU,MAAM,SAAS,SAAS,YAAY;AACpD,QAAI,QAAQ,WAAW,kBAAkB,QAAQ,WAAW;AAC1D,aAAO;AACT,QAAI,QAAQ,WAAW;AACrB,YAAM,IAAI,YAAY,EAAE,QAAQ,cAAc,CAAC;AACjD,WAAO;AAAA,MACL,OAAO,QAAQ,MAAM;AAAA,MACrB,WAAW,OAAO,MAAM,OAAO,QAAQ,MAAM,aAAa,aAAa;AAAA,IACzE;AAAA,EACF;AAEA,iBAAe,WACb,SACkC;AAClC,UAAM,eAAe,WAAW,SAAS,MAAM,OAAO;AACtD,QAAI,CAAC,aAAc,QAAO;AAC1B,UAAMC,WAAU,MAAM,OAAO,OAAO,YAAY;AAChD,QAAI,CAACA,UAAS,OAAQ,QAAO;AAC7B,UAAM,OAAO,UAAU,OAAO;AAC9B,UAAM,OAAO,OAAOA,UAAS,MAAM,SAAS,CAAC,GAAG,OAAO;AACvD,QAAI,CAAC,KAAM,QAAO;AAClB,WAAO,EAAE,MAAM,SAAS,EAAE,WAAWA,SAAQ,cAAc,GAAG,EAAE;AAAA,EAClE;AAEA,iBAAe,KAAK,SAAoD;AACtE,UAAM,MAAO,MAAM,QAAQ,KAAK,EAAE,MAAM,MAAM,IAAI;AAIlD,WAAO,OAAO,CAAC;AAAA,EACjB;AAEA,QAAM,MAAM,CAAC,GAA4B,QACvC,OAAO,EAAE,GAAG,MAAM,WAAY,EAAE,GAAG,EAAa,KAAK,IAAI;AAE3D,iBAAe,YAAY,SAAqC;AAC9D,UAAM,IAAI,MAAM,KAAK,OAAO;AAC5B,UAAM,QAAQ,IAAI,GAAG,OAAO;AAC5B,UAAM,WAAW,OAAO,EAAE,aAAa,WAAW,EAAE,WAAW;AAC/D,QAAI,CAAC,SAAS,CAAC;AACb,aAAO,QAAQ,iBAAiB,KAAK,iCAAiC;AACxE,UAAM,OAAO,MAAM,OAAO,MAAM,OAAO;AACvC,UAAM,SAAS,MAAM,OAAO,OAA4B,SAAS,KAAK,IAAI;AAAA,MACxE,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ;AAAA,IACF,CAAC;AACD,WAAO;AAAA,MACL;AAAA,MACA,EAAE,MAAM,OAAO,UAAU,OAAO,OAAO,SAAS,CAAC,GAAG,OAAO,IAAI,KAAK;AAAA,MACpE,SAAS,MAAM;AAAA,IACjB;AAAA,EACF;AAEA,iBAAe,YAAY,SAAqC;AAC9D,UAAM,IAAI,MAAM,KAAK,OAAO;AAC5B,UAAM,QAAQ,IAAI,GAAG,OAAO;AAC5B,UAAM,WAAW,OAAO,EAAE,aAAa,WAAW,EAAE,WAAW;AAC/D,QAAI,CAAC,SAAS,CAAC;AACb,aAAO,QAAQ,iBAAiB,KAAK,iCAAiC;AACxE,UAAM,OAAO,MAAM,OAAO,MAAM,cAAc;AAC9C,UAAM,SAAS,MAAM,OAAO;AAAA,MAC1B;AAAA,MACA,KAAK;AAAA,MACL;AAAA,QACE,QAAQ;AAAA,QACR,QAAQ,EAAE,OAAO,GAAI,IAAI,GAAG,MAAM,IAAI,EAAE,MAAM,IAAI,GAAG,MAAM,EAAE,IAAI,CAAC,EAAG;AAAA,QACrE;AAAA,MACF;AAAA,IACF;AACA,UAAM,eAAe;AAAA,MACnB,OAAO;AAAA,MACP;AAAA,IACF;AACA,UAAM,UAAU,SAAS,MAAM;AAC/B,QAAI;AACF,cAAQ;AAAA,QACN;AAAA,UACE,MAAM;AAAA,UACN,gBAAgB,aAAa,KAAK,EAAE;AAAA,UACpC;AAAA,QACF;AAAA,MACF;AACF,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,MAAM,OAAO,UAAU,OAAO,OAAO,SAAS,CAAC,GAAG,OAAO,IAAI;AAAA,QAC7D,GAAI,eACA,EAAE,cAAc,EAAE,QAAQ,aAAa,KAAK,GAAG,EAAE,IACjD,CAAC;AAAA,MACP;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,iBAAe,QAAQ,SAAqC;AAC1D,UAAM,IAAI,MAAM,KAAK,OAAO;AAC5B,UAAM,QAAQ,IAAI,GAAG,OAAO;AAC5B,UAAM,OAAO,IAAI,GAAG,MAAM;AAC1B,UAAM,OAAO,aAAa,IAAI;AAC9B,QAAI,CAAC,SAAS,CAAC;AACb,aAAO,QAAQ,iBAAiB,KAAK,6BAA6B;AACpE,UAAM,OAAO,MAAM,OAAO,MAAM,IAAI;AACpC,UAAM,YAAY,MAAM,OAAO,OAAO,MAAM,KAAK,IAAI;AAAA,MACnD,QAAQ;AAAA,MACR,GAAI,SAAS,UAAU,EAAE,YAAY,MAAM,IAAI,EAAE,MAAM;AAAA,IACzD,CAAC;AACD,QAAI,CAAC,YAAY,SAAS,KAAK,UAAU,UAAU,cAAc;AAC/D,aAAO,QAAQ,iBAAiB,KAAK,4BAA4B;AAAA,IACnE;AACA,WAAO,KAAK,KAAK,EAAE,MAAM,KAAK,GAAG;AAAA,MAC/B,OAAO,MAAM,MAAM,GAAG,IAAI,IAAI,KAAK,EAAE,IAAI,YAAY;AAAA,IACvD,CAAC;AAAA,EACH;AAEA,WAAS,YAAY,SAAkB,MAA6B;AAClE,UAAM,MAAM,WAAW,SAAS,MAAM,IAAI,KAAK;AAC/C,UAAM,CAAC,GAAG,EAAE,IAAI,IAAI,MAAM,GAAG;AAC7B,WAAO,MAAM,QAAQ,KAAK,KAAK;AAAA,EACjC;AAEA,iBAAe,UAAU,SAAqC;AAC5D,UAAM,IAAI,MAAM,KAAK,OAAO;AAC5B,UAAM,QAAQ,IAAI,GAAG,OAAO;AAC5B,UAAM,OAAO,IAAI,GAAG,KAAK;AACzB,UAAM,SAAS,YAAY,QAAQ,SAAS,OAAO;AACnD,QAAI,CAAC,OAAQ,QAAO,QAAQ,gBAAgB,GAAG;AAC/C,QAAI,CAAC,SAAS,CAAC;AACb,aAAO,QAAQ,iBAAiB,KAAK,4BAA4B;AACnE,UAAM,SAAS,MAAM,OAAO,OAA4B,SAAS,QAAQ;AAAA,MACvE,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ;AAAA,IACF,CAAC;AACD,WAAO;AAAA,MACL;AAAA,MACA,EAAE,MAAM,OAAO,UAAU,OAAO,OAAO,SAAS,CAAC,GAAG,OAAO,IAAI,KAAK;AAAA,MACpE,SAAS,MAAM;AAAA,IACjB;AAAA,EACF;AAEA,iBAAe,YAAY,SAAqC;AAC9D,UAAM,IAAI,MAAM,KAAK,OAAO;AAC5B,UAAM,OAAO,IAAI,GAAG,KAAK;AACzB,UAAM,SAAS,YAAY,QAAQ,SAAS,cAAc;AAC1D,QAAI,CAAC,OAAQ,QAAO,QAAQ,gBAAgB,GAAG;AAC/C,QAAI,CAAC,KAAM,QAAO,QAAQ,iBAAiB,KAAK,iBAAiB;AACjE,UAAM,OAAO,MAAM,OAAO,OAAO,gBAAgB,QAAQ;AAAA,MACvD,QAAQ;AAAA,MACR;AAAA,IACF,CAAC;AACD,QAAI,KAAK,UAAU,mBAAoB,QAAO,QAAQ,gBAAgB,GAAG;AACzE,WAAO,KAAK,KAAK,EAAE,UAAU,KAAK,GAAG,CAAC,YAAY,MAAM,MAAM,MAAM,CAAC,CAAC;AAAA,EACxE;AAEA,iBAAe,cAAc,SAAqC;AAChE,UAAM,IAAI,MAAM,KAAK,OAAO;AAC5B,UAAM,OAAO,IAAI,GAAG,KAAK;AACzB,UAAM,WAAW,OAAO,EAAE,aAAa,WAAW,EAAE,WAAW;AAC/D,UAAM,SAAS,YAAY,QAAQ,SAAS,UAAU;AACtD,QAAI,CAAC,OAAQ,QAAO,QAAQ,gBAAgB,GAAG;AAC/C,QAAI,CAAC,QAAQ,CAAC;AACZ,aAAO,QAAQ,iBAAiB,KAAK,+BAA+B;AACtE,UAAM,YAAY,MAAM,OAAO;AAAA,MAC7B;AAAA,MACA;AAAA,MACA,EAAE,QAAQ,QAAQ,KAAK;AAAA,IACzB;AACA,UAAM,QAAQ;AAAA,MACZ,UAAU;AAAA,MACV;AAAA,IACF,GAAG;AACH,UAAM,WAAW,aAAa,UAAU,eAAe,kBAAkB;AACzE,QAAI,CAAC,SAAS,CAAC,SAAU,QAAO,QAAQ,gBAAgB,GAAG;AAC3D,UAAM,OAAO;AAAA,MACX;AAAA,MACA,SAAS,KAAK;AAAA,MACd,EAAE,QAAQ,YAAY,SAAS;AAAA,MAC/B;AAAA,IACF;AACA,WAAO,KAAK,KAAK,EAAE,OAAO,KAAK,GAAG;AAAA,MAChC,OAAO,MAAM,SAAS,OAAO,eAAe;AAAA,MAC5C,YAAY,MAAM,MAAM,MAAM;AAAA,IAChC,CAAC;AAAA,EACH;AAEA,iBAAe,QAAQ,SAAqC;AAC1D,UAAM,QAAQ,WAAW,QAAQ,SAAS,MAAM,OAAO;AACvD,QAAI,MAAO,OAAM,OAAO,OAAO,KAAK;AACpC,WAAO,KAAK,KAAK,EAAE,WAAW,KAAK,GAAG;AAAA,MACpC,YAAY,MAAM,SAAS,MAAM;AAAA,MACjC,YAAY,MAAM,OAAO,MAAM;AAAA,MAC/B,YAAY,MAAM,MAAM,MAAM;AAAA,IAChC,CAAC;AAAA,EACH;AAEA,iBAAe,QAAQ,SAAqC;AAC1D,UAAM,IAAI,MAAM,WAAW,QAAQ,OAAO;AAC1C,WAAO,KAAK,KAAK,CAAC;AAAA,EACpB;AAEA,QAAM,SAAkE;AAAA,IACtE,sBAAsB;AAAA,IACtB,0BAA0B;AAAA,IAC1B,sBAAsB;AAAA,IACtB,wCAAwC;AAAA,IACxC,+BAA+B;AAAA,IAC/B,iCAAiC;AAAA,IACjC,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,EACrB;AAEA,iBAAe,QAAQ,SAAqC;AAC1D,UAAM,OAAO,IAAI,IAAI,QAAQ,GAAG,EAAE,SAAS,QAAQ,OAAO,EAAE;AAC5D,UAAM,SAAS,KAAK,MAAM,KAAK,QAAQ,YAAY,IAAI,aAAa,MAAM;AAC1E,QAAI,WAAW,iBAAkB,QAAO,QAAQ,iBAAiB,GAAG;AACpE,UAAM,QAAQ,OAAO,GAAG,QAAQ,MAAM,IAAI,MAAM,EAAE;AAClD,QAAI,CAAC,MAAO,QAAO,QAAQ,aAAa,GAAG;AAC3C,QAAI;AACF,aAAO,MAAM,MAAM,OAAO;AAAA,IAC5B,SAAS,OAAO;AACd,aAAO,OAAO,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,WAAS,UAAU,SAAoC;AACrD,UAAM,UAAU,QAAQ,QAAQ,QAAQ,OAAO,EAAE;AACjD,WAAO,OAAO,YAAwC;AACpD,UAAI;AACJ,UAAI;AACJ,UAAI;AACF,gBAAQ,MAAM,YAAY,QAAQ,OAAO;AACzC,YAAI,QACA,MAAM;AAAA,UACJ,IAAI,QAAQ,EAAE,GAAG,aAAa,QAAQ,SAAS,OAAO,KAAK,EAAE,CAAC;AAAA,QAChE,IACA;AAAA,MACN,QAAQ;AACN,eAAO,QAAQ,iCAAiC,GAAG;AAAA,MACrD;AACA,UAAI,CAAC,SAAS,CAAC,EAAG,QAAO,QAAQ,oBAAoB,GAAG;AACxD,UAAI,QAAQ,aAAa,EAAE,KAAK,SAAS;AACvC,eAAO,QAAQ,aAAa,GAAG;AACjC,YAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC/B,YAAM,UAAU,eAAe,QAAQ,OAAO;AAC9C,cAAQ,IAAI,iBAAiB,UAAU,MAAM,KAAK,EAAE;AACpD,YAAM,WAAW,MAAM,MAAM,GAAG,OAAO,GAAG,IAAI,QAAQ,GAAG,IAAI,MAAM,IAAI;AAAA,QACrE,QAAQ,QAAQ;AAAA,QAChB;AAAA,QACA,MACE,QAAQ,WAAW,SAAS,QAAQ,WAAW,SAC3C,SACA,QAAQ;AAAA,QACd,QAAQ;AAAA,QACR,UAAU;AAAA,MACZ,CAAgB;AAChB,YAAM,MAAM,eAAe,SAAS,OAAO;AAC3C,UAAI,MAAM,UAAW,KAAI,OAAO,cAAc,MAAM,SAAS;AAC7D,aAAO,IAAI,SAAS,SAAS,MAAM;AAAA,QACjC,QAAQ,SAAS;AAAA,QACjB,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,YAAY,aAAa,UAAU;AACvD;AAIA,SAAS,aACP,SACA,OACA,OACA;AACA,QAAM,UAAU,WAAW,SAAS,MAAM,OAAO,KAAK;AACtD,SAAO;AAAA,IACL,QAAQ,GAAG,MAAM,OAAO,IAAI,mBAAmB,OAAO,CAAC,KAAK,MAAM,KAAK,IAAI,mBAAmB,MAAM,KAAK,CAAC;AAAA,EAC5G;AACF;","names":["failure","session"]}
1
+ {"version":3,"sources":["../src/urbangate/cookies.ts","../src/urbangate/exchange.ts","../src/urbangate/kratos.ts","../src/urbangate/server.ts"],"sourcesContent":["export interface CookieOptions {\n maxAge: number;\n secure: boolean;\n}\n\nexport function readCookie(headers: Headers, name: string): string | undefined {\n const raw = headers.get(\"cookie\") ?? \"\";\n for (const part of raw.split(\";\")) {\n const [k, ...v] = part.trim().split(\"=\");\n if (k === name) return decodeURIComponent(v.join(\"=\"));\n }\n return undefined;\n}\n\nexport function serializeCookie(\n name: string,\n value: string,\n o: CookieOptions,\n): string {\n const attrs = [\n `${name}=${encodeURIComponent(value)}`,\n \"Path=/\",\n \"HttpOnly\",\n \"SameSite=Lax\",\n `Max-Age=${value ? o.maxAge : 0}`,\n ];\n if (o.secure) attrs.push(\"Secure\");\n return attrs.join(\"; \");\n}\n\nexport function clearCookie(name: string, secure: boolean): string {\n return serializeCookie(name, \"\", { maxAge: 0, secure });\n}\n","import type { Fetch } from \"./kratos\";\n\nexport interface ExchangeConfig {\n product: string;\n issuerUrl: string;\n provisioner: { clientId: string; clientSecret: string };\n admin: { clientId: string; clientSecret: string };\n}\n\nexport interface PersonToken {\n accessToken: string;\n expiresAt: number;\n identityId: string;\n roles: Array<string>;\n}\n\nexport type ExchangeOutcome =\n | { status: \"ok\"; token: PersonToken }\n | { status: \"session_gone\" }\n | { status: \"inactive\" }\n | { status: \"unavailable\"; reason: string };\n\nconst JWT_BEARER = \"urn:ietf:params:oauth:grant-type:jwt-bearer\";\nconst EXCHANGE_SCOPE = \"urbangate:sessions:exchange\";\nconst REFRESH_MARGIN_MS = 60_000;\n\nfunction basic(id: string, secret: string) {\n return \"Basic \" + btoa(`${id}:${secret}`);\n}\n\n/**\n * Turns a Kratos session token into the person's Hydra access token, in the\n * three calls ADR 0009 of urbangate describes: a machine token for the\n * provisioner, the assertion from urbangate, the token from Hydra with the\n * product's own admin client. Hydra issues no refresh token on this grant,\n * so the caller exchanges again when the token nears its end.\n */\nexport class Exchange {\n private machine: { token: string; expiresAt: number } | null = null;\n\n private readonly config: ExchangeConfig;\n private readonly fetchImpl: Fetch;\n\n constructor(config: ExchangeConfig, fetchImpl: Fetch = fetch) {\n this.config = config;\n this.fetchImpl = fetchImpl;\n }\n\n needsRefresh(token: PersonToken | null, now = Date.now()): boolean {\n return !token || token.expiresAt - now < REFRESH_MARGIN_MS;\n }\n\n async exchange(sessionToken: string): Promise<ExchangeOutcome> {\n const machine = await this.machineToken();\n if (!machine) return { status: \"unavailable\", reason: \"machine_token\" };\n const url = new URL(\n \"/api/machine/sessions/exchange\",\n this.config.issuerUrl,\n );\n let res: Response;\n try {\n res = await this.fetchImpl(url, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${machine}`,\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({ session_token: sessionToken }),\n });\n } catch {\n return { status: \"unavailable\", reason: \"exchange\" };\n }\n if (res.status === 401) {\n const body = (await res.json().catch(() => ({}))) as { error?: string };\n // Our own credential was refused: fetch a fresh one next time.\n if (body.error === \"invalid_token\") this.machine = null;\n return body.error === \"session\"\n ? { status: \"session_gone\" }\n : { status: \"unavailable\", reason: \"exchange_401\" };\n }\n if (res.status === 403) return { status: \"inactive\" };\n if (!res.ok)\n return { status: \"unavailable\", reason: `exchange_${res.status}` };\n const answer = (await res.json()) as {\n assertion: string;\n identity_id: string;\n roles: Array<string>;\n };\n return this.hydraToken(answer);\n }\n\n private async hydraToken(answer: {\n assertion: string;\n identity_id: string;\n roles: Array<string>;\n }): Promise<ExchangeOutcome> {\n // An -admin client authenticates with client_secret_post, unlike the\n // provisioner: Hydra refuses its credentials in a Basic header.\n const form = new URLSearchParams({\n grant_type: JWT_BEARER,\n assertion: answer.assertion,\n audience: this.config.product,\n client_id: this.config.admin.clientId,\n client_secret: this.config.admin.clientSecret,\n });\n let res: Response;\n try {\n res = await this.fetchImpl(\n new URL(\"/oauth2/token\", this.config.issuerUrl),\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/x-www-form-urlencoded\",\n },\n body: form,\n },\n );\n } catch {\n return { status: \"unavailable\", reason: \"hydra\" };\n }\n if (!res.ok)\n return { status: \"unavailable\", reason: `hydra_${res.status}` };\n const body = (await res.json()) as {\n access_token: string;\n expires_in: number;\n };\n return {\n status: \"ok\",\n token: {\n accessToken: body.access_token,\n expiresAt: Date.now() + body.expires_in * 1000,\n identityId: answer.identity_id,\n roles: answer.roles,\n },\n };\n }\n\n private async machineToken(): Promise<string | null> {\n if (\n this.machine &&\n this.machine.expiresAt - Date.now() > REFRESH_MARGIN_MS\n ) {\n return this.machine.token;\n }\n let res: Response;\n try {\n res = await this.fetchImpl(\n new URL(\"/oauth2/token\", this.config.issuerUrl),\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/x-www-form-urlencoded\",\n authorization: basic(\n this.config.provisioner.clientId,\n this.config.provisioner.clientSecret,\n ),\n },\n body: new URLSearchParams({\n grant_type: \"client_credentials\",\n scope: EXCHANGE_SCOPE,\n audience: \"urbangate\",\n }),\n },\n );\n } catch {\n return null;\n }\n if (!res.ok) return null;\n const body = (await res.json()) as {\n access_token: string;\n expires_in: number;\n };\n this.machine = {\n token: body.access_token,\n expiresAt: Date.now() + body.expires_in * 1000,\n };\n return this.machine.token;\n }\n}\n\nexport function decodeToken(\n raw: string,\n): { identityId: string; roles: Array<string>; expiresAt: number } | null {\n const parts = raw.split(\".\");\n if (parts.length !== 3) return null;\n try {\n const b64 = parts[1].replace(/-/g, \"+\").replace(/_/g, \"/\");\n const payload = JSON.parse(decodeURIComponent(escape(atob(b64)))) as {\n sub?: string;\n roles?: unknown;\n exp?: number;\n };\n if (!payload.sub || !payload.exp) return null;\n return {\n identityId: payload.sub,\n roles: Array.isArray(payload.roles)\n ? payload.roles.filter((r): r is string => typeof r === \"string\")\n : [],\n expiresAt: payload.exp * 1000,\n };\n } catch {\n return null;\n }\n}\n","export type Fetch = typeof fetch;\n\nexport interface KratosFlow {\n id: string;\n ui?: {\n messages?: Array<KratosMessage>;\n nodes?: Array<{\n attributes?: { name?: string; value?: unknown };\n messages?: Array<KratosMessage>;\n }>;\n };\n state?: string;\n}\n\nexport interface KratosMessage {\n id?: number;\n text?: string;\n type?: string;\n}\n\nexport interface KratosSession {\n id: string;\n active?: boolean;\n expires_at?: string;\n authenticated_at?: string;\n identity?: {\n id: string;\n state?: string;\n traits?: { email?: string; name?: string };\n verifiable_addresses?: Array<{ value: string; verified: boolean }>;\n };\n}\n\nexport interface KratosSessionResult {\n session_token?: string;\n session?: KratosSession;\n continue_with?: Array<ContinueWith>;\n}\n\nexport type ContinueWith =\n | { action: \"show_verification_ui\"; flow: { id: string } }\n | { action: \"show_settings_ui\"; flow: { id: string } }\n | { action: \"set_ory_session_token\"; ory_session_token: string }\n | { action: string };\n\nexport type KratosFailure =\n | { status: \"invalid_credentials\" }\n | { status: \"invalid_code\" }\n | { status: \"email_not_verified\" }\n | { status: \"second_factor_required\" }\n | { status: \"account_disabled\" }\n | { status: \"already_registered\" }\n | { status: \"password_refused\"; message: string }\n | { status: \"invalid_input\"; message: string }\n | { status: \"flow_expired\" }\n | { status: \"unavailable\" };\n\nexport class KratosError extends Error {\n failure: KratosFailure;\n constructor(failure: KratosFailure) {\n super(failure.status);\n this.failure = failure;\n }\n}\n\n// Kratos' message ids, from its text catalogue. A wrong password and an\n// unknown address share one id on purpose.\nconst INVALID_CREDENTIALS = 4000006;\nconst INVALID_CODE = new Set([4010008, 4060006, 4070006]);\nconst CODE_SENT = new Set([1010014, 1040005, 1060003, 1070003, 1080003]);\nconst ALREADY_REGISTERED = 4000007;\nconst PASSWORD_POLICY = new Set([4000005, 4000031, 4000032, 4000033, 4000034]);\nconst FLOW_EXPIRED = new Set([4010001, 4040001, 4060005, 4070005]);\n\nexport function failureOf(status: number, body: unknown): KratosFailure {\n const b = (body ?? {}) as {\n error?: { id?: string; code?: number; message?: string };\n ui?: KratosFlow[\"ui\"];\n };\n if (status === 410 || FLOW_EXPIRED.has(b.error?.code ?? -1)) {\n return { status: \"flow_expired\" };\n }\n if (b.error?.id === \"session_aal2_required\") {\n return { status: \"second_factor_required\" };\n }\n if (b.error?.id === \"self_service_flow_expired\")\n return { status: \"flow_expired\" };\n if (status >= 500 || status === 0) return { status: \"unavailable\" };\n const messages = [\n ...(b.ui?.messages ?? []),\n ...(b.ui?.nodes ?? []).flatMap((n) => n.messages ?? []),\n ].filter((m) => m.type === \"error\");\n for (const m of messages) {\n if (m.id === INVALID_CREDENTIALS) return { status: \"invalid_credentials\" };\n if (INVALID_CODE.has(m.id ?? -1)) return { status: \"invalid_code\" };\n if (m.id === ALREADY_REGISTERED) return { status: \"already_registered\" };\n if (PASSWORD_POLICY.has(m.id ?? -1)) {\n return { status: \"password_refused\", message: m.text ?? \"\" };\n }\n if (FLOW_EXPIRED.has(m.id ?? -1)) return { status: \"flow_expired\" };\n }\n if (b.error?.id === \"account_disabled\" || status === 401) {\n return { status: \"account_disabled\" };\n }\n const first = messages[0]?.text ?? b.error?.message ?? \"\";\n return { status: \"invalid_input\", message: first };\n}\n\nexport function codeWasSent(flow: KratosFlow): boolean {\n return (flow.ui?.messages ?? []).some((m) => CODE_SENT.has(m.id ?? -1));\n}\n\nexport function csrfOf(flow: KratosFlow): string | undefined {\n const node = flow.ui?.nodes?.find((n) => n.attributes?.name === \"csrf_token\");\n return typeof node?.attributes?.value === \"string\"\n ? node.attributes.value\n : undefined;\n}\n\nexport type FlowKind =\n \"login\" | \"registration\" | \"verification\" | \"recovery\" | \"settings\";\n\n/**\n * Kratos' native (API) flows, the ones an app that renders its own screens\n * drives from its server: no cookies, no CSRF, a session token in the answer.\n */\nexport class KratosFlows {\n private readonly baseUrl: string;\n private readonly fetchImpl: Fetch;\n\n constructor(baseUrl: string, fetchImpl: Fetch = fetch) {\n this.baseUrl = baseUrl;\n this.fetchImpl = fetchImpl;\n }\n\n async start(\n kind: FlowKind,\n sessionToken?: string,\n params: Record<string, string> = {},\n ): Promise<KratosFlow> {\n const url = new URL(`/self-service/${kind}/api`, this.baseUrl);\n for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);\n const res = await this.call(url, { method: \"GET\" }, sessionToken);\n return (await this.body(res)) as KratosFlow;\n }\n\n async submit<T = KratosFlow>(\n kind: FlowKind,\n flowId: string,\n body: Record<string, unknown>,\n sessionToken?: string,\n ): Promise<T> {\n const url = new URL(`/self-service/${kind}`, this.baseUrl);\n url.searchParams.set(\"flow\", flowId);\n const res = await this.call(\n url,\n {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n },\n sessionToken,\n );\n return (await this.body(res)) as T;\n }\n\n async whoami(sessionToken: string): Promise<KratosSession | null> {\n const url = new URL(\"/sessions/whoami\", this.baseUrl);\n let res: Response;\n try {\n res = await this.fetchImpl(url, {\n headers: { \"x-session-token\": sessionToken },\n });\n } catch {\n throw new KratosError({ status: \"unavailable\" });\n }\n if (res.status === 401 || res.status === 403) return null;\n if (!res.ok) throw new KratosError({ status: \"unavailable\" });\n return (await res.json()) as KratosSession;\n }\n\n async logout(sessionToken: string): Promise<void> {\n const url = new URL(\"/self-service/logout/api\", this.baseUrl);\n try {\n await this.fetchImpl(url, {\n method: \"DELETE\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ session_token: sessionToken }),\n });\n } catch {\n // The cookie is dropped regardless; a token Kratos still knows expires\n // on its own, and the person is signed out of this app either way.\n }\n }\n\n private async call(\n url: URL,\n init: RequestInit,\n sessionToken?: string,\n ): Promise<Response> {\n const headers = new Headers(init.headers);\n headers.set(\"accept\", \"application/json\");\n if (sessionToken) headers.set(\"x-session-token\", sessionToken);\n try {\n return await this.fetchImpl(url, { ...init, headers });\n } catch {\n throw new KratosError({ status: \"unavailable\" });\n }\n }\n\n private async body(res: Response): Promise<unknown> {\n const body = await res.json().catch(() => null);\n // A recovery code that verified answers 422 with what to do next, which\n // is a success shaped as a refusal.\n if (\n res.ok ||\n (res.status === 422 && body && \"continue_with\" in (body as object))\n ) {\n return body;\n }\n // A submitted flow that still has errors comes back as the flow itself\n // with a 400, and the messages say which field.\n throw new KratosError(failureOf(res.status, body));\n }\n}\n","import { forwardHeaders } from \"../core-proxy.ts\";\nimport { clearCookie, readCookie, serializeCookie } from \"./cookies.ts\";\nimport { Exchange, decodeToken } from \"./exchange.ts\";\nimport type { ExchangeConfig, PersonToken } from \"./exchange.ts\";\nimport { KratosError, KratosFlows, codeWasSent } from \"./kratos.ts\";\nimport type {\n ContinueWith,\n Fetch,\n KratosFailure,\n KratosSession,\n KratosSessionResult,\n} from \"./kratos.ts\";\n\nexport interface UrbangateAuthConfig {\n product: string;\n /** Kratos' public API as the server reaches it, e.g. http://kratos:4433 or https://id.urbangate.dev. */\n kratosUrl: string;\n urbangate: Omit<ExchangeConfig, \"product\">;\n cookie?: { name?: string; secure?: boolean };\n fetch?: Fetch;\n}\n\nexport interface UrbangateUser {\n id: string;\n identityId: string;\n email: string;\n emailVerified: boolean;\n name: string;\n role: \"admin\" | \"user\";\n}\n\nexport interface UrbangateSession {\n user: UrbangateUser;\n session: { expiresAt: string };\n}\n\nexport interface AccessToken {\n token: string;\n setCookie?: string;\n}\n\nexport interface UrbangateCoreProxyOptions {\n coreUrl: string;\n adminOnly?: boolean;\n}\n\nexport interface UrbangateAuth {\n handler(request: Request): Promise<Response>;\n getSession(headers: Headers): Promise<UrbangateSession | null>;\n accessToken(headers: Headers): Promise<AccessToken | null>;\n coreProxy(\n options: UrbangateCoreProxyOptions,\n ): (request: Request) => Promise<Response>;\n}\n\nconst SESSION_MAX_AGE = 30 * 24 * 60 * 60;\nconst FLOW_MAX_AGE = 600;\nconst TOKEN_MAX_AGE = 15 * 60;\n\ntype OtpType = \"email-verification\" | \"forget-password\" | \"sign-in\";\n\nconst FLOW_OF_TYPE: Record<OtpType, \"verification\" | \"recovery\" | \"login\"> = {\n \"email-verification\": \"verification\",\n \"forget-password\": \"recovery\",\n \"sign-in\": \"login\",\n};\n\nconst FAILURE_STATUS: Record<KratosFailure[\"status\"], number> = {\n invalid_credentials: 401,\n invalid_code: 400,\n email_not_verified: 403,\n second_factor_required: 403,\n account_disabled: 403,\n already_registered: 409,\n password_refused: 422,\n invalid_input: 400,\n flow_expired: 410,\n unavailable: 503,\n};\n\nfunction json(\n status: number,\n body: unknown,\n cookies: Array<string> = [],\n): Response {\n const headers = new Headers({ \"content-type\": \"application/json\" });\n for (const c of cookies) headers.append(\"set-cookie\", c);\n return new Response(JSON.stringify(body), { status, headers });\n}\n\nfunction failure(code: string, status: number, message?: string): Response {\n return json(status, {\n error: { code, status, ...(message ? { message } : {}) },\n });\n}\n\nfunction failed(error: unknown): Response {\n if (error instanceof KratosError) {\n const f = error.failure;\n return failure(\n f.status,\n FAILURE_STATUS[f.status],\n \"message\" in f ? f.message : undefined,\n );\n }\n return failure(\"unavailable\", 503);\n}\n\nfunction userOf(\n session: KratosSession,\n roles: Array<string>,\n product: string,\n): UrbangateUser | null {\n const identity = session.identity;\n if (!identity) return null;\n const email = identity.traits?.email ?? \"\";\n const address = identity.verifiable_addresses?.find((a) => a.value === email);\n return {\n id: identity.id,\n identityId: identity.id,\n email,\n emailVerified: address?.verified ?? false,\n name: identity.traits?.name ?? \"\",\n role: roles.includes(`${product}:admin`) ? \"admin\" : \"user\",\n };\n}\n\nfunction continueWith<A extends ContinueWith[\"action\"]>(\n list: Array<ContinueWith> | undefined,\n action: A,\n): Extract<ContinueWith, { action: A }> | undefined {\n return list?.find(\n (c): c is Extract<ContinueWith, { action: A }> => c.action === action,\n );\n}\n\nexport function createUrbangateAuth(\n config: UrbangateAuthConfig,\n): UrbangateAuth {\n const product = config.product;\n const kratos = new KratosFlows(config.kratosUrl, config.fetch);\n const exchange = new Exchange({ ...config.urbangate, product }, config.fetch);\n const names = {\n session: config.cookie?.name ?? `${product}_session`,\n token: `${product}_token`,\n flow: `${product}_flow`,\n };\n const secure =\n config.cookie?.secure ?? config.urbangate.issuerUrl.startsWith(\"https://\");\n\n const cookie = (name: string, value: string, maxAge: number) =>\n serializeCookie(name, value, { maxAge, secure });\n\n const signedIn = (result: KratosSessionResult): Array<string> => {\n const token = result.session_token;\n if (!token) return [];\n return [\n cookie(names.session, token, SESSION_MAX_AGE),\n clearCookie(names.flow, secure),\n ];\n };\n\n const readToken = (headers: Headers): PersonToken | null => {\n const raw = readCookie(headers, names.token);\n const decoded = raw ? decodeToken(raw) : null;\n return raw && decoded ? { accessToken: raw, ...decoded } : null;\n };\n\n async function accessToken(headers: Headers): Promise<AccessToken | null> {\n const sessionToken = readCookie(headers, names.session);\n if (!sessionToken) return null;\n const held = readToken(headers);\n if (held && !exchange.needsRefresh(held))\n return { token: held.accessToken };\n const outcome = await exchange.exchange(sessionToken);\n if (outcome.status === \"session_gone\" || outcome.status === \"inactive\")\n return null;\n if (outcome.status === \"unavailable\")\n throw new KratosError({ status: \"unavailable\" });\n return {\n token: outcome.token.accessToken,\n setCookie: cookie(names.token, outcome.token.accessToken, TOKEN_MAX_AGE),\n };\n }\n\n async function getSession(\n headers: Headers,\n ): Promise<UrbangateSession | null> {\n const sessionToken = readCookie(headers, names.session);\n if (!sessionToken) return null;\n const session = await kratos.whoami(sessionToken);\n if (!session?.active) return null;\n const held = readToken(headers);\n const user = userOf(session, held?.roles ?? [], product);\n if (!user) return null;\n return { user, session: { expiresAt: session.expires_at ?? \"\" } };\n }\n\n async function body(request: Request): Promise<Record<string, unknown>> {\n const raw = (await request.json().catch(() => null)) as Record<\n string,\n unknown\n > | null;\n return raw ?? {};\n }\n\n const str = (b: Record<string, unknown>, key: string) =>\n typeof b[key] === \"string\" ? (b[key] as string).trim() : \"\";\n\n async function signInEmail(request: Request): Promise<Response> {\n const b = await body(request);\n const email = str(b, \"email\");\n const password = typeof b.password === \"string\" ? b.password : \"\";\n if (!email || !password)\n return failure(\"invalid_input\", 400, \"email and password are required\");\n const flow = await kratos.start(\"login\");\n const result = await kratos.submit<KratosSessionResult>(\"login\", flow.id, {\n method: \"password\",\n identifier: email,\n password,\n });\n return json(\n 200,\n { user: result.session ? userOf(result.session, [], product) : null },\n signedIn(result),\n );\n }\n\n async function signUpEmail(request: Request): Promise<Response> {\n const b = await body(request);\n const email = str(b, \"email\");\n const password = typeof b.password === \"string\" ? b.password : \"\";\n if (!email || !password)\n return failure(\"invalid_input\", 400, \"email and password are required\");\n const flow = await kratos.start(\"registration\");\n const result = await kratos.submit<KratosSessionResult>(\n \"registration\",\n flow.id,\n {\n method: \"password\",\n traits: { email, ...(str(b, \"name\") ? { name: str(b, \"name\") } : {}) },\n password,\n },\n );\n const verification = continueWith(\n result.continue_with,\n \"show_verification_ui\",\n );\n const cookies = signedIn(result);\n if (verification)\n cookies.push(\n cookie(\n names.flow,\n `verification:${verification.flow.id}`,\n FLOW_MAX_AGE,\n ),\n );\n return json(\n 200,\n {\n user: result.session ? userOf(result.session, [], product) : null,\n ...(verification\n ? { verification: { flowId: verification.flow.id } }\n : {}),\n },\n cookies,\n );\n }\n\n async function sendOtp(request: Request): Promise<Response> {\n const b = await body(request);\n const email = str(b, \"email\");\n const type = str(b, \"type\") as OtpType;\n const kind = FLOW_OF_TYPE[type];\n if (!email || !kind)\n return failure(\"invalid_input\", 400, \"email and type are required\");\n const flow = await kratos.start(kind);\n const submitted = await kratos.submit(kind, flow.id, {\n method: \"code\",\n ...(kind === \"login\" ? { identifier: email } : { email }),\n });\n if (!codeWasSent(submitted) && submitted.state !== \"sent_email\") {\n return failure(\"invalid_input\", 400, \"the code could not be sent\");\n }\n return json(200, { sent: true }, [\n cookie(names.flow, `${kind}:${flow.id}`, FLOW_MAX_AGE),\n ]);\n }\n\n function pendingFlow(headers: Headers, kind: string): string | null {\n const raw = readCookie(headers, names.flow) ?? \"\";\n const [k, id] = raw.split(\":\");\n return k === kind && id ? id : null;\n }\n\n async function signInOtp(request: Request): Promise<Response> {\n const b = await body(request);\n const email = str(b, \"email\");\n const code = str(b, \"otp\");\n const flowId = pendingFlow(request.headers, \"login\");\n if (!flowId) return failure(\"flow_expired\", 410);\n if (!email || !code)\n return failure(\"invalid_input\", 400, \"email and otp are required\");\n const result = await kratos.submit<KratosSessionResult>(\"login\", flowId, {\n method: \"code\",\n identifier: email,\n code,\n });\n return json(\n 200,\n { user: result.session ? userOf(result.session, [], product) : null },\n signedIn(result),\n );\n }\n\n async function verifyEmail(request: Request): Promise<Response> {\n const b = await body(request);\n const code = str(b, \"otp\");\n const flowId = pendingFlow(request.headers, \"verification\");\n if (!flowId) return failure(\"flow_expired\", 410);\n if (!code) return failure(\"invalid_input\", 400, \"otp is required\");\n const flow = await kratos.submit(\"verification\", flowId, {\n method: \"code\",\n code,\n });\n if (flow.state !== \"passed_challenge\") return failure(\"invalid_code\", 400);\n return json(200, { verified: true }, [clearCookie(names.flow, secure)]);\n }\n\n async function resetPassword(request: Request): Promise<Response> {\n const b = await body(request);\n const code = str(b, \"otp\");\n const password = typeof b.password === \"string\" ? b.password : \"\";\n const flowId = pendingFlow(request.headers, \"recovery\");\n if (!flowId) return failure(\"flow_expired\", 410);\n if (!code || !password)\n return failure(\"invalid_input\", 400, \"otp and password are required\");\n const recovered = await kratos.submit<KratosSessionResult>(\n \"recovery\",\n flowId,\n { method: \"code\", code },\n );\n const token = continueWith(\n recovered.continue_with,\n \"set_ory_session_token\",\n )?.ory_session_token;\n const settings = continueWith(recovered.continue_with, \"show_settings_ui\");\n if (!token || !settings) return failure(\"invalid_code\", 400);\n await kratos.submit(\n \"settings\",\n settings.flow.id,\n { method: \"password\", password },\n token,\n );\n return json(200, { reset: true }, [\n cookie(names.session, token, SESSION_MAX_AGE),\n clearCookie(names.flow, secure),\n ]);\n }\n\n async function signOut(request: Request): Promise<Response> {\n const token = readCookie(request.headers, names.session);\n if (token) await kratos.logout(token);\n return json(200, { signedOut: true }, [\n clearCookie(names.session, secure),\n clearCookie(names.token, secure),\n clearCookie(names.flow, secure),\n ]);\n }\n\n async function session(request: Request): Promise<Response> {\n const s = await getSession(request.headers);\n return json(200, s);\n }\n\n const routes: Record<string, (request: Request) => Promise<Response>> = {\n \"POST sign-in/email\": signInEmail,\n \"POST sign-in/email-otp\": signInOtp,\n \"POST sign-up/email\": signUpEmail,\n \"POST email-otp/send-verification-otp\": sendOtp,\n \"POST email-otp/verify-email\": verifyEmail,\n \"POST email-otp/reset-password\": resetPassword,\n \"POST sign-out\": signOut,\n \"GET get-session\": session,\n };\n\n async function handler(request: Request): Promise<Response> {\n const path = new URL(request.url).pathname.replace(/\\/$/, \"\");\n const suffix = path.slice(path.indexOf(\"/api/auth/\") + \"/api/auth/\".length);\n if (suffix === \"sign-in/social\") return failure(\"not_supported\", 501);\n const route = routes[`${request.method} ${suffix}`];\n if (!route) return failure(\"not_found\", 404);\n try {\n return await route(request);\n } catch (error) {\n return failed(error);\n }\n }\n\n function coreProxy(options: UrbangateCoreProxyOptions) {\n const coreUrl = options.coreUrl.replace(/\\/$/, \"\");\n return async (request: Request): Promise<Response> => {\n let s: UrbangateSession | null;\n let token: AccessToken | null;\n try {\n token = await accessToken(request.headers);\n s = token\n ? await getSession(\n new Headers({ ...cookieHeader(request.headers, names, token) }),\n )\n : null;\n } catch {\n return failure(\"identity_provider_unavailable\", 503);\n }\n if (!token || !s) return failure(\"sign_in_required\", 401);\n if (options.adminOnly && s.user.role !== \"admin\")\n return failure(\"forbidden\", 403);\n const url = new URL(request.url);\n const headers = forwardHeaders(request.headers);\n headers.set(\"authorization\", `Bearer ${token.token}`);\n const upstream = await fetch(`${coreUrl}${url.pathname}${url.search}`, {\n method: request.method,\n headers,\n body:\n request.method === \"GET\" || request.method === \"HEAD\"\n ? undefined\n : request.body,\n duplex: \"half\",\n redirect: \"manual\",\n } as RequestInit);\n const out = forwardHeaders(upstream.headers);\n if (token.setCookie) out.append(\"set-cookie\", token.setCookie);\n return new Response(upstream.body, {\n status: upstream.status,\n headers: out,\n });\n };\n }\n\n return { handler, getSession, accessToken, coreProxy };\n}\n\n// A token just exchanged is not in the request's cookie yet; the session\n// lookup reads the roles off it as if it were.\nfunction cookieHeader(\n headers: Headers,\n names: { session: string; token: string },\n token: AccessToken,\n) {\n const session = readCookie(headers, names.session) ?? \"\";\n return {\n cookie: `${names.session}=${encodeURIComponent(session)}; ${names.token}=${encodeURIComponent(token.token)}`,\n };\n}\n"],"mappings":"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","names":["failure","session"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lalternative/auth",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Shared Better Auth wrapper for L'Alternative apps (server + React client + auth UI)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",