@lalternative/auth 1.0.0 → 1.0.2

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.
@@ -26,7 +26,13 @@ interface SessionState {
26
26
  status?: number;
27
27
  } | null;
28
28
  }
29
- type UrbangateAuthClient = Omit<AuthClientSurface, "admin"> & {
29
+ type UrbangateAuthClient = Omit<AuthClientSurface, "admin" | "signIn"> & {
30
+ signIn: AuthClientSurface["signIn"] & {
31
+ emailOtp(input: {
32
+ email: string;
33
+ otp: string;
34
+ }): Promise<AuthClientResult>;
35
+ };
30
36
  signOut(): Promise<AuthClientResult>;
31
37
  getSession(): Promise<{
32
38
  data: UrbangateClientSession | null;
@@ -32,6 +32,7 @@ function createUrbangateAuthClient(config = {}) {
32
32
  const client = {
33
33
  signIn: {
34
34
  email: (input) => call("POST", "sign-in/email", input),
35
+ emailOtp: (input) => call("POST", "sign-in/email-otp", input),
35
36
  social: async () => ({ error: { code: "not_supported", status: 501 } })
36
37
  },
37
38
  signUp: {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/urbangate/client.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { AuthClientResult, AuthClientSurface } from \"../types\";\n\nexport interface UrbangateAuthClientConfig {\n baseURL?: string;\n}\n\nexport interface UrbangateClientUser {\n id: string;\n identityId: string;\n email: string;\n emailVerified: boolean;\n name: string;\n role: \"admin\" | \"user\";\n}\n\nexport interface UrbangateClientSession {\n user: UrbangateClientUser;\n session: { expiresAt: string };\n}\n\nexport interface SessionState {\n data: UrbangateClientSession | null;\n isPending: boolean;\n error: { message?: string; status?: number } | null;\n}\n\nexport type UrbangateAuthClient = Omit<AuthClientSurface, \"admin\"> & {\n signOut(): Promise<AuthClientResult>;\n getSession(): Promise<{\n data: UrbangateClientSession | null;\n error: AuthClientResult[\"error\"];\n }>;\n useSession(): SessionState;\n};\n\ntype Result<T = unknown> = { data: T | null; error: AuthClientResult[\"error\"] };\n\nexport function createUrbangateAuthClient(\n config: UrbangateAuthClientConfig = {},\n): UrbangateAuthClient {\n const base = (\n config.baseURL ??\n (typeof window !== \"undefined\" ? window.location.origin : \"\")\n ).replace(/\\/$/, \"\");\n\n async function call<T = unknown>(\n method: \"GET\" | \"POST\",\n path: string,\n body?: unknown,\n ): Promise<Result<T>> {\n try {\n const res = await fetch(`${base}/api/auth/${path}`, {\n method,\n credentials: \"include\",\n headers:\n body === undefined ? {} : { \"content-type\": \"application/json\" },\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n const parsed = (await res.json().catch(() => null)) as\n { error?: AuthClientResult[\"error\"] } | T | null;\n if (!res.ok) {\n const error = (parsed as { error?: AuthClientResult[\"error\"] } | null)\n ?.error;\n return {\n data: null,\n error: error ?? { status: res.status, message: res.statusText },\n };\n }\n return { data: parsed as T, error: null };\n } catch (err) {\n return {\n data: null,\n error: {\n message: err instanceof Error ? err.message : \"network\",\n status: 0,\n },\n };\n }\n }\n\n const client: UrbangateAuthClient = {\n signIn: {\n email: (input) => call(\"POST\", \"sign-in/email\", input),\n social: async () => ({ error: { code: \"not_supported\", status: 501 } }),\n },\n signUp: {\n email: (input) => call(\"POST\", \"sign-up/email\", input),\n },\n emailOtp: {\n sendVerificationOtp: (input) =>\n call(\"POST\", \"email-otp/send-verification-otp\", input),\n verifyEmail: (input) => call(\"POST\", \"email-otp/verify-email\", input),\n resetPassword: (input) => call(\"POST\", \"email-otp/reset-password\", input),\n },\n signOut: () => call(\"POST\", \"sign-out\"),\n getSession: () => call<UrbangateClientSession | null>(\"GET\", \"get-session\"),\n useSession() {\n const [state, setState] = useState<SessionState>({\n data: null,\n isPending: true,\n error: null,\n });\n useEffect(() => {\n let live = true;\n void client.getSession().then(({ data, error }) => {\n if (live) setState({ data, isPending: false, error: error ?? null });\n });\n return () => {\n live = false;\n };\n }, []);\n return state;\n },\n };\n return client;\n}\n"],"mappings":";AAAA,SAAS,WAAW,gBAAgB;AAsC7B,SAAS,0BACd,SAAoC,CAAC,GAChB;AACrB,QAAM,QACJ,OAAO,YACN,OAAO,WAAW,cAAc,OAAO,SAAS,SAAS,KAC1D,QAAQ,OAAO,EAAE;AAEnB,iBAAe,KACb,QACA,MACA,MACoB;AACpB,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,aAAa,IAAI,IAAI;AAAA,QAClD;AAAA,QACA,aAAa;AAAA,QACb,SACE,SAAS,SAAY,CAAC,IAAI,EAAE,gBAAgB,mBAAmB;AAAA,QACjE,MAAM,SAAS,SAAY,SAAY,KAAK,UAAU,IAAI;AAAA,MAC5D,CAAC;AACD,YAAM,SAAU,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AAEjD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,QAAS,QACX;AACJ,eAAO;AAAA,UACL,MAAM;AAAA,UACN,OAAO,SAAS,EAAE,QAAQ,IAAI,QAAQ,SAAS,IAAI,WAAW;AAAA,QAChE;AAAA,MACF;AACA,aAAO,EAAE,MAAM,QAAa,OAAO,KAAK;AAAA,IAC1C,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO;AAAA,UACL,SAAS,eAAe,QAAQ,IAAI,UAAU;AAAA,UAC9C,QAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAA8B;AAAA,IAClC,QAAQ;AAAA,MACN,OAAO,CAAC,UAAU,KAAK,QAAQ,iBAAiB,KAAK;AAAA,MACrD,QAAQ,aAAa,EAAE,OAAO,EAAE,MAAM,iBAAiB,QAAQ,IAAI,EAAE;AAAA,IACvE;AAAA,IACA,QAAQ;AAAA,MACN,OAAO,CAAC,UAAU,KAAK,QAAQ,iBAAiB,KAAK;AAAA,IACvD;AAAA,IACA,UAAU;AAAA,MACR,qBAAqB,CAAC,UACpB,KAAK,QAAQ,mCAAmC,KAAK;AAAA,MACvD,aAAa,CAAC,UAAU,KAAK,QAAQ,0BAA0B,KAAK;AAAA,MACpE,eAAe,CAAC,UAAU,KAAK,QAAQ,4BAA4B,KAAK;AAAA,IAC1E;AAAA,IACA,SAAS,MAAM,KAAK,QAAQ,UAAU;AAAA,IACtC,YAAY,MAAM,KAAoC,OAAO,aAAa;AAAA,IAC1E,aAAa;AACX,YAAM,CAAC,OAAO,QAAQ,IAAI,SAAuB;AAAA,QAC/C,MAAM;AAAA,QACN,WAAW;AAAA,QACX,OAAO;AAAA,MACT,CAAC;AACD,gBAAU,MAAM;AACd,YAAI,OAAO;AACX,aAAK,OAAO,WAAW,EAAE,KAAK,CAAC,EAAE,MAAM,MAAM,MAAM;AACjD,cAAI,KAAM,UAAS,EAAE,MAAM,WAAW,OAAO,OAAO,SAAS,KAAK,CAAC;AAAA,QACrE,CAAC;AACD,eAAO,MAAM;AACX,iBAAO;AAAA,QACT;AAAA,MACF,GAAG,CAAC,CAAC;AACL,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/urbangate/client.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { AuthClientResult, AuthClientSurface } from \"../types\";\n\nexport interface UrbangateAuthClientConfig {\n baseURL?: string;\n}\n\nexport interface UrbangateClientUser {\n id: string;\n identityId: string;\n email: string;\n emailVerified: boolean;\n name: string;\n role: \"admin\" | \"user\";\n}\n\nexport interface UrbangateClientSession {\n user: UrbangateClientUser;\n session: { expiresAt: string };\n}\n\nexport interface SessionState {\n data: UrbangateClientSession | null;\n isPending: boolean;\n error: { message?: string; status?: number } | null;\n}\n\nexport type UrbangateAuthClient = Omit<\n AuthClientSurface,\n \"admin\" | \"signIn\"\n> & {\n signIn: AuthClientSurface[\"signIn\"] & {\n emailOtp(input: { email: string; otp: string }): Promise<AuthClientResult>;\n };\n signOut(): Promise<AuthClientResult>;\n getSession(): Promise<{\n data: UrbangateClientSession | null;\n error: AuthClientResult[\"error\"];\n }>;\n useSession(): SessionState;\n};\n\ntype Result<T = unknown> = { data: T | null; error: AuthClientResult[\"error\"] };\n\nexport function createUrbangateAuthClient(\n config: UrbangateAuthClientConfig = {},\n): UrbangateAuthClient {\n const base = (\n config.baseURL ??\n (typeof window !== \"undefined\" ? window.location.origin : \"\")\n ).replace(/\\/$/, \"\");\n\n async function call<T = unknown>(\n method: \"GET\" | \"POST\",\n path: string,\n body?: unknown,\n ): Promise<Result<T>> {\n try {\n const res = await fetch(`${base}/api/auth/${path}`, {\n method,\n credentials: \"include\",\n headers:\n body === undefined ? {} : { \"content-type\": \"application/json\" },\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n const parsed = (await res.json().catch(() => null)) as\n { error?: AuthClientResult[\"error\"] } | T | null;\n if (!res.ok) {\n const error = (parsed as { error?: AuthClientResult[\"error\"] } | null)\n ?.error;\n return {\n data: null,\n error: error ?? { status: res.status, message: res.statusText },\n };\n }\n return { data: parsed as T, error: null };\n } catch (err) {\n return {\n data: null,\n error: {\n message: err instanceof Error ? err.message : \"network\",\n status: 0,\n },\n };\n }\n }\n\n const client: UrbangateAuthClient = {\n signIn: {\n email: (input) => call(\"POST\", \"sign-in/email\", input),\n emailOtp: (input) => call(\"POST\", \"sign-in/email-otp\", input),\n social: async () => ({ error: { code: \"not_supported\", status: 501 } }),\n },\n signUp: {\n email: (input) => call(\"POST\", \"sign-up/email\", input),\n },\n emailOtp: {\n sendVerificationOtp: (input) =>\n call(\"POST\", \"email-otp/send-verification-otp\", input),\n verifyEmail: (input) => call(\"POST\", \"email-otp/verify-email\", input),\n resetPassword: (input) => call(\"POST\", \"email-otp/reset-password\", input),\n },\n signOut: () => call(\"POST\", \"sign-out\"),\n getSession: () => call<UrbangateClientSession | null>(\"GET\", \"get-session\"),\n useSession() {\n const [state, setState] = useState<SessionState>({\n data: null,\n isPending: true,\n error: null,\n });\n useEffect(() => {\n let live = true;\n void client.getSession().then(({ data, error }) => {\n if (live) setState({ data, isPending: false, error: error ?? null });\n });\n return () => {\n live = false;\n };\n }, []);\n return state;\n },\n };\n return client;\n}\n"],"mappings":";AAAA,SAAS,WAAW,gBAAgB;AA4C7B,SAAS,0BACd,SAAoC,CAAC,GAChB;AACrB,QAAM,QACJ,OAAO,YACN,OAAO,WAAW,cAAc,OAAO,SAAS,SAAS,KAC1D,QAAQ,OAAO,EAAE;AAEnB,iBAAe,KACb,QACA,MACA,MACoB;AACpB,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,aAAa,IAAI,IAAI;AAAA,QAClD;AAAA,QACA,aAAa;AAAA,QACb,SACE,SAAS,SAAY,CAAC,IAAI,EAAE,gBAAgB,mBAAmB;AAAA,QACjE,MAAM,SAAS,SAAY,SAAY,KAAK,UAAU,IAAI;AAAA,MAC5D,CAAC;AACD,YAAM,SAAU,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AAEjD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,QAAS,QACX;AACJ,eAAO;AAAA,UACL,MAAM;AAAA,UACN,OAAO,SAAS,EAAE,QAAQ,IAAI,QAAQ,SAAS,IAAI,WAAW;AAAA,QAChE;AAAA,MACF;AACA,aAAO,EAAE,MAAM,QAAa,OAAO,KAAK;AAAA,IAC1C,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO;AAAA,UACL,SAAS,eAAe,QAAQ,IAAI,UAAU;AAAA,UAC9C,QAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAA8B;AAAA,IAClC,QAAQ;AAAA,MACN,OAAO,CAAC,UAAU,KAAK,QAAQ,iBAAiB,KAAK;AAAA,MACrD,UAAU,CAAC,UAAU,KAAK,QAAQ,qBAAqB,KAAK;AAAA,MAC5D,QAAQ,aAAa,EAAE,OAAO,EAAE,MAAM,iBAAiB,QAAQ,IAAI,EAAE;AAAA,IACvE;AAAA,IACA,QAAQ;AAAA,MACN,OAAO,CAAC,UAAU,KAAK,QAAQ,iBAAiB,KAAK;AAAA,IACvD;AAAA,IACA,UAAU;AAAA,MACR,qBAAqB,CAAC,UACpB,KAAK,QAAQ,mCAAmC,KAAK;AAAA,MACvD,aAAa,CAAC,UAAU,KAAK,QAAQ,0BAA0B,KAAK;AAAA,MACpE,eAAe,CAAC,UAAU,KAAK,QAAQ,4BAA4B,KAAK;AAAA,IAC1E;AAAA,IACA,SAAS,MAAM,KAAK,QAAQ,UAAU;AAAA,IACtC,YAAY,MAAM,KAAoC,OAAO,aAAa;AAAA,IAC1E,aAAa;AACX,YAAM,CAAC,OAAO,QAAQ,IAAI,SAAuB;AAAA,QAC/C,MAAM;AAAA,QACN,WAAW;AAAA,QACX,OAAO;AAAA,MACT,CAAC;AACD,gBAAU,MAAM;AACd,YAAI,OAAO;AACX,aAAK,OAAO,WAAW,EAAE,KAAK,CAAC,EAAE,MAAM,MAAM,MAAM;AACjD,cAAI,KAAM,UAAS,EAAE,MAAM,WAAW,OAAO,OAAO,SAAS,KAAK,CAAC;AAAA,QACrE,CAAC;AACD,eAAO,MAAM;AACX,iBAAO;AAAA,QACT;AAAA,MACF,GAAG,CAAC,CAAC;AACL,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
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
  }
@@ -178,6 +176,7 @@ var INVALID_CREDENTIALS = 4000006;
178
176
  var INVALID_CODE = /* @__PURE__ */ new Set([4010008, 4060006, 4070006]);
179
177
  var CODE_SENT = /* @__PURE__ */ new Set([1010014, 1040005, 1060003, 1070003, 1080003]);
180
178
  var ALREADY_REGISTERED = 4000007;
179
+ var ACCOUNT_NOT_FOUND = 4000035;
181
180
  var PASSWORD_POLICY = /* @__PURE__ */ new Set([4000005, 4000031, 4000032, 4000033, 4000034]);
182
181
  var FLOW_EXPIRED = /* @__PURE__ */ new Set([4010001, 4040001, 4060005, 4070005]);
183
182
  function failureOf(status, body) {
@@ -199,6 +198,7 @@ function failureOf(status, body) {
199
198
  if (m.id === INVALID_CREDENTIALS) return { status: "invalid_credentials" };
200
199
  if (INVALID_CODE.has(m.id ?? -1)) return { status: "invalid_code" };
201
200
  if (m.id === ALREADY_REGISTERED) return { status: "already_registered" };
201
+ if (m.id === ACCOUNT_NOT_FOUND) return { status: "account_not_found" };
202
202
  if (PASSWORD_POLICY.has(m.id ?? -1)) {
203
203
  return { status: "password_refused", message: m.text ?? "" };
204
204
  }
@@ -300,6 +300,7 @@ var FAILURE_STATUS = {
300
300
  second_factor_required: 403,
301
301
  account_disabled: 403,
302
302
  already_registered: 409,
303
+ account_not_found: 404,
303
304
  password_refused: 422,
304
305
  invalid_input: 400,
305
306
  flow_expired: 410,
@@ -463,17 +464,34 @@ function createUrbangateAuth(config) {
463
464
  const kind = FLOW_OF_TYPE[type];
464
465
  if (!email || !kind)
465
466
  return failure("invalid_input", 400, "email and type are required");
467
+ const sent = await sendCode(kind, email);
468
+ return json(200, { sent: true }, [
469
+ cookie(names.flow, `${sent.kind}:${sent.flowId}`, FLOW_MAX_AGE)
470
+ ]);
471
+ }
472
+ async function sendCode(kind, email) {
473
+ try {
474
+ return await sendCodeOn(kind, email);
475
+ } catch (error) {
476
+ if (kind !== "login" || !(error instanceof KratosError) || error.failure.status !== "account_not_found") {
477
+ throw error;
478
+ }
479
+ return sendCodeOn("registration", email);
480
+ }
481
+ }
482
+ async function sendCodeOn(kind, email) {
466
483
  const flow = await kratos.start(kind);
467
484
  const submitted = await kratos.submit(kind, flow.id, {
468
485
  method: "code",
469
- ...kind === "login" ? { identifier: email } : { email }
486
+ ...kind === "login" ? { identifier: email } : kind === "registration" ? { traits: { email } } : { email }
470
487
  });
471
488
  if (!codeWasSent(submitted) && submitted.state !== "sent_email") {
472
- return failure("invalid_input", 400, "the code could not be sent");
489
+ throw new KratosError({
490
+ status: "invalid_input",
491
+ message: "the code could not be sent"
492
+ });
473
493
  }
474
- return json(200, { sent: true }, [
475
- cookie(names.flow, `${kind}:${flow.id}`, FLOW_MAX_AGE)
476
- ]);
494
+ return { kind, flowId: flow.id };
477
495
  }
478
496
  function pendingFlow(headers, kind) {
479
497
  const raw = readCookie(headers, names.flow) ?? "";
@@ -484,14 +502,20 @@ function createUrbangateAuth(config) {
484
502
  const b = await body(request);
485
503
  const email = str(b, "email");
486
504
  const code = str(b, "otp");
487
- const flowId = pendingFlow(request.headers, "login");
505
+ const login = pendingFlow(request.headers, "login");
506
+ const registration = pendingFlow(request.headers, "registration");
507
+ const flowId = login ?? registration;
488
508
  if (!flowId) return failure("flow_expired", 410);
489
509
  if (!email || !code)
490
510
  return failure("invalid_input", 400, "email and otp are required");
491
- const result = await kratos.submit("login", flowId, {
511
+ const result = login ? await kratos.submit("login", flowId, {
492
512
  method: "code",
493
513
  identifier: email,
494
514
  code
515
+ }) : await kratos.submit("registration", flowId, {
516
+ method: "code",
517
+ traits: { email },
518
+ code
495
519
  });
496
520
  return json(
497
521
  200,
@@ -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":"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","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: \"account_not_found\" }\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 ACCOUNT_NOT_FOUND = 4000035;\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 (m.id === ACCOUNT_NOT_FOUND) return { status: \"account_not_found\" };\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 account_not_found: 404,\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 sent = await sendCode(kind, email);\n return json(200, { sent: true }, [\n cookie(names.flow, `${sent.kind}:${sent.flowId}`, FLOW_MAX_AGE),\n ]);\n }\n\n // A code asked for an address Kratos does not know is a sign-up, not a\n // refusal: the person typed their address on the product's screen and\n // expects a code either way, and a registration by code opens the session\n // exactly as a login does.\n async function sendCode(\n kind: \"verification\" | \"recovery\" | \"login\",\n email: string,\n ): Promise<{ kind: string; flowId: string }> {\n try {\n return await sendCodeOn(kind, email);\n } catch (error) {\n if (\n kind !== \"login\" ||\n !(error instanceof KratosError) ||\n error.failure.status !== \"account_not_found\"\n ) {\n throw error;\n }\n return sendCodeOn(\"registration\", email);\n }\n }\n\n async function sendCodeOn(\n kind: \"verification\" | \"recovery\" | \"login\" | \"registration\",\n email: string,\n ): Promise<{ kind: string; flowId: string }> {\n const flow = await kratos.start(kind);\n const submitted = await kratos.submit(kind, flow.id, {\n method: \"code\",\n ...(kind === \"login\"\n ? { identifier: email }\n : kind === \"registration\"\n ? { traits: { email } }\n : { email }),\n });\n if (!codeWasSent(submitted) && submitted.state !== \"sent_email\") {\n throw new KratosError({\n status: \"invalid_input\",\n message: \"the code could not be sent\",\n });\n }\n return { kind, flowId: flow.id };\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 login = pendingFlow(request.headers, \"login\");\n const registration = pendingFlow(request.headers, \"registration\");\n const flowId = login ?? registration;\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 = login\n ? await kratos.submit<KratosSessionResult>(\"login\", flowId, {\n method: \"code\",\n identifier: email,\n code,\n })\n : await kratos.submit<KratosSessionResult>(\"registration\", flowId, {\n method: \"code\",\n traits: { 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.2",
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",