@_mustachio/openauth 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (277) hide show
  1. package/dist/esm/adapters/d1/index.js +18 -9
  2. package/dist/esm/adapters/d1/migrations.js +7 -2
  3. package/dist/esm/adapters/d1/session-store.js +39 -0
  4. package/dist/esm/adapters/d1/session.js +2 -2
  5. package/dist/esm/adapters/durable-object/index.js +4 -0
  6. package/dist/esm/adapters/durable-object/session-store.js +42 -0
  7. package/dist/esm/adapters/dynamo/index.js +24 -7
  8. package/dist/esm/adapters/dynamo/session-store.js +49 -0
  9. package/dist/esm/adapters/kms/index.js +6 -2
  10. package/dist/esm/adapters/kv/index.js +6 -2
  11. package/dist/esm/adapters/memory/index.js +24 -7
  12. package/dist/esm/adapters/memory/session-store.js +45 -0
  13. package/dist/esm/adapters/memory/token-store.js +13 -0
  14. package/dist/esm/adapters/postgres/executor.js +2 -2
  15. package/dist/esm/adapters/postgres/index.js +33 -10
  16. package/dist/esm/adapters/postgres/migrations.js +8 -2
  17. package/dist/esm/adapters/postgres/session-store.js +42 -0
  18. package/dist/esm/client.js +35 -28
  19. package/dist/esm/domain/authorize.js +7 -2
  20. package/dist/esm/domain/callback.js +124 -7
  21. package/dist/esm/domain/client-auth.js +2 -2
  22. package/dist/esm/domain/crypto.js +14 -14
  23. package/dist/esm/domain/discovery.js +46 -4
  24. package/dist/esm/domain/dpop.js +122 -0
  25. package/dist/esm/domain/id-token.js +94 -0
  26. package/dist/esm/domain/introspect.js +5 -1
  27. package/dist/esm/domain/jwt.js +30 -2
  28. package/dist/esm/domain/logout.js +75 -0
  29. package/dist/esm/domain/method-dispatch.js +27 -1
  30. package/dist/esm/domain/method-route.js +83 -1
  31. package/dist/esm/domain/par.js +51 -0
  32. package/dist/esm/domain/pkce.js +2 -2
  33. package/dist/esm/domain/refresh.js +12 -1
  34. package/dist/esm/domain/register.js +74 -0
  35. package/dist/esm/domain/revoke.js +2 -2
  36. package/dist/esm/domain/state-envelope.js +2 -2
  37. package/dist/esm/domain/token-exchange.js +1 -0
  38. package/dist/esm/domain/token.js +53 -12
  39. package/dist/esm/domain/userinfo.js +38 -3
  40. package/dist/esm/error.js +8 -8
  41. package/dist/esm/http/cookies.js +19 -2
  42. package/dist/esm/http/errors.js +4 -4
  43. package/dist/esm/http/handlers/authorize.js +92 -21
  44. package/dist/esm/http/handlers/callback.js +7 -1
  45. package/dist/esm/http/handlers/end-session.js +64 -0
  46. package/dist/esm/http/handlers/metadata.js +7 -4
  47. package/dist/esm/http/handlers/method-route.js +47 -2
  48. package/dist/esm/http/handlers/par.js +53 -0
  49. package/dist/esm/http/handlers/register.js +46 -0
  50. package/dist/esm/http/handlers/revocation.js +2 -2
  51. package/dist/esm/http/handlers/token.js +36 -6
  52. package/dist/esm/http/handlers/userinfo.js +35 -29
  53. package/dist/esm/http/middleware/tenant.js +6 -6
  54. package/dist/esm/http/router.js +10 -0
  55. package/dist/esm/http/schemas/authorize.js +8 -2
  56. package/dist/esm/http/schemas/end-session.js +13 -0
  57. package/dist/esm/http/schemas/par.js +9 -0
  58. package/dist/esm/http/schemas/register.js +16 -0
  59. package/dist/esm/http/schemas/revocation.js +2 -2
  60. package/dist/esm/http/schemas/token.js +7 -7
  61. package/dist/esm/index.js +40 -34
  62. package/dist/esm/methods/oauth2-factory.js +2 -2
  63. package/dist/esm/methods/password-hash.js +2 -2
  64. package/dist/esm/methods/providers/index.js +30 -14
  65. package/dist/esm/methods/saml-sp/acs.js +208 -0
  66. package/dist/esm/methods/saml-sp/attributes.js +75 -0
  67. package/dist/esm/methods/saml-sp/authnrequest.js +63 -0
  68. package/dist/esm/methods/saml-sp/cache-provider.js +33 -0
  69. package/dist/esm/methods/saml-sp/cert-rotation.js +13 -0
  70. package/dist/esm/methods/saml-sp/factory.js +70 -0
  71. package/dist/esm/methods/saml-sp/index.js +7 -0
  72. package/dist/esm/methods/saml-sp/metadata.js +69 -0
  73. package/dist/esm/methods/saml-sp/method.js +29 -0
  74. package/dist/esm/methods/saml-sp/parse-idp-metadata.js +102 -0
  75. package/dist/esm/methods/saml-sp/saml-instance.js +49 -0
  76. package/dist/esm/methods/saml-sp/slo-initiate.js +84 -0
  77. package/dist/esm/methods/saml-sp/sls.js +154 -0
  78. package/dist/esm/methods/saml-sp/types.js +0 -0
  79. package/dist/esm/types/error.js +5 -0
  80. package/dist/esm/types/result.js +3 -3
  81. package/dist/esm/ui/forms.js +3 -3
  82. package/dist/esm/ui/index.js +8 -5
  83. package/dist/esm/ui/picker.js +2 -2
  84. package/dist/types/adapters/d1/migrations.d.ts.map +1 -1
  85. package/dist/types/adapters/d1/session-store.d.ts +3 -0
  86. package/dist/types/adapters/d1/session-store.d.ts.map +1 -1
  87. package/dist/types/adapters/durable-object/session-store.d.ts +3 -0
  88. package/dist/types/adapters/durable-object/session-store.d.ts.map +1 -1
  89. package/dist/types/adapters/dynamo/session-store.d.ts +3 -0
  90. package/dist/types/adapters/dynamo/session-store.d.ts.map +1 -1
  91. package/dist/types/adapters/memory/session-store.d.ts +6 -1
  92. package/dist/types/adapters/memory/session-store.d.ts.map +1 -1
  93. package/dist/types/adapters/memory/token-store.d.ts +1 -0
  94. package/dist/types/adapters/memory/token-store.d.ts.map +1 -1
  95. package/dist/types/adapters/postgres/migrations.d.ts +1 -1
  96. package/dist/types/adapters/postgres/migrations.d.ts.map +1 -1
  97. package/dist/types/adapters/postgres/session-store.d.ts +3 -0
  98. package/dist/types/adapters/postgres/session-store.d.ts.map +1 -1
  99. package/dist/types/client.d.ts +19 -0
  100. package/dist/types/client.d.ts.map +1 -1
  101. package/dist/types/domain/authorize.d.ts.map +1 -1
  102. package/dist/types/domain/callback.d.ts +16 -1
  103. package/dist/types/domain/callback.d.ts.map +1 -1
  104. package/dist/types/domain/client-credentials.d.ts +2 -0
  105. package/dist/types/domain/client-credentials.d.ts.map +1 -1
  106. package/dist/types/domain/discovery.d.ts +37 -0
  107. package/dist/types/domain/discovery.d.ts.map +1 -1
  108. package/dist/types/domain/dpop.d.ts +61 -0
  109. package/dist/types/domain/dpop.d.ts.map +1 -0
  110. package/dist/types/domain/id-token.d.ts +83 -0
  111. package/dist/types/domain/id-token.d.ts.map +1 -0
  112. package/dist/types/domain/introspect.d.ts +8 -0
  113. package/dist/types/domain/introspect.d.ts.map +1 -1
  114. package/dist/types/domain/jwt.d.ts +27 -1
  115. package/dist/types/domain/jwt.d.ts.map +1 -1
  116. package/dist/types/domain/logout.d.ts +69 -0
  117. package/dist/types/domain/logout.d.ts.map +1 -0
  118. package/dist/types/domain/method-dispatch.d.ts.map +1 -1
  119. package/dist/types/domain/method-route.d.ts +57 -1
  120. package/dist/types/domain/method-route.d.ts.map +1 -1
  121. package/dist/types/domain/par.d.ts +62 -0
  122. package/dist/types/domain/par.d.ts.map +1 -0
  123. package/dist/types/domain/refresh.d.ts +9 -0
  124. package/dist/types/domain/refresh.d.ts.map +1 -1
  125. package/dist/types/domain/register.d.ts +33 -0
  126. package/dist/types/domain/register.d.ts.map +1 -0
  127. package/dist/types/domain/token-exchange.d.ts +2 -0
  128. package/dist/types/domain/token-exchange.d.ts.map +1 -1
  129. package/dist/types/domain/token.d.ts +41 -1
  130. package/dist/types/domain/token.d.ts.map +1 -1
  131. package/dist/types/domain/userinfo.d.ts +44 -5
  132. package/dist/types/domain/userinfo.d.ts.map +1 -1
  133. package/dist/types/http/context.d.ts +6 -1
  134. package/dist/types/http/context.d.ts.map +1 -1
  135. package/dist/types/http/cookies.d.ts +9 -1
  136. package/dist/types/http/cookies.d.ts.map +1 -1
  137. package/dist/types/http/handlers/authorize.d.ts.map +1 -1
  138. package/dist/types/http/handlers/callback.d.ts.map +1 -1
  139. package/dist/types/http/handlers/end-session.d.ts +3 -0
  140. package/dist/types/http/handlers/end-session.d.ts.map +1 -0
  141. package/dist/types/http/handlers/metadata.d.ts +1 -1
  142. package/dist/types/http/handlers/metadata.d.ts.map +1 -1
  143. package/dist/types/http/handlers/method-route.d.ts.map +1 -1
  144. package/dist/types/http/handlers/par.d.ts +3 -0
  145. package/dist/types/http/handlers/par.d.ts.map +1 -0
  146. package/dist/types/http/handlers/register.d.ts +3 -0
  147. package/dist/types/http/handlers/register.d.ts.map +1 -0
  148. package/dist/types/http/handlers/token.d.ts.map +1 -1
  149. package/dist/types/http/handlers/userinfo.d.ts.map +1 -1
  150. package/dist/types/http/router.d.ts.map +1 -1
  151. package/dist/types/http/schemas/authorize.d.ts +40 -0
  152. package/dist/types/http/schemas/authorize.d.ts.map +1 -1
  153. package/dist/types/http/schemas/end-session.d.ts +31 -0
  154. package/dist/types/http/schemas/end-session.d.ts.map +1 -0
  155. package/dist/types/http/schemas/par.d.ts +20 -0
  156. package/dist/types/http/schemas/par.d.ts.map +1 -0
  157. package/dist/types/http/schemas/register.d.ts +42 -0
  158. package/dist/types/http/schemas/register.d.ts.map +1 -0
  159. package/dist/types/index.d.ts +3 -3
  160. package/dist/types/index.d.ts.map +1 -1
  161. package/dist/types/methods/saml-sp/acs.d.ts +4 -0
  162. package/dist/types/methods/saml-sp/acs.d.ts.map +1 -0
  163. package/dist/types/methods/saml-sp/attributes.d.ts +40 -0
  164. package/dist/types/methods/saml-sp/attributes.d.ts.map +1 -0
  165. package/dist/types/methods/saml-sp/authnrequest.d.ts +4 -0
  166. package/dist/types/methods/saml-sp/authnrequest.d.ts.map +1 -0
  167. package/dist/types/methods/saml-sp/cache-provider.d.ts +38 -0
  168. package/dist/types/methods/saml-sp/cache-provider.d.ts.map +1 -0
  169. package/dist/types/methods/saml-sp/cert-rotation.d.ts +23 -0
  170. package/dist/types/methods/saml-sp/cert-rotation.d.ts.map +1 -0
  171. package/dist/types/methods/saml-sp/factory.d.ts +704 -0
  172. package/dist/types/methods/saml-sp/factory.d.ts.map +1 -0
  173. package/dist/types/methods/saml-sp/index.d.ts +24 -0
  174. package/dist/types/methods/saml-sp/index.d.ts.map +1 -0
  175. package/dist/types/methods/saml-sp/metadata.d.ts +42 -0
  176. package/dist/types/methods/saml-sp/metadata.d.ts.map +1 -0
  177. package/dist/types/methods/saml-sp/method.d.ts +17 -0
  178. package/dist/types/methods/saml-sp/method.d.ts.map +1 -0
  179. package/dist/types/methods/saml-sp/parse-idp-metadata.d.ts +4 -0
  180. package/dist/types/methods/saml-sp/parse-idp-metadata.d.ts.map +1 -0
  181. package/dist/types/methods/saml-sp/saml-instance.d.ts +81 -0
  182. package/dist/types/methods/saml-sp/saml-instance.d.ts.map +1 -0
  183. package/dist/types/methods/saml-sp/slo-initiate.d.ts +4 -0
  184. package/dist/types/methods/saml-sp/slo-initiate.d.ts.map +1 -0
  185. package/dist/types/methods/saml-sp/sls.d.ts +4 -0
  186. package/dist/types/methods/saml-sp/sls.d.ts.map +1 -0
  187. package/dist/types/methods/saml-sp/types.d.ts +204 -0
  188. package/dist/types/methods/saml-sp/types.d.ts.map +1 -0
  189. package/dist/types/ports/audit-log.d.ts +40 -0
  190. package/dist/types/ports/audit-log.d.ts.map +1 -1
  191. package/dist/types/ports/session-store.d.ts +50 -0
  192. package/dist/types/ports/session-store.d.ts.map +1 -1
  193. package/dist/types/ports/token-store.d.ts +12 -0
  194. package/dist/types/ports/token-store.d.ts.map +1 -1
  195. package/dist/types/types/authorization.d.ts +23 -0
  196. package/dist/types/types/authorization.d.ts.map +1 -1
  197. package/dist/types/types/error.d.ts +9 -0
  198. package/dist/types/types/error.d.ts.map +1 -1
  199. package/dist/types/types/flow.d.ts +10 -0
  200. package/dist/types/types/flow.d.ts.map +1 -1
  201. package/dist/types/types/idp.d.ts +162 -1
  202. package/dist/types/types/idp.d.ts.map +1 -1
  203. package/dist/types/types/method.d.ts +109 -0
  204. package/dist/types/types/method.d.ts.map +1 -1
  205. package/dist/types/types/tenant.d.ts +37 -0
  206. package/dist/types/types/tenant.d.ts.map +1 -1
  207. package/dist/types/types/token.d.ts +128 -0
  208. package/dist/types/types/token.d.ts.map +1 -1
  209. package/package.json +9 -1
  210. package/src/adapters/d1/migrations.ts +5 -0
  211. package/src/adapters/d1/session-store.ts +68 -0
  212. package/src/adapters/durable-object/session-store.ts +61 -0
  213. package/src/adapters/dynamo/session-store.ts +63 -0
  214. package/src/adapters/memory/session-store.ts +83 -1
  215. package/src/adapters/memory/token-store.ts +16 -0
  216. package/src/adapters/postgres/migrations.ts +6 -0
  217. package/src/adapters/postgres/session-store.ts +69 -0
  218. package/src/client.ts +32 -0
  219. package/src/domain/authorize.ts +9 -0
  220. package/src/domain/callback.ts +220 -7
  221. package/src/domain/client-credentials.ts +2 -0
  222. package/src/domain/discovery.ts +80 -2
  223. package/src/domain/dpop.ts +293 -0
  224. package/src/domain/id-token.ts +213 -0
  225. package/src/domain/introspect.ts +10 -0
  226. package/src/domain/jwt.ts +80 -1
  227. package/src/domain/logout.ts +180 -0
  228. package/src/domain/method-dispatch.ts +53 -1
  229. package/src/domain/method-route.ts +195 -1
  230. package/src/domain/par.ts +141 -0
  231. package/src/domain/refresh.ts +41 -0
  232. package/src/domain/register.ts +168 -0
  233. package/src/domain/token-exchange.ts +11 -0
  234. package/src/domain/token.ts +134 -8
  235. package/src/domain/userinfo.ts +121 -6
  236. package/src/http/context.ts +10 -0
  237. package/src/http/cookies.ts +19 -1
  238. package/src/http/handlers/authorize.ts +146 -24
  239. package/src/http/handlers/callback.ts +9 -1
  240. package/src/http/handlers/end-session.ts +106 -0
  241. package/src/http/handlers/metadata.ts +7 -2
  242. package/src/http/handlers/method-route.ts +75 -2
  243. package/src/http/handlers/par.ts +84 -0
  244. package/src/http/handlers/register.ts +70 -0
  245. package/src/http/handlers/token.ts +56 -0
  246. package/src/http/handlers/userinfo.ts +62 -38
  247. package/src/http/router.ts +19 -0
  248. package/src/http/schemas/authorize.ts +27 -0
  249. package/src/http/schemas/end-session.ts +23 -0
  250. package/src/http/schemas/par.ts +23 -0
  251. package/src/http/schemas/register.ts +29 -0
  252. package/src/index.ts +21 -1
  253. package/src/methods/saml-sp/acs.ts +399 -0
  254. package/src/methods/saml-sp/attributes.ts +137 -0
  255. package/src/methods/saml-sp/authnrequest.ts +128 -0
  256. package/src/methods/saml-sp/cache-provider.ts +76 -0
  257. package/src/methods/saml-sp/cert-rotation.ts +34 -0
  258. package/src/methods/saml-sp/factory.ts +118 -0
  259. package/src/methods/saml-sp/index.ts +34 -0
  260. package/src/methods/saml-sp/metadata.ts +204 -0
  261. package/src/methods/saml-sp/method.ts +65 -0
  262. package/src/methods/saml-sp/parse-idp-metadata.ts +164 -0
  263. package/src/methods/saml-sp/saml-instance.ts +173 -0
  264. package/src/methods/saml-sp/slo-initiate.ts +177 -0
  265. package/src/methods/saml-sp/sls.ts +306 -0
  266. package/src/methods/saml-sp/types.ts +203 -0
  267. package/src/ports/CONSISTENCY.md +77 -21
  268. package/src/ports/audit-log.ts +42 -0
  269. package/src/ports/session-store.ts +61 -0
  270. package/src/ports/token-store.ts +13 -0
  271. package/src/types/authorization.ts +25 -0
  272. package/src/types/error.ts +23 -0
  273. package/src/types/flow.ts +10 -0
  274. package/src/types/idp.ts +170 -0
  275. package/src/types/method.ts +110 -0
  276. package/src/types/tenant.ts +37 -0
  277. package/src/types/token.ts +135 -0
@@ -4,13 +4,16 @@ import { startAuthorize } from "../../domain/authorize";
4
4
  import { asTenantId } from "../../types/tenant";
5
5
  import { authError } from "../../types/error";
6
6
  import { renderPicker as renderDefaultPicker } from "../../ui/picker";
7
- import { applyResponsePolicy } from "../cookies";
7
+ import { applyResponsePolicy, cacheControlHeader } from "../cookies";
8
8
  import {
9
9
  authorizeDirectErrorResponse,
10
10
  authorizeRedirectErrorResponse,
11
11
  isNonRecoverable
12
12
  } from "../errors";
13
- import { authorizeQuerySchema } from "../schemas/authorize";
13
+ import {
14
+ authorizeQuerySchema,
15
+ authorizeRequestUriQuerySchema
16
+ } from "../schemas/authorize";
14
17
  function makeAuthorizeHandler(deps) {
15
18
  return async (c) => {
16
19
  const tenant = c.get("tenant");
@@ -19,13 +22,37 @@ function makeAuthorizeHandler(deps) {
19
22
  }
20
23
  const url = new URL(c.req.url);
21
24
  const raw = Object.fromEntries(url.searchParams.entries());
22
- const parsed = authorizeQuerySchema.safeParse(raw);
25
+ let workingRaw = raw;
26
+ if (raw.request_uri !== undefined) {
27
+ const parParsed = authorizeRequestUriQuerySchema.safeParse(raw);
28
+ if (!parParsed.success) {
29
+ return authorizeDirectErrorResponse(authError.invalidRequest(parParsed.error.issues[0]?.message ?? "invalid request", parParsed.error.issues[0]?.path[0]?.toString() ?? "request"));
30
+ }
31
+ if (!deps.sessionStore.consumePar) {
32
+ return authorizeDirectErrorResponse(authError.invalidRequest("session adapter does not support PAR", "request_uri"));
33
+ }
34
+ const par = await deps.sessionStore.consumePar(parParsed.data.request_uri);
35
+ if (isErr(par)) {
36
+ return authorizeDirectErrorResponse(authError.invalidRequest("unknown or expired request_uri", "request_uri"));
37
+ }
38
+ if (par.value.clientId !== parParsed.data.client_id) {
39
+ return authorizeDirectErrorResponse(authError.invalidRequest("request_uri belongs to a different client", "request_uri"));
40
+ }
41
+ workingRaw = par.value.params;
42
+ }
43
+ const parsed = authorizeQuerySchema.safeParse(workingRaw);
23
44
  if (!parsed.success) {
24
45
  const first = parsed.error.issues[0];
25
46
  const field = first?.path[0]?.toString() ?? "request";
26
47
  return authorizeDirectErrorResponse(authError.invalidRequest(first?.message ?? "invalid request", field));
27
48
  }
28
49
  const q = parsed.data;
50
+ if (raw.request_uri === undefined) {
51
+ const client = tenant.config.clients.find((c) => c.id === q.client_id);
52
+ if (client?.requirePushedAuthorizationRequests) {
53
+ return authorizeDirectErrorResponse(authError.invalidRequest("this client requires Pushed Authorization Requests (RFC 9126)", "request_uri"));
54
+ }
55
+ }
29
56
  if (q.response_type !== "code") {
30
57
  return authorizeDirectErrorResponse({
31
58
  code: "invalid_request",
@@ -33,6 +60,14 @@ function makeAuthorizeHandler(deps) {
33
60
  field: "response_type"
34
61
  });
35
62
  }
63
+ let claimsRequest;
64
+ if (q.claims !== undefined) {
65
+ const parsedClaims = parseClaimsParameter(q.claims);
66
+ if (!parsedClaims.ok) {
67
+ return authorizeDirectErrorResponse(authError.invalidRequest(parsedClaims.error, "claims"));
68
+ }
69
+ claimsRequest = parsedClaims.value;
70
+ }
36
71
  const request = {
37
72
  tenantId: asTenantId(tenant.id),
38
73
  clientId: q.client_id,
@@ -46,7 +81,8 @@ function makeAuthorizeHandler(deps) {
46
81
  ...q.code_challenge_method !== undefined ? { codeChallengeMethod: q.code_challenge_method } : {},
47
82
  ...q.prompt !== undefined ? { prompt: q.prompt } : {},
48
83
  ...q.ui_locales !== undefined ? { uiLocales: q.ui_locales } : {},
49
- ...q.nonce !== undefined ? { nonce: q.nonce } : {}
84
+ ...q.nonce !== undefined ? { nonce: q.nonce } : {},
85
+ ...claimsRequest !== undefined ? { claimsRequest } : {}
50
86
  };
51
87
  const result = await startAuthorize({
52
88
  request,
@@ -87,7 +123,7 @@ function makeAuthorizeHandler(deps) {
87
123
  return applyResponsePolicy(out.response, {
88
124
  setCookies: [flowCookie, ...out.setCookies],
89
125
  cookieDefaults: deps.cookieDefaults,
90
- cacheControl: cacheControlFor(out.cache)
126
+ cacheControl: cacheControlHeader(out.cache)
91
127
  });
92
128
  }
93
129
  case "issue-code": {
@@ -132,6 +168,57 @@ function makeAuthorizeHandler(deps) {
132
168
  }
133
169
  };
134
170
  }
171
+ function parseClaimsParameter(raw) {
172
+ let parsed;
173
+ try {
174
+ parsed = JSON.parse(raw);
175
+ } catch (e) {
176
+ return {
177
+ ok: false,
178
+ error: `claims parameter is not valid JSON: ${e instanceof Error ? e.message : String(e)}`
179
+ };
180
+ }
181
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
182
+ return { ok: false, error: "claims parameter must be a JSON object" };
183
+ }
184
+ const obj = parsed;
185
+ const result = {};
186
+ for (const section of ["userinfo", "id_token"]) {
187
+ const v = obj[section];
188
+ if (v === undefined)
189
+ continue;
190
+ if (!v || typeof v !== "object" || Array.isArray(v)) {
191
+ return {
192
+ ok: false,
193
+ error: `claims.${section} must be a JSON object`
194
+ };
195
+ }
196
+ const sectionMap = {};
197
+ for (const [name, entry] of Object.entries(v)) {
198
+ if (entry === null) {
199
+ sectionMap[name] = null;
200
+ continue;
201
+ }
202
+ if (typeof entry !== "object" || Array.isArray(entry)) {
203
+ return {
204
+ ok: false,
205
+ error: `claims.${section}.${name} must be null or a JSON object`
206
+ };
207
+ }
208
+ const e = entry;
209
+ const normalized = {};
210
+ if (typeof e.essential === "boolean")
211
+ normalized.essential = e.essential;
212
+ if ("value" in e)
213
+ normalized.value = e.value;
214
+ if (Array.isArray(e.values))
215
+ normalized.values = e.values;
216
+ sectionMap[name] = normalized;
217
+ }
218
+ result[section] = sectionMap;
219
+ }
220
+ return { ok: true, value: result };
221
+ }
135
222
  function clearFlowCookie() {
136
223
  return {
137
224
  name: "idp.flow",
@@ -142,22 +229,6 @@ function clearFlowCookie() {
142
229
  maxAge: 0
143
230
  };
144
231
  }
145
- function cacheControlFor(cache) {
146
- if (!cache)
147
- return "no-store";
148
- if ((cache.maxAge ?? -1) === 0)
149
- return "no-store";
150
- const parts = [];
151
- if (cache.isPrivate)
152
- parts.push("private");
153
- if (cache.maxAge !== undefined)
154
- parts.push(`max-age=${cache.maxAge}`);
155
- if (cache.sMaxAge !== undefined)
156
- parts.push(`s-maxage=${cache.sMaxAge}`);
157
- if (cache.immutable)
158
- parts.push("immutable");
159
- return parts.length ? parts.join(", ") : "no-store";
160
- }
161
232
  export {
162
233
  makeAuthorizeHandler
163
234
  };
@@ -5,7 +5,13 @@ import { applyResponsePolicy } from "../cookies";
5
5
  import { authorizeDirectErrorResponse } from "../errors";
6
6
  function makeCallbackHandler(deps) {
7
7
  return async (c) => {
8
- const result = await handleCallback({ rawRequest: c.req.raw, cookies: c.get("cookies") }, {
8
+ const tenant = c.get("tenant");
9
+ const result = await handleCallback({
10
+ rawRequest: c.req.raw,
11
+ cookies: c.get("cookies"),
12
+ ...tenant ? { tenant } : {},
13
+ issuerUrl: c.get("issuerUrl")
14
+ }, {
9
15
  configStore: deps.configStore,
10
16
  sessionStore: deps.sessionStore,
11
17
  tokenStore: deps.tokenStore,
@@ -0,0 +1,64 @@
1
+ // src/http/handlers/end-session.ts
2
+ import { endSession } from "../../domain/logout";
3
+ import { authError } from "../../types/error";
4
+ import { isErr } from "../../types/result";
5
+ import { applyResponsePolicy } from "../cookies";
6
+ import { tokenEndpointErrorResponse } from "../errors";
7
+ import { endSessionParamsSchema } from "../schemas/end-session";
8
+ function makeEndSessionHandler(deps) {
9
+ return async (c) => {
10
+ const tenant = c.get("tenant");
11
+ if (!tenant) {
12
+ return tokenEndpointErrorResponse(authError.invalidRequest("tenant unresolved"));
13
+ }
14
+ let raw;
15
+ const httpMethod = c.req.method.toUpperCase();
16
+ if (httpMethod === "GET") {
17
+ raw = Object.fromEntries(new URL(c.req.url).searchParams.entries());
18
+ } else if (httpMethod === "POST") {
19
+ const ct = (c.req.header("content-type") ?? "").toLowerCase();
20
+ if (!ct.startsWith("application/x-www-form-urlencoded")) {
21
+ return tokenEndpointErrorResponse(authError.invalidRequest("POST /end_session requires application/x-www-form-urlencoded body"));
22
+ }
23
+ raw = Object.fromEntries(new URLSearchParams(await c.req.raw.text()));
24
+ } else {
25
+ return tokenEndpointErrorResponse(authError.invalidRequest(`unsupported method "${httpMethod}"`));
26
+ }
27
+ const parsed = endSessionParamsSchema.safeParse(raw);
28
+ if (!parsed.success) {
29
+ return tokenEndpointErrorResponse(authError.invalidRequest(parsed.error.issues[0]?.message ?? "invalid request"));
30
+ }
31
+ const q = parsed.data;
32
+ const res = await endSession({
33
+ ...q.id_token_hint !== undefined ? { idTokenHint: q.id_token_hint } : {},
34
+ ...q.client_id !== undefined ? { clientId: q.client_id } : {},
35
+ ...q.post_logout_redirect_uri !== undefined ? { postLogoutRedirectUri: q.post_logout_redirect_uri } : {},
36
+ ...q.state !== undefined ? { state: q.state } : {}
37
+ }, tenant, {
38
+ configStore: deps.configStore,
39
+ tokenStore: deps.tokenStore,
40
+ keyStore: deps.keyStore,
41
+ ...deps.auditLog ? { auditLog: deps.auditLog } : {},
42
+ issuerUrl: c.get("issuerUrl"),
43
+ clock: deps.clock
44
+ });
45
+ if (isErr(res))
46
+ return tokenEndpointErrorResponse(res.error);
47
+ if (res.value.kind === "redirect") {
48
+ return applyResponsePolicy(new Response(null, {
49
+ status: 302,
50
+ headers: { location: res.value.url }
51
+ }), { setCookies: [], cookieDefaults: deps.cookieDefaults });
52
+ }
53
+ return new Response("Logged out.", {
54
+ status: 200,
55
+ headers: {
56
+ "content-type": "text/plain; charset=utf-8",
57
+ "cache-control": "no-store"
58
+ }
59
+ });
60
+ };
61
+ }
62
+ export {
63
+ makeEndSessionHandler
64
+ };
@@ -2,9 +2,12 @@
2
2
  import { buildDiscoveryDocument, buildJwks } from "../../domain/discovery";
3
3
  import { isErr } from "../../types/result";
4
4
  import { tokenEndpointErrorResponse } from "../errors";
5
- function makeDiscoveryHandler(_deps) {
5
+ function makeDiscoveryHandler(deps) {
6
6
  return async (c) => {
7
- const doc = buildDiscoveryDocument({ issuerUrl: c.get("issuerUrl") });
7
+ const doc = buildDiscoveryDocument({
8
+ issuerUrl: c.get("issuerUrl"),
9
+ ...deps.customScopeClaims !== undefined ? { customScopeClaims: deps.customScopeClaims } : {}
10
+ });
8
11
  return new Response(JSON.stringify(doc), {
9
12
  status: 200,
10
13
  headers: {
@@ -29,6 +32,6 @@ function makeJwksHandler(deps) {
29
32
  };
30
33
  }
31
34
  export {
32
- makeJwksHandler,
33
- makeDiscoveryHandler
35
+ makeDiscoveryHandler,
36
+ makeJwksHandler
34
37
  };
@@ -1,8 +1,11 @@
1
1
  // src/http/handlers/method-route.ts
2
- import { handleMethodRoute } from "../../domain/method-route";
2
+ import {
3
+ handleMethodRoute,
4
+ handlePublicMethodRoute
5
+ } from "../../domain/method-route";
3
6
  import { authError } from "../../types/error";
4
7
  import { isErr } from "../../types/result";
5
- import { applyResponsePolicy } from "../cookies";
8
+ import { applyResponsePolicy, cacheControlHeader } from "../cookies";
6
9
  import { authorizeDirectErrorResponse } from "../errors";
7
10
  function makeMethodRouteHandler(deps) {
8
11
  return async (c) => {
@@ -10,6 +13,48 @@ function makeMethodRouteHandler(deps) {
10
13
  if (!tenant) {
11
14
  return authorizeDirectErrorResponse(authError.invalidRequest("tenant unresolved"));
12
15
  }
16
+ {
17
+ const u = new URL(c.req.url);
18
+ const segs = u.pathname.split("/").filter(Boolean);
19
+ if (segs.length >= 3 && segs[0] === "m") {
20
+ const methodId = segs[1];
21
+ const subPath = "/" + segs.slice(2).join("/");
22
+ const httpMethod = c.req.method.toUpperCase() === "POST" ? "POST" : "GET";
23
+ const routeKey = `${httpMethod} ${subPath}`;
24
+ const resolved = await deps.methodCache.resolve(tenant.config, methodId);
25
+ if (!isErr(resolved) && resolved.value.publicRoutes?.includes(routeKey)) {
26
+ const issuerUrl = c.get("issuerUrl");
27
+ const callbackHost = deps.callbackHostFor?.(tenant.id) ?? new URL(issuerUrl).host;
28
+ const callbackUrl = `${new URL(issuerUrl).protocol}//${callbackHost}/cb/${methodId}`;
29
+ const pub = await handlePublicMethodRoute({
30
+ rawRequest: c.req.raw,
31
+ tenant,
32
+ method: resolved.value,
33
+ route: routeKey,
34
+ subPath,
35
+ dispatch: { state: "", callbackUrl, issuerUrl }
36
+ }, {
37
+ sessionStore: deps.sessionStore,
38
+ tokenStore: deps.tokenStore,
39
+ clock: deps.clock,
40
+ ...deps.auditLog ? { auditLog: deps.auditLog } : {},
41
+ ...deps.onLogout ? { onLogout: deps.onLogout } : {}
42
+ });
43
+ if (isErr(pub))
44
+ return authorizeDirectErrorResponse(pub.error);
45
+ if (pub.value.kind === "denied") {
46
+ return applyResponsePolicy(new Response(`access_denied: ${pub.value.reason}`, {
47
+ status: 403,
48
+ headers: { "content-type": "text/plain" }
49
+ }), { cookieDefaults: deps.cookieDefaults });
50
+ }
51
+ return applyResponsePolicy(pub.value.response, {
52
+ cacheControl: cacheControlHeader(pub.value.cache),
53
+ cookieDefaults: deps.cookieDefaults
54
+ });
55
+ }
56
+ }
57
+ }
13
58
  const cookies = c.get("cookies");
14
59
  const flowId = cookies.get("idp.flow");
15
60
  if (!flowId) {
@@ -0,0 +1,53 @@
1
+ // src/http/handlers/par.ts
2
+ import { resolveClientCredentials } from "../../domain/client-auth";
3
+ import { pushAuthorizationRequest } from "../../domain/par";
4
+ import { authError } from "../../types/error";
5
+ import { isErr } from "../../types/result";
6
+ import { tokenEndpointErrorResponse } from "../errors";
7
+ import { parBodySchema } from "../schemas/par";
8
+ function makeParHandler(deps) {
9
+ return async (c) => {
10
+ const tenant = c.get("tenant");
11
+ if (!tenant) {
12
+ return tokenEndpointErrorResponse(authError.invalidRequest("tenant unresolved"));
13
+ }
14
+ const ct = (c.req.header("content-type") ?? "").toLowerCase();
15
+ if (!ct.startsWith("application/x-www-form-urlencoded")) {
16
+ return tokenEndpointErrorResponse(authError.invalidRequest("body must be application/x-www-form-urlencoded"));
17
+ }
18
+ const raw = Object.fromEntries(new URLSearchParams(await c.req.raw.text()).entries());
19
+ const parsed = parBodySchema.safeParse(raw);
20
+ if (!parsed.success) {
21
+ return tokenEndpointErrorResponse(authError.invalidRequest(parsed.error.issues[0]?.message ?? "invalid request"));
22
+ }
23
+ const creds = resolveClientCredentials({
24
+ authorizationHeader: c.req.header("authorization") ?? null,
25
+ bodyClientId: parsed.data.client_id,
26
+ bodyClientSecret: parsed.data.client_secret
27
+ });
28
+ const clientId = creds?.clientId ?? parsed.data.client_id;
29
+ const { client_secret: _strip, ...paramsForAuthorize } = parsed.data;
30
+ const res = await pushAuthorizationRequest({
31
+ clientId,
32
+ ...creds?.clientSecret !== undefined ? { clientSecret: creds.clientSecret } : {},
33
+ params: paramsForAuthorize
34
+ }, tenant, {
35
+ configStore: deps.configStore,
36
+ sessionStore: deps.sessionStore,
37
+ ...deps.auditLog ? { auditLog: deps.auditLog } : {},
38
+ clock: deps.clock
39
+ });
40
+ if (isErr(res))
41
+ return tokenEndpointErrorResponse(res.error);
42
+ return new Response(JSON.stringify(res.value), {
43
+ status: 201,
44
+ headers: {
45
+ "content-type": "application/json",
46
+ "cache-control": "no-store"
47
+ }
48
+ });
49
+ };
50
+ }
51
+ export {
52
+ makeParHandler
53
+ };
@@ -0,0 +1,46 @@
1
+ // src/http/handlers/register.ts
2
+ import { registerNewClient } from "../../domain/register";
3
+ import { authError } from "../../types/error";
4
+ import { isErr } from "../../types/result";
5
+ import { tokenEndpointErrorResponse } from "../errors";
6
+ import { registerBodySchema } from "../schemas/register";
7
+ function makeRegisterHandler(deps) {
8
+ return async (c) => {
9
+ const tenant = c.get("tenant");
10
+ if (!tenant) {
11
+ return tokenEndpointErrorResponse(authError.invalidRequest("tenant unresolved"));
12
+ }
13
+ const ct = (c.req.header("content-type") ?? "").toLowerCase();
14
+ if (!ct.startsWith("application/json")) {
15
+ return tokenEndpointErrorResponse(authError.invalidRequest("body must be application/json"));
16
+ }
17
+ let raw;
18
+ try {
19
+ raw = await c.req.raw.json();
20
+ } catch (e) {
21
+ const reason = e instanceof Error ? e.message : String(e);
22
+ return tokenEndpointErrorResponse(authError.invalidRequest(`malformed JSON: ${reason}`));
23
+ }
24
+ const parsed = registerBodySchema.safeParse(raw);
25
+ if (!parsed.success) {
26
+ return tokenEndpointErrorResponse(authError.invalidRequest(parsed.error.issues[0]?.message ?? "invalid request"));
27
+ }
28
+ const request = parsed.data;
29
+ const res = await registerNewClient(request, tenant, {
30
+ ...deps.registerClient ? { registerClient: deps.registerClient } : {},
31
+ clock: deps.clock
32
+ });
33
+ if (isErr(res))
34
+ return tokenEndpointErrorResponse(res.error);
35
+ return new Response(JSON.stringify(res.value), {
36
+ status: 201,
37
+ headers: {
38
+ "content-type": "application/json",
39
+ "cache-control": "no-store"
40
+ }
41
+ });
42
+ };
43
+ }
44
+ export {
45
+ makeRegisterHandler
46
+ };
@@ -93,6 +93,6 @@ async function readForm(req) {
93
93
  return new URLSearchParams(await req.text());
94
94
  }
95
95
  export {
96
- makeRevokeHandler,
97
- makeIntrospectHandler
96
+ makeIntrospectHandler,
97
+ makeRevokeHandler
98
98
  };
@@ -1,5 +1,6 @@
1
1
  // src/http/handlers/token.ts
2
2
  import { clientCredentialsGrant } from "../../domain/client-credentials";
3
+ import { canonicalHtu, verifyDpopProof } from "../../domain/dpop";
3
4
  import { exchangeCode } from "../../domain/token";
4
5
  import { exchangeToken } from "../../domain/token-exchange";
5
6
  import { refreshTokens } from "../../domain/refresh";
@@ -19,6 +20,29 @@ function makeTokenHandler(deps) {
19
20
  body.set("client_id", basic.id);
20
21
  body.set("client_secret", basic.secret);
21
22
  }
23
+ const dpopHeader = c.req.header("dpop") ?? null;
24
+ let dpopJkt;
25
+ if (dpopHeader) {
26
+ const dpopRes = await verifyDpopProof({
27
+ proofJwt: dpopHeader,
28
+ htu: canonicalHtu(c.req.url),
29
+ htm: "POST",
30
+ nowSec: Math.floor(deps.clock() / 1000)
31
+ }, { tokenStore: deps.tokenStore });
32
+ if (isErr(dpopRes)) {
33
+ const errVal = dpopRes.error;
34
+ if (errVal.code === "invalid_dpop_proof" && errVal.replaySignal && deps.auditLog) {
35
+ await deps.auditLog.log({
36
+ kind: "dpop_replay_detected",
37
+ tenantId: null,
38
+ jtiPrefix: errVal.replaySignal.jti.slice(0, 16),
39
+ timestamp: deps.clock()
40
+ });
41
+ }
42
+ return tokenEndpointErrorResponse(errVal);
43
+ }
44
+ dpopJkt = dpopRes.value.jkt;
45
+ }
22
46
  const parsed = tokenRequestSchema.safeParse(Object.fromEntries(body.entries()));
23
47
  if (!parsed.success) {
24
48
  const first = parsed.error.issues[0];
@@ -35,7 +59,8 @@ function makeTokenHandler(deps) {
35
59
  redirectUri: req.redirect_uri,
36
60
  clientId: req.client_id,
37
61
  ...req.client_secret !== undefined ? { clientSecret: req.client_secret } : {},
38
- ...req.code_verifier !== undefined ? { codeVerifier: req.code_verifier } : {}
62
+ ...req.code_verifier !== undefined ? { codeVerifier: req.code_verifier } : {},
63
+ ...dpopJkt !== undefined ? { dpopJkt } : {}
39
64
  }, {
40
65
  configStore: deps.configStore,
41
66
  tokenStore: deps.tokenStore,
@@ -44,7 +69,8 @@ function makeTokenHandler(deps) {
44
69
  success: deps.success,
45
70
  ...deps.persistUpstreamTokens ? { persistUpstreamTokens: deps.persistUpstreamTokens } : {},
46
71
  issuerUrl: c.get("issuerUrl"),
47
- clock: deps.clock
72
+ clock: deps.clock,
73
+ ...deps.customScopeClaims !== undefined ? { customScopeClaims: deps.customScopeClaims } : {}
48
74
  });
49
75
  if (isErr(result))
50
76
  return tokenEndpointErrorResponse(result.error);
@@ -75,7 +101,8 @@ function makeTokenHandler(deps) {
75
101
  success: deps.success,
76
102
  ...deps.persistUpstreamTokens ? { persistUpstreamTokens: deps.persistUpstreamTokens } : {},
77
103
  issuerUrl: c.get("issuerUrl"),
78
- clock: deps.clock
104
+ clock: deps.clock,
105
+ ...deps.customScopeClaims !== undefined ? { customScopeClaims: deps.customScopeClaims } : {}
79
106
  }, tenantRes.value);
80
107
  if (isErr(result))
81
108
  return tokenEndpointErrorResponse(result.error);
@@ -87,14 +114,16 @@ function makeTokenHandler(deps) {
87
114
  refreshToken: req.refresh_token,
88
115
  ...req.scope !== undefined ? { scope: req.scope } : {},
89
116
  ...req.client_id !== undefined ? { clientId: req.client_id } : {},
90
- ...req.client_secret !== undefined ? { clientSecret: req.client_secret } : {}
117
+ ...req.client_secret !== undefined ? { clientSecret: req.client_secret } : {},
118
+ ...dpopJkt !== undefined ? { dpopJkt } : {}
91
119
  }, {
92
120
  configStore: deps.configStore,
93
121
  tokenStore: deps.tokenStore,
94
122
  keyStore: deps.keyStore,
95
123
  ...deps.auditLog ? { auditLog: deps.auditLog } : {},
96
124
  issuerUrl: c.get("issuerUrl"),
97
- clock: deps.clock
125
+ clock: deps.clock,
126
+ ...deps.customScopeClaims !== undefined ? { customScopeClaims: deps.customScopeClaims } : {}
98
127
  });
99
128
  if (isErr(refreshResult))
100
129
  return tokenEndpointErrorResponse(refreshResult.error);
@@ -117,7 +146,8 @@ function makeTokenHandler(deps) {
117
146
  ...deps.auditLog ? { auditLog: deps.auditLog } : {},
118
147
  ...deps.exchangeAudience ? { exchangeAudience: deps.exchangeAudience } : {},
119
148
  issuerUrl: c.get("issuerUrl"),
120
- clock: deps.clock
149
+ clock: deps.clock,
150
+ ...deps.customScopeClaims !== undefined ? { customScopeClaims: deps.customScopeClaims } : {}
121
151
  });
122
152
  if (isErr(exchangeResult)) {
123
153
  return tokenEndpointErrorResponse(exchangeResult.error);
@@ -1,38 +1,32 @@
1
1
  // src/http/handlers/userinfo.ts
2
+ import { canonicalHtu } from "../../domain/dpop";
2
3
  import { userinfo } from "../../domain/userinfo";
3
4
  import { isErr } from "../../types/result";
4
5
  function makeUserinfoHandler(deps) {
5
6
  return async (c) => {
6
- const bearer = extractBearer(c.req.header("authorization") ?? null);
7
- if (!bearer) {
8
- const r = new Response(JSON.stringify({
9
- error: "invalid_token",
10
- error_description: "missing bearer token"
11
- }), {
12
- status: 401,
13
- headers: {
14
- "content-type": "application/json",
15
- "www-authenticate": 'Bearer realm="userinfo"'
16
- }
17
- });
18
- return r;
7
+ const auth = extractAuthorization(c.req.header("authorization") ?? null);
8
+ if (!auth) {
9
+ return wwwAuthenticateResponse("Bearer", "invalid_token", "missing bearer / DPoP token", 401);
19
10
  }
20
- const res = await userinfo(bearer, {
11
+ const dpopProof = c.req.header("dpop") ?? undefined;
12
+ const res = await userinfo({
13
+ accessToken: auth.token,
14
+ scheme: auth.scheme,
15
+ ...dpopProof !== undefined ? { dpopProof } : {},
16
+ htu: canonicalHtu(c.req.url),
17
+ htm: c.req.method.toUpperCase(),
18
+ nowSec: Math.floor(deps.clock() / 1000)
19
+ }, {
21
20
  keyStore: deps.keyStore,
22
- issuerUrl: c.get("issuerUrl")
21
+ tokenStore: deps.tokenStore,
22
+ issuerUrl: c.get("issuerUrl"),
23
+ ...deps.customScopeClaims !== undefined ? { customScopeClaims: deps.customScopeClaims } : {}
23
24
  });
24
25
  if (isErr(res)) {
25
- const err = res.error;
26
- return new Response(JSON.stringify({
27
- error: "invalid_token",
28
- error_description: err.description
29
- }), {
30
- status: 401,
31
- headers: {
32
- "content-type": "application/json",
33
- "www-authenticate": `Bearer error="invalid_token"`
34
- }
35
- });
26
+ const error = res.error;
27
+ const scheme = auth.scheme;
28
+ const wireError = error.code === "invalid_dpop_proof" ? "invalid_dpop_proof" : "invalid_token";
29
+ return wwwAuthenticateResponse(scheme, wireError, error.description, 401);
36
30
  }
37
31
  return new Response(JSON.stringify(res.value), {
38
32
  status: 200,
@@ -43,11 +37,23 @@ function makeUserinfoHandler(deps) {
43
37
  });
44
38
  };
45
39
  }
46
- function extractBearer(header) {
40
+ function extractAuthorization(header) {
47
41
  if (!header)
48
42
  return null;
49
- const m = /^Bearer\s+(.+)$/i.exec(header);
50
- return m?.[1] ?? null;
43
+ const m = /^(Bearer|DPoP)\s+(.+)$/i.exec(header);
44
+ if (!m || !m[1] || !m[2])
45
+ return null;
46
+ const scheme = m[1].toLowerCase() === "dpop" ? "DPoP" : "Bearer";
47
+ return { scheme, token: m[2] };
48
+ }
49
+ function wwwAuthenticateResponse(scheme, error, description, status) {
50
+ return new Response(JSON.stringify({ error, error_description: description }), {
51
+ status,
52
+ headers: {
53
+ "content-type": "application/json",
54
+ "www-authenticate": `${scheme} error="${error}"`
55
+ }
56
+ });
51
57
  }
52
58
  export {
53
59
  makeUserinfoHandler
@@ -27,12 +27,12 @@ function tenantMiddleware(deps) {
27
27
  const recovery = await runCallbackRecovery(c.req.raw, deps);
28
28
  c.set("recovery", recovery);
29
29
  if (recovery.kind !== "fresh-request") {
30
- const cfg2 = await deps.configStore.getTenantConfig(recovery.tenantId);
31
- if (isErr(cfg2)) {
32
- return tokenEndpointErrorResponse(cfg2.error);
30
+ const cfg = await deps.configStore.getTenantConfig(recovery.tenantId);
31
+ if (isErr(cfg)) {
32
+ return tokenEndpointErrorResponse(cfg.error);
33
33
  }
34
34
  const customCb = deps.buildCustomContext ? await deps.buildCustomContext(c.req.raw) : {};
35
- c.set("tenant", buildTenantContext(c.req.raw, recovery.tenantId, cfg2.value, customCb));
35
+ c.set("tenant", buildTenantContext(c.req.raw, recovery.tenantId, cfg.value, customCb));
36
36
  return next();
37
37
  }
38
38
  }
@@ -97,6 +97,6 @@ async function extractStateParam(req) {
97
97
  }
98
98
  }
99
99
  export {
100
- tenantMiddleware,
101
- bootstrapMiddleware
100
+ bootstrapMiddleware,
101
+ tenantMiddleware
102
102
  };