@atlasauth/emulator-core 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/dist/adopt.d.ts +50 -0
  2. package/dist/adopt.js +57 -0
  3. package/dist/adopt.js.map +1 -0
  4. package/dist/coverage.d.ts +15 -0
  5. package/dist/coverage.js +31 -0
  6. package/dist/coverage.js.map +1 -0
  7. package/dist/crypto-util.d.ts +10 -0
  8. package/dist/crypto-util.js +76 -0
  9. package/dist/crypto-util.js.map +1 -0
  10. package/dist/custom-html.d.ts +57 -0
  11. package/dist/custom-html.js +491 -0
  12. package/dist/custom-html.js.map +1 -0
  13. package/dist/engine.d.ts +34 -3
  14. package/dist/engine.js +57 -8
  15. package/dist/engine.js.map +1 -1
  16. package/dist/error-catalog.js +16 -0
  17. package/dist/error-catalog.js.map +1 -1
  18. package/dist/event-gate.d.ts +3 -0
  19. package/dist/event-gate.js +430 -0
  20. package/dist/event-gate.js.map +1 -0
  21. package/dist/generic.js +14 -2
  22. package/dist/generic.js.map +1 -1
  23. package/dist/handlers/access-signals.d.ts +48 -0
  24. package/dist/handlers/access-signals.js +76 -0
  25. package/dist/handlers/access-signals.js.map +1 -0
  26. package/dist/handlers/approvals.d.ts +2 -0
  27. package/dist/handlers/approvals.js +418 -0
  28. package/dist/handlers/approvals.js.map +1 -0
  29. package/dist/handlers/authz-data.d.ts +2 -0
  30. package/dist/handlers/authz-data.js +627 -0
  31. package/dist/handlers/authz-data.js.map +1 -0
  32. package/dist/handlers/authz-layers.d.ts +229 -0
  33. package/dist/handlers/authz-layers.js +786 -0
  34. package/dist/handlers/authz-layers.js.map +1 -0
  35. package/dist/handlers/authz-state.d.ts +80 -0
  36. package/dist/handlers/authz-state.js +175 -0
  37. package/dist/handlers/authz-state.js.map +1 -0
  38. package/dist/handlers/authz.d.ts +2 -0
  39. package/dist/handlers/authz.js +410 -0
  40. package/dist/handlers/authz.js.map +1 -0
  41. package/dist/handlers/billing.d.ts +100 -0
  42. package/dist/handlers/billing.js +877 -0
  43. package/dist/handlers/billing.js.map +1 -0
  44. package/dist/handlers/cedar-ext.d.ts +35 -0
  45. package/dist/handlers/cedar-ext.js +174 -0
  46. package/dist/handlers/cedar-ext.js.map +1 -0
  47. package/dist/handlers/cedar.d.ts +146 -0
  48. package/dist/handlers/cedar.js +689 -0
  49. package/dist/handlers/cedar.js.map +1 -0
  50. package/dist/handlers/connections.d.ts +5 -0
  51. package/dist/handlers/connections.js +561 -0
  52. package/dist/handlers/connections.js.map +1 -0
  53. package/dist/handlers/control.js +6 -1
  54. package/dist/handlers/control.js.map +1 -1
  55. package/dist/handlers/device.d.ts +47 -0
  56. package/dist/handlers/device.js +665 -0
  57. package/dist/handlers/device.js.map +1 -0
  58. package/dist/handlers/emu3-misc.d.ts +38 -0
  59. package/dist/handlers/emu3-misc.js +1056 -0
  60. package/dist/handlers/emu3-misc.js.map +1 -0
  61. package/dist/handlers/emu4-actions-access.d.ts +46 -0
  62. package/dist/handlers/emu4-actions-access.js +107 -0
  63. package/dist/handlers/emu4-actions-access.js.map +1 -0
  64. package/dist/handlers/emu4-actions-catalog-extra.d.ts +3 -0
  65. package/dist/handlers/emu4-actions-catalog-extra.js +204 -0
  66. package/dist/handlers/emu4-actions-catalog-extra.js.map +1 -0
  67. package/dist/handlers/emu4-actions-catalog.d.ts +3 -0
  68. package/dist/handlers/emu4-actions-catalog.js +143 -0
  69. package/dist/handlers/emu4-actions-catalog.js.map +1 -0
  70. package/dist/handlers/emu4-actions-def.d.ts +50 -0
  71. package/dist/handlers/emu4-actions-def.js +250 -0
  72. package/dist/handlers/emu4-actions-def.js.map +1 -0
  73. package/dist/handlers/emu4-actions-sandbox.d.ts +36 -0
  74. package/dist/handlers/emu4-actions-sandbox.js +318 -0
  75. package/dist/handlers/emu4-actions-sandbox.js.map +1 -0
  76. package/dist/handlers/emu4-actions-typegen.d.ts +5 -0
  77. package/dist/handlers/emu4-actions-typegen.js +73 -0
  78. package/dist/handlers/emu4-actions-typegen.js.map +1 -0
  79. package/dist/handlers/emu4-actions.d.ts +2 -0
  80. package/dist/handlers/emu4-actions.js +506 -0
  81. package/dist/handlers/emu4-actions.js.map +1 -0
  82. package/dist/handlers/emu4-authz-trust.d.ts +40 -0
  83. package/dist/handlers/emu4-authz-trust.js +488 -0
  84. package/dist/handlers/emu4-authz-trust.js.map +1 -0
  85. package/dist/handlers/emu4-authz.d.ts +2 -0
  86. package/dist/handlers/emu4-authz.js +752 -0
  87. package/dist/handlers/emu4-authz.js.map +1 -0
  88. package/dist/handlers/emu4-billing-core.d.ts +23 -0
  89. package/dist/handlers/emu4-billing-core.js +22 -0
  90. package/dist/handlers/emu4-billing-core.js.map +1 -0
  91. package/dist/handlers/emu4-billing-x.d.ts +2 -0
  92. package/dist/handlers/emu4-billing-x.js +489 -0
  93. package/dist/handlers/emu4-billing-x.js.map +1 -0
  94. package/dist/handlers/emu4-billing.d.ts +14 -0
  95. package/dist/handlers/emu4-billing.js +846 -0
  96. package/dist/handlers/emu4-billing.js.map +1 -0
  97. package/dist/handlers/emu4-clones.d.ts +2 -0
  98. package/dist/handlers/emu4-clones.js +322 -0
  99. package/dist/handlers/emu4-clones.js.map +1 -0
  100. package/dist/handlers/emu4-compliance.d.ts +8 -0
  101. package/dist/handlers/emu4-compliance.js +39 -0
  102. package/dist/handlers/emu4-compliance.js.map +1 -0
  103. package/dist/handlers/emu4-config.d.ts +14 -0
  104. package/dist/handlers/emu4-config.js +148 -0
  105. package/dist/handlers/emu4-config.js.map +1 -0
  106. package/dist/handlers/emu4-cookies.d.ts +8 -0
  107. package/dist/handlers/emu4-cookies.js +248 -0
  108. package/dist/handlers/emu4-cookies.js.map +1 -0
  109. package/dist/handlers/emu4-erasure.d.ts +2 -0
  110. package/dist/handlers/emu4-erasure.js +587 -0
  111. package/dist/handlers/emu4-erasure.js.map +1 -0
  112. package/dist/handlers/emu4-federation.d.ts +8 -0
  113. package/dist/handlers/emu4-federation.js +18 -0
  114. package/dist/handlers/emu4-federation.js.map +1 -0
  115. package/dist/handlers/emu4-journey-math.d.ts +42 -0
  116. package/dist/handlers/emu4-journey-math.js +99 -0
  117. package/dist/handlers/emu4-journey-math.js.map +1 -0
  118. package/dist/handlers/emu4-messaging.d.ts +21 -0
  119. package/dist/handlers/emu4-messaging.js +595 -0
  120. package/dist/handlers/emu4-messaging.js.map +1 -0
  121. package/dist/handlers/emu4-minors.d.ts +7 -0
  122. package/dist/handlers/emu4-minors.js +408 -0
  123. package/dist/handlers/emu4-minors.js.map +1 -0
  124. package/dist/handlers/emu4-oauth-audit.d.ts +5 -0
  125. package/dist/handlers/emu4-oauth-audit.js +17 -0
  126. package/dist/handlers/emu4-oauth-audit.js.map +1 -0
  127. package/dist/handlers/emu4-oauth-auth.d.ts +83 -0
  128. package/dist/handlers/emu4-oauth-auth.js +363 -0
  129. package/dist/handlers/emu4-oauth-auth.js.map +1 -0
  130. package/dist/handlers/emu4-oauth-delivery.d.ts +68 -0
  131. package/dist/handlers/emu4-oauth-delivery.js +207 -0
  132. package/dist/handlers/emu4-oauth-delivery.js.map +1 -0
  133. package/dist/handlers/emu4-oauth-keys.d.ts +11 -0
  134. package/dist/handlers/emu4-oauth-keys.js +268 -0
  135. package/dist/handlers/emu4-oauth-keys.js.map +1 -0
  136. package/dist/handlers/emu4-oauth-pub.d.ts +36 -0
  137. package/dist/handlers/emu4-oauth-pub.js +420 -0
  138. package/dist/handlers/emu4-oauth-pub.js.map +1 -0
  139. package/dist/handlers/emu4-oauth.d.ts +10 -0
  140. package/dist/handlers/emu4-oauth.js +23 -0
  141. package/dist/handlers/emu4-oauth.js.map +1 -0
  142. package/dist/handlers/emu4-policies.d.ts +2 -0
  143. package/dist/handlers/emu4-policies.js +408 -0
  144. package/dist/handlers/emu4-policies.js.map +1 -0
  145. package/dist/handlers/emu4-region.d.ts +2 -0
  146. package/dist/handlers/emu4-region.js +189 -0
  147. package/dist/handlers/emu4-region.js.map +1 -0
  148. package/dist/handlers/emu4-saml.d.ts +12 -0
  149. package/dist/handlers/emu4-saml.js +532 -0
  150. package/dist/handlers/emu4-saml.js.map +1 -0
  151. package/dist/handlers/emu4-shared.d.ts +44 -0
  152. package/dist/handlers/emu4-shared.js +90 -0
  153. package/dist/handlers/emu4-shared.js.map +1 -0
  154. package/dist/handlers/emu4-snapshots.d.ts +38 -0
  155. package/dist/handlers/emu4-snapshots.js +231 -0
  156. package/dist/handlers/emu4-snapshots.js.map +1 -0
  157. package/dist/handlers/emu4-sns.d.ts +9 -0
  158. package/dist/handlers/emu4-sns.js +21 -0
  159. package/dist/handlers/emu4-sns.js.map +1 -0
  160. package/dist/handlers/emu4-trust-binding.d.ts +27 -0
  161. package/dist/handlers/emu4-trust-binding.js +344 -0
  162. package/dist/handlers/emu4-trust-binding.js.map +1 -0
  163. package/dist/handlers/emu4-trust.d.ts +35 -0
  164. package/dist/handlers/emu4-trust.js +543 -0
  165. package/dist/handlers/emu4-trust.js.map +1 -0
  166. package/dist/handlers/federation.d.ts +2 -0
  167. package/dist/handlers/federation.js +481 -0
  168. package/dist/handlers/federation.js.map +1 -0
  169. package/dist/handlers/fga-engine.d.ts +78 -0
  170. package/dist/handlers/fga-engine.js +663 -0
  171. package/dist/handlers/fga-engine.js.map +1 -0
  172. package/dist/handlers/governance.d.ts +2 -0
  173. package/dist/handlers/governance.js +460 -0
  174. package/dist/handlers/governance.js.map +1 -0
  175. package/dist/handlers/hooks.js +17 -1
  176. package/dist/handlers/hooks.js.map +1 -1
  177. package/dist/handlers/index.d.ts +5 -1
  178. package/dist/handlers/index.js +45 -3
  179. package/dist/handlers/index.js.map +1 -1
  180. package/dist/handlers/keys.js +6 -1
  181. package/dist/handlers/keys.js.map +1 -1
  182. package/dist/handlers/machines.js +61 -8
  183. package/dist/handlers/machines.js.map +1 -1
  184. package/dist/handlers/messaging.d.ts +6 -0
  185. package/dist/handlers/messaging.js +893 -0
  186. package/dist/handlers/messaging.js.map +1 -0
  187. package/dist/handlers/oauth-adv.d.ts +191 -0
  188. package/dist/handlers/oauth-adv.js +1305 -0
  189. package/dist/handlers/oauth-adv.js.map +1 -0
  190. package/dist/handlers/oauth-flows.d.ts +52 -0
  191. package/dist/handlers/oauth-flows.js +480 -0
  192. package/dist/handlers/oauth-flows.js.map +1 -0
  193. package/dist/handlers/oauth.d.ts +96 -0
  194. package/dist/handlers/oauth.js +793 -0
  195. package/dist/handlers/oauth.js.map +1 -0
  196. package/dist/handlers/orgs.js +6 -0
  197. package/dist/handlers/orgs.js.map +1 -1
  198. package/dist/handlers/passkeys.d.ts +3 -0
  199. package/dist/handlers/passkeys.js +293 -0
  200. package/dist/handlers/passkeys.js.map +1 -0
  201. package/dist/handlers/platform.d.ts +20 -0
  202. package/dist/handlers/platform.js +472 -0
  203. package/dist/handlers/platform.js.map +1 -0
  204. package/dist/handlers/round2.d.ts +8 -0
  205. package/dist/handlers/round2.js +372 -0
  206. package/dist/handlers/round2.js.map +1 -0
  207. package/dist/handlers/scim.d.ts +4 -0
  208. package/dist/handlers/scim.js +595 -0
  209. package/dist/handlers/scim.js.map +1 -0
  210. package/dist/handlers/sessions.d.ts +9 -3
  211. package/dist/handlers/sessions.js +76 -38
  212. package/dist/handlers/sessions.js.map +1 -1
  213. package/dist/handlers/support.d.ts +20 -0
  214. package/dist/handlers/support.js +62 -0
  215. package/dist/handlers/support.js.map +1 -0
  216. package/dist/handlers/users.js +7 -0
  217. package/dist/handlers/users.js.map +1 -1
  218. package/dist/handlers/workflows.d.ts +2 -0
  219. package/dist/handlers/workflows.js +428 -0
  220. package/dist/handlers/workflows.js.map +1 -0
  221. package/dist/index.d.ts +8 -0
  222. package/dist/index.js +23 -1
  223. package/dist/index.js.map +1 -1
  224. package/dist/jwe.d.ts +18 -0
  225. package/dist/jwe.js +101 -0
  226. package/dist/jwe.js.map +1 -0
  227. package/dist/jws.d.ts +41 -0
  228. package/dist/jws.js +135 -0
  229. package/dist/jws.js.map +1 -0
  230. package/dist/messages.d.ts +7 -0
  231. package/dist/messages.js +8 -4
  232. package/dist/messages.js.map +1 -1
  233. package/dist/messaging-callbacks.d.ts +100 -0
  234. package/dist/messaging-callbacks.js +767 -0
  235. package/dist/messaging-callbacks.js.map +1 -0
  236. package/dist/openapi.js +41 -1
  237. package/dist/openapi.js.map +1 -1
  238. package/dist/parity-bin.js +3 -1
  239. package/dist/parity-bin.js.map +1 -1
  240. package/dist/parity-emu3-authz.d.ts +3 -0
  241. package/dist/parity-emu3-authz.js +61 -0
  242. package/dist/parity-emu3-authz.js.map +1 -0
  243. package/dist/parity-emu3-billing.d.ts +3 -0
  244. package/dist/parity-emu3-billing.js +25 -0
  245. package/dist/parity-emu3-billing.js.map +1 -0
  246. package/dist/parity-emu3-messaging.d.ts +3 -0
  247. package/dist/parity-emu3-messaging.js +27 -0
  248. package/dist/parity-emu3-messaging.js.map +1 -0
  249. package/dist/parity-emu3-misc.d.ts +2 -0
  250. package/dist/parity-emu3-misc.js +63 -0
  251. package/dist/parity-emu3-misc.js.map +1 -0
  252. package/dist/parity-emu3-oauth.d.ts +2 -0
  253. package/dist/parity-emu3-oauth.js +39 -0
  254. package/dist/parity-emu3-oauth.js.map +1 -0
  255. package/dist/parity-emu4-actions.d.ts +2 -0
  256. package/dist/parity-emu4-actions.js +38 -0
  257. package/dist/parity-emu4-actions.js.map +1 -0
  258. package/dist/parity-emu4-authz.d.ts +3 -0
  259. package/dist/parity-emu4-authz.js +68 -0
  260. package/dist/parity-emu4-authz.js.map +1 -0
  261. package/dist/parity-emu4-billing.d.ts +6 -0
  262. package/dist/parity-emu4-billing.js +44 -0
  263. package/dist/parity-emu4-billing.js.map +1 -0
  264. package/dist/parity-emu4-compliance.d.ts +2 -0
  265. package/dist/parity-emu4-compliance.js +92 -0
  266. package/dist/parity-emu4-compliance.js.map +1 -0
  267. package/dist/parity-emu4-federation.d.ts +2 -0
  268. package/dist/parity-emu4-federation.js +33 -0
  269. package/dist/parity-emu4-federation.js.map +1 -0
  270. package/dist/parity-emu4-messaging.d.ts +3 -0
  271. package/dist/parity-emu4-messaging.js +42 -0
  272. package/dist/parity-emu4-messaging.js.map +1 -0
  273. package/dist/parity-emu4-oauth.d.ts +2 -0
  274. package/dist/parity-emu4-oauth.js +55 -0
  275. package/dist/parity-emu4-oauth.js.map +1 -0
  276. package/dist/parity-emu4-trust.d.ts +2 -0
  277. package/dist/parity-emu4-trust.js +41 -0
  278. package/dist/parity-emu4-trust.js.map +1 -0
  279. package/dist/parity-routes.d.ts +14 -0
  280. package/dist/parity-routes.js +439 -0
  281. package/dist/parity-routes.js.map +1 -0
  282. package/dist/parity.d.ts +10 -3
  283. package/dist/parity.js +14 -5
  284. package/dist/parity.js.map +1 -1
  285. package/dist/sns-cert.d.ts +9 -0
  286. package/dist/sns-cert.js +101 -0
  287. package/dist/sns-cert.js.map +1 -0
  288. package/dist/tokens.d.ts +85 -3
  289. package/dist/tokens.js +196 -24
  290. package/dist/tokens.js.map +1 -1
  291. package/dist/webauthn-virtual.d.ts +70 -0
  292. package/dist/webauthn-virtual.js +186 -0
  293. package/dist/webauthn-virtual.js.map +1 -0
  294. package/dist/webhook-ext.d.ts +16 -0
  295. package/dist/webhook-ext.js +218 -0
  296. package/dist/webhook-ext.js.map +1 -0
  297. package/dist/webhooks.d.ts +3 -0
  298. package/dist/webhooks.js +37 -3
  299. package/dist/webhooks.js.map +1 -1
  300. package/package.json +2 -2
@@ -0,0 +1,793 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.userClaims = exports.splitScope = exports.fail = exports.resetOAuth = exports.st = void 0;
4
+ exports.discoveryDocument = discoveryDocument;
5
+ exports.authenticateClient = authenticateClient;
6
+ exports.mintAccess = mintAccess;
7
+ exports.issueTokens = issueTokens;
8
+ exports.idTokenFor = idTokenFor;
9
+ exports.revokeGrant = revokeGrant;
10
+ exports.checkResourceToken = checkResourceToken;
11
+ exports.issueCode = issueCode;
12
+ exports.oauthHandlers = oauthHandlers;
13
+ const crypto_util_1 = require("../crypto-util");
14
+ const errors_1 = require("../errors");
15
+ const openapi_1 = require("../openapi");
16
+ const store_1 = require("../store");
17
+ const jws_1 = require("../jws");
18
+ const oauth_flows_1 = require("./oauth-flows");
19
+ const oauth_adv_1 = require("./oauth-adv");
20
+ const util_1 = require("./util");
21
+ const emu4_oauth_auth_1 = require("./emu4-oauth-auth");
22
+ const emu4_oauth_pub_1 = require("./emu4-oauth-pub");
23
+ /**
24
+ * The OAuth 2.0 / OpenID Connect provider: client registry (`/v1/oauth_clients`), discovery, authorize (code + PKCE
25
+ * S256), token (authorization_code, refresh_token with rotation and reuse detection, client_credentials, token
26
+ * exchange RFC 8693), revoke (RFC 7009), introspect (RFC 7662), userinfo and the JWKS. Tokens are real signed JWTs
27
+ * (access and ID tokens) verifiable against `/.well-known/jwks.json`. Errors follow RFC 6749 section 5.2.
28
+ */
29
+ const CODE_TTL_MS = 10 * 60_000;
30
+ const ACCESS_TTL_S = 3600;
31
+ const GRACE_MS = 30_000;
32
+ const EXCHANGE = 'urn:ietf:params:oauth:grant-type:token-exchange';
33
+ const ACCESS_TYPE = 'urn:ietf:params:oauth:token-type:access_token';
34
+ const SCOPES = ['openid', 'profile', 'email', 'offline_access'];
35
+ const states = new WeakMap();
36
+ const st = (e) => {
37
+ let s = states.get(e);
38
+ if (!s)
39
+ states.set(e, (s = { codes: new Map(), grants: new Map(), refresh: new Map(), access: new Map(), autoUser: null }));
40
+ return s;
41
+ };
42
+ exports.st = st;
43
+ const resetOAuth = (e) => {
44
+ states.delete(e);
45
+ (0, oauth_flows_1.resetOAuthFlows)(e);
46
+ };
47
+ exports.resetOAuth = resetOAuth;
48
+ const fail = (status, error, description, extra = {}) => ({ status, body: { error, error_description: description, ...extra }, headers: { 'cache-control': 'no-store', pragma: 'no-cache' } });
49
+ exports.fail = fail;
50
+ function discoveryDocument(e) {
51
+ const iss = e.issuer;
52
+ return {
53
+ issuer: iss,
54
+ jwks_uri: `${iss}/.well-known/jwks.json`,
55
+ authorization_endpoint: `${iss}/oauth2/authorize`,
56
+ token_endpoint: `${iss}/oauth2/token`,
57
+ userinfo_endpoint: `${iss}/oauth2/userinfo`,
58
+ revocation_endpoint: `${iss}/oauth2/revoke`,
59
+ introspection_endpoint: `${iss}/oauth2/introspect`,
60
+ end_session_endpoint: `${iss}/oauth2/logout`,
61
+ backchannel_logout_supported: true,
62
+ backchannel_logout_session_supported: true,
63
+ frontchannel_logout_supported: true,
64
+ frontchannel_logout_session_supported: true,
65
+ grant_management_endpoint: `${iss}/oauth2/grants`,
66
+ grant_management_actions_supported: ['create', 'merge', 'replace'],
67
+ grant_management_action_required: false,
68
+ ...((0, oauth_adv_1.adv)(e).cfg.dynamic_registration ? { registration_endpoint: `${iss}/oauth2/register` } : {}),
69
+ response_types_supported: [...oauth_adv_1.RESPONSE_TYPES_SUPPORTED],
70
+ response_modes_supported: ['query', 'fragment', ...emu4_oauth_auth_1.EXT_RESPONSE_MODES],
71
+ authorization_signing_alg_values_supported: [...emu4_oauth_auth_1.OP_SIGNING_ALGS],
72
+ request_object_encryption_alg_values_supported: ['RSA-OAEP-256', 'ECDH-ES'],
73
+ request_object_encryption_enc_values_supported: ['A128GCM', 'A256GCM', 'A128CBC-HS256', 'A256CBC-HS512'],
74
+ request_parameter_supported: true,
75
+ request_uri_parameter_supported: true,
76
+ require_request_uri_registration: true,
77
+ request_object_signing_alg_values_supported: [...oauth_adv_1.REQUEST_OBJECT_ALGS],
78
+ authorization_details_types_supported: (0, oauth_adv_1.adv)(e).cfg.authorization_details_types,
79
+ grant_types_supported: ['authorization_code', 'implicit', 'refresh_token', 'client_credentials', EXCHANGE, 'urn:ietf:params:oauth:grant-type:device_code', 'urn:openid:params:grant-type:ciba', ...((0, oauth_adv_1.umaEnabled)(e) ? [oauth_adv_1.UMA_GRANT_TYPE] : [])],
80
+ subject_types_supported: ['public'],
81
+ id_token_signing_alg_values_supported: [...emu4_oauth_auth_1.OP_SIGNING_ALGS],
82
+ token_endpoint_auth_methods_supported: ['client_secret_basic', 'client_secret_post', 'private_key_jwt', 'none'],
83
+ token_endpoint_auth_signing_alg_values_supported: [...oauth_adv_1.REQUEST_OBJECT_ALGS],
84
+ code_challenge_methods_supported: ['S256'],
85
+ dpop_signing_alg_values_supported: ['ES256', 'ES384', 'RS256', 'PS256', 'EdDSA'],
86
+ tls_client_certificate_bound_access_tokens: true,
87
+ pushed_authorization_request_endpoint: `${iss}/oauth2/par`,
88
+ device_authorization_endpoint: `${iss}/oauth2/device_authorization`,
89
+ backchannel_authentication_endpoint: `${iss}/oauth2/backchannel_authentication`,
90
+ backchannel_token_delivery_modes_supported: ['poll', 'ping', 'push'],
91
+ backchannel_user_code_parameter_supported: false,
92
+ scopes_supported: [...SCOPES, ...oauth_adv_1.GRANT_MANAGEMENT_SCOPES],
93
+ claims_supported: ['sub', 'iss', 'aud', 'exp', 'iat', 'auth_time', 'nonce', 'email', 'email_verified', 'name', 'picture'],
94
+ authorization_response_iss_parameter_supported: true,
95
+ atlas_emulator: true,
96
+ };
97
+ }
98
+ const FLOW_FLAGS = ['require_consent', 'dpop_bound_access_tokens', 'tls_client_certificate_bound_access_tokens', 'require_pushed_authorization_requests'];
99
+ /** The client settings the consent, binding, PAR and CIBA flows read; validated here so a bad value never reaches a flow. */
100
+ function pickFlowFields(body, existing) {
101
+ const out = {};
102
+ for (const k of FLOW_FLAGS) {
103
+ if (body[k] === undefined)
104
+ continue;
105
+ if (typeof body[k] !== 'boolean')
106
+ throw new errors_1.EmulatorError('VALIDATION_FAILED', `${k} must be true or false.`, k);
107
+ out[k] = body[k];
108
+ }
109
+ if (body.backchannel_token_delivery_mode !== undefined) {
110
+ if (!['poll', 'ping', 'push'].includes(body.backchannel_token_delivery_mode))
111
+ throw new errors_1.EmulatorError('VALIDATION_FAILED', 'backchannel_token_delivery_mode must be poll, ping or push.', 'backchannel_token_delivery_mode');
112
+ out.backchannel_token_delivery_mode = body.backchannel_token_delivery_mode;
113
+ }
114
+ if (body.backchannel_client_notification_endpoint !== undefined)
115
+ out.backchannel_client_notification_endpoint = body.backchannel_client_notification_endpoint;
116
+ if ((out.backchannel_token_delivery_mode ?? existing?.backchannel_token_delivery_mode) !== undefined && ['ping', 'push'].includes(out.backchannel_token_delivery_mode ?? existing?.backchannel_token_delivery_mode) && !(out.backchannel_client_notification_endpoint ?? existing?.backchannel_client_notification_endpoint)) {
117
+ throw new errors_1.EmulatorError('VALIDATION_FAILED', 'ping and push delivery need backchannel_client_notification_endpoint.', 'backchannel_client_notification_endpoint');
118
+ }
119
+ // Key, request-object and response-type metadata, validated like the real registry.
120
+ const meta = (0, oauth_adv_1.parseClientMeta)(body);
121
+ if (!meta.ok)
122
+ throw new errors_1.EmulatorError('VALIDATION_FAILED', meta.message);
123
+ Object.assign(out, meta.value);
124
+ for (const k of ['backchannel_logout_uri', 'frontchannel_logout_uri'])
125
+ if (body[k] !== undefined)
126
+ out[k] = body[k];
127
+ if (body.claims_redirect_uris !== undefined)
128
+ out.claims_redirect_uris = body.claims_redirect_uris;
129
+ return out;
130
+ }
131
+ const clientShape = (e, c, secret) => {
132
+ const op = e.index.ops.find((o) => o.method === 'POST' && o.path === '/v1/oauth_clients');
133
+ const base = op?.successSchema ? (0, openapi_1.synthesize)(e.index, op.successSchema) : {};
134
+ return {
135
+ ...(base && typeof base === 'object' ? base : {}),
136
+ object: 'oauth_client',
137
+ id: c.id,
138
+ client_id: c.id,
139
+ name: c.name,
140
+ redirect_uris: c.redirect_uris,
141
+ allowed_scopes: c.allowed_scopes,
142
+ grant_types: c.grant_types,
143
+ token_endpoint_auth_method: c.auth_method,
144
+ first_party: c.first_party === true,
145
+ post_logout_redirect_uris: c.post_logout_redirect_uris ?? [],
146
+ require_consent: c.require_consent === true,
147
+ dpop_bound_access_tokens: c.dpop_bound_access_tokens === true,
148
+ tls_client_certificate_bound_access_tokens: c.tls_client_certificate_bound_access_tokens === true,
149
+ require_pushed_authorization_requests: c.require_pushed_authorization_requests === true,
150
+ backchannel_token_delivery_mode: c.backchannel_token_delivery_mode ?? 'poll',
151
+ backchannel_client_notification_endpoint: c.backchannel_client_notification_endpoint ?? null,
152
+ response_types: (0, oauth_adv_1.clientResponseTypes)(c),
153
+ require_signed_request_object: c.require_signed_request_object === true,
154
+ ...(0, emu4_oauth_auth_1.clientExtras)(c),
155
+ request_object_signing_alg: c.request_object_signing_alg ?? null,
156
+ request_uris: c.request_uris ?? [],
157
+ jwks: c.jwks ?? null,
158
+ jwks_uri: c.jwks_uri ?? null,
159
+ backchannel_logout_uri: c.backchannel_logout_uri ?? null,
160
+ frontchannel_logout_uri: c.frontchannel_logout_uri ?? null,
161
+ claims_redirect_uris: c.claims_redirect_uris ?? [],
162
+ created_at: c.created_at,
163
+ updated_at: c.updated_at,
164
+ ...(secret ? { client_secret: secret } : {}),
165
+ };
166
+ };
167
+ const liveClient = (e, id) => {
168
+ const c = e.db.oauthClients.get(id);
169
+ if (!c || c.deleted)
170
+ throw (0, errors_1.notFound)('OAuth client');
171
+ return c;
172
+ };
173
+ /** Client authentication with the method the client is REGISTERED for: Basic, `client_id` (+ `client_secret`) in the body, a signed `client_assertion` (private_key_jwt), or nothing for a public client. */
174
+ async function authenticateClient(e, c) {
175
+ let id = typeof c.body.client_id === 'string' ? c.body.client_id : undefined;
176
+ let secret = typeof c.body.client_secret === 'string' ? c.body.client_secret : undefined;
177
+ const basic = /^Basic\s+(.+)$/i.exec(c.request.headers.get('authorization') ?? '')?.[1];
178
+ if (basic) {
179
+ const [i, ...rest] = atob(basic).split(':');
180
+ id = decodeURIComponent(i ?? '');
181
+ secret = decodeURIComponent(rest.join(':'));
182
+ }
183
+ // A client assertion names the client itself when no client_id was sent.
184
+ id ??= (0, emu4_oauth_auth_1.assertionClientId)(c.body);
185
+ const client = e.db.oauthClients.get(id);
186
+ if (!client || client.deleted)
187
+ return { error: (0, exports.fail)(401, 'invalid_client', 'Unknown client.') };
188
+ if (client.auth_method === 'private_key_jwt') {
189
+ const problem = secret !== undefined ? 'This client must authenticate with a private_key_jwt client assertion.' : await (0, emu4_oauth_auth_1.verifyClientAssertion)(e, c.body, client);
190
+ return problem ? { error: (0, exports.fail)(401, 'invalid_client', problem) } : { client };
191
+ }
192
+ const previousOk = client.previous_secret !== undefined && client.previous_secret === secret && e.clock.now() < client.previous_until;
193
+ if (client.auth_method !== 'none' && client.secret_expires_at && e.clock.now() >= client.secret_expires_at)
194
+ return { error: (0, exports.fail)(401, 'invalid_client', 'Client authentication failed.') };
195
+ if (client.auth_method !== 'none' && client.secret !== secret && !previousOk)
196
+ return { error: (0, exports.fail)(401, 'invalid_client', 'Client authentication failed.') };
197
+ return { client };
198
+ }
199
+ const splitScope = (s) => (typeof s === 'string' ? s.split(/\s+/).filter(Boolean) : []);
200
+ exports.splitScope = splitScope;
201
+ async function mintAccess(e, input) {
202
+ const ttl = input.ttl ?? ACCESS_TTL_S;
203
+ const jti = e.db.newId('oat');
204
+ const token = await e.tokens.signClaims({
205
+ sub: input.userId ? await (0, emu4_oauth_auth_1.subjectFor)(e, input.client, input.userId) : input.client.id,
206
+ aud: input.audience ?? input.client.id,
207
+ client_id: input.client.id,
208
+ scope: input.scope.join(' '),
209
+ jti,
210
+ token_use: 'access_token',
211
+ ...(input.cnf ? { cnf: input.cnf } : {}),
212
+ ...(input.extra ?? {}),
213
+ }, ttl);
214
+ const s = (0, exports.st)(e);
215
+ s.access.set(jti, { clientId: input.client.id, userId: input.userId, scope: input.scope, exp: e.clock.now() + ttl * 1000, revoked: false, grantId: input.grant?.id ?? null, cnf: input.cnf });
216
+ input.grant?.jtis.add(jti);
217
+ return { token, jti };
218
+ }
219
+ const userClaims = (e, userId, scope) => {
220
+ const u = e.db.users.get(userId);
221
+ if (!u)
222
+ return {};
223
+ const primary = u.emails.find((m) => m.id === u.primary_email_address_id);
224
+ return {
225
+ ...(scope.includes('email') && primary ? { email: primary.email_address, email_verified: primary.verified === true } : {}),
226
+ ...(scope.includes('profile') ? { name: [u.first_name, u.last_name].filter(Boolean).join(' ') || undefined, picture: u.image_url ?? undefined, given_name: u.first_name ?? undefined, family_name: u.last_name ?? undefined } : {}),
227
+ };
228
+ };
229
+ exports.userClaims = userClaims;
230
+ async function issueTokens(e, client, grant, nonce, authTime, refreshRec, cnf) {
231
+ const access = await mintAccess(e, { client, userId: grant.userId, scope: grant.scope, grant, cnf, extra: grant.managedId ? { grant_id: grant.managedId } : undefined });
232
+ const out = { access_token: access.token, token_type: cnf?.jkt ? 'DPoP' : 'Bearer', expires_in: ACCESS_TTL_S, scope: grant.scope.join(' ') };
233
+ if (grant.managedId)
234
+ out.grant_id = grant.managedId;
235
+ if (grant.details?.length)
236
+ out.authorization_details = grant.details;
237
+ if (grant.scope.includes('offline_access') || client.grant_types.includes('refresh_token')) {
238
+ const rt = refreshRec ?? `ort_${(0, store_1.randomToken)(32)}`;
239
+ if (!(0, exports.st)(e).refresh.has(rt))
240
+ (0, exports.st)(e).refresh.set(rt, { grantId: grant.id });
241
+ out.refresh_token = rt;
242
+ }
243
+ if (grant.scope.includes('openid') && grant.userId) {
244
+ out.id_token = await idTokenFor(e, client, grant.userId, grant.scope, nonce, authTime, grant.sid);
245
+ (0, oauth_adv_1.recordRp)(e, grant.sid, client.id, grant.userId);
246
+ }
247
+ return out;
248
+ }
249
+ async function idTokenFor(e, client, userId, scope, nonce, authTime, sid, extra = {}) {
250
+ return e.tokens.signClaimsAs(client.id_token_signed_response_alg ?? null, { sub: await (0, emu4_oauth_auth_1.subjectFor)(e, client, userId), aud: client.id, azp: client.id, auth_time: Math.floor(authTime / 1000), ...(nonce ? { nonce } : {}), ...(sid ? { sid } : {}), token_use: 'id_token', ...(0, exports.userClaims)(e, userId, scope), ...extra }, ACCESS_TTL_S);
251
+ }
252
+ /** End a grant and every access token minted from it. */
253
+ function revokeGrant(e, g) {
254
+ g.revoked = true;
255
+ for (const j of g.jtis) {
256
+ const a = (0, exports.st)(e).access.get(j);
257
+ if (a)
258
+ a.revoked = true;
259
+ }
260
+ }
261
+ /** A presented access token for a resource of this provider: signature, revocation and its sender constraint (DPoP proof or client certificate). */
262
+ async function checkResourceToken(c, bearer, scheme) {
263
+ const e = c.engine;
264
+ const v = await e.tokens.verifyAny(bearer);
265
+ const rec = v.ok ? (0, exports.st)(e).access.get(String(v.claims.jti)) : undefined;
266
+ if (!v.ok || v.claims.token_use !== 'access_token' || !rec || rec.revoked || (rec.grantId && (0, exports.st)(e).grants.get(rec.grantId)?.revoked))
267
+ return { error: true, message: 'The access token is invalid or expired.' };
268
+ const bad = { error: true, message: 'A valid DPoP proof for this token is required.' };
269
+ if (rec.cnf?.jkt) {
270
+ const proof = c.request.headers.get('dpop');
271
+ if (scheme.toLowerCase() !== 'dpop' || !proof)
272
+ return bad;
273
+ try {
274
+ const r = await (0, jws_1.verifyDpopProof)({ proof, htm: c.request.method, htu: c.request.url, nowMs: e.clock.now(), accessToken: bearer });
275
+ if (r.jkt !== rec.cnf.jkt || !(0, oauth_flows_1.claimDpopJti)(e, r.jkt, r.jti, (jws_1.DPOP_IAT_WINDOW_S + 5) * 1000))
276
+ return bad;
277
+ }
278
+ catch {
279
+ return bad;
280
+ }
281
+ }
282
+ else if (rec.cnf?.['x5t#S256'] && (await (0, jws_1.forwardedCertThumbprint)(c.request.headers)) !== rec.cnf['x5t#S256'])
283
+ return { error: true, message: 'The token is bound to a client certificate that was not presented.' };
284
+ return { rec };
285
+ }
286
+ /** Create the grant and the single-use authorization code for an approved request (after login and, when needed, consent). */
287
+ function issueCode(e, a) {
288
+ const grant = { id: e.db.newId('oag'), clientId: a.client.id, userId: a.userId, scope: a.scope, revoked: false, jtis: new Set(), sid: (0, oauth_adv_1.sidFor)(e, a.userId), details: a.ext?.details ?? null };
289
+ (0, exports.st)(e).grants.set(grant.id, grant);
290
+ const code = `oc_${(0, store_1.randomToken)(24)}`;
291
+ const now = e.clock.now();
292
+ (0, exports.st)(e).codes.set(code, { grantId: grant.id, redirectUri: a.redirectUri, challenge: a.challenge, nonce: a.nonce, authTime: now, expiresAt: now + CODE_TTL_MS, used: false, ext: a.ext });
293
+ return code;
294
+ }
295
+ /** What one token request proved: a DPoP key (single-use proof) and, for clients that ask for it, a client certificate. */
296
+ async function provedOf(c, client) {
297
+ const e = c.engine;
298
+ const proved = {};
299
+ const header = c.request.headers.get('dpop');
300
+ if (header !== null) {
301
+ try {
302
+ const r = await (0, jws_1.verifyDpopProof)({ proof: header, htm: 'POST', htu: c.request.url, nowMs: e.clock.now() });
303
+ if (!(0, oauth_flows_1.claimDpopJti)(e, r.jkt, r.jti, (jws_1.DPOP_IAT_WINDOW_S + 5) * 1000))
304
+ return { error: (0, exports.fail)(400, 'invalid_dpop_proof', 'The DPoP proof has already been used.') };
305
+ proved.jkt = r.jkt;
306
+ }
307
+ catch (err) {
308
+ return { error: (0, exports.fail)(400, 'invalid_dpop_proof', err.message) };
309
+ }
310
+ }
311
+ const x5t = await (0, jws_1.forwardedCertThumbprint)(c.request.headers);
312
+ if (x5t === null)
313
+ return { error: (0, exports.fail)(400, 'invalid_request', 'The forwarded client certificate could not be read.') };
314
+ if (x5t && client.tls_client_certificate_bound_access_tokens === true)
315
+ proved.x5t = x5t;
316
+ if (client.dpop_bound_access_tokens === true && !proved.jkt)
317
+ return { error: (0, exports.fail)(400, 'invalid_dpop_proof', 'This client must send a DPoP proof with every token request.') };
318
+ if (client.tls_client_certificate_bound_access_tokens === true && !proved.x5t)
319
+ return { error: (0, exports.fail)(401, 'invalid_client', 'This client must present its client certificate (mutual TLS).') };
320
+ return { proved };
321
+ }
322
+ const cnfFor = (p) => (p.jkt || p.x5t ? { ...(p.jkt ? { jkt: p.jkt } : {}), ...(p.x5t ? { 'x5t#S256': p.x5t } : {}) } : undefined);
323
+ function oauthHandlers() {
324
+ return [
325
+ // ── client registry ──
326
+ {
327
+ method: 'POST',
328
+ pattern: '/v1/oauth_clients',
329
+ handler: async (c) => {
330
+ const e = c.engine;
331
+ const name = String((0, util_1.need)(c.body, 'name'));
332
+ const uris = c.body.redirect_uris;
333
+ if (!Array.isArray(uris) || !uris.length || !uris.every((u) => typeof u === 'string'))
334
+ throw new errors_1.EmulatorError('VALIDATION_FAILED', 'redirect_uris needs at least one URI.', 'redirect_uris');
335
+ const method = c.body.token_endpoint_auth_method ?? 'client_secret_basic';
336
+ if (!emu4_oauth_auth_1.AUTH_METHODS.includes(method))
337
+ throw new errors_1.EmulatorError('VALIDATION_FAILED', `token_endpoint_auth_method must be one of: ${emu4_oauth_auth_1.AUTH_METHODS.join(', ')}.`, 'token_endpoint_auth_method');
338
+ const flowFields = pickFlowFields(c.body);
339
+ const keyedProblem = (0, emu4_oauth_auth_1.keyedClientProblem)(method, flowFields.jwks, flowFields.jwks_uri);
340
+ if (keyedProblem)
341
+ throw new errors_1.EmulatorError('VALIDATION_FAILED', keyedProblem, 'jwks');
342
+ const subject = await (0, emu4_oauth_auth_1.resolveSubject)(e, { subject_type: c.body.subject_type, sector_identifier_uri: c.body.sector_identifier_uri, redirect_uris: uris, redirectUrisChanged: true });
343
+ if (!subject.ok)
344
+ throw new errors_1.EmulatorError('VALIDATION_FAILED', subject.message, subject.param);
345
+ const rtProblem = (0, oauth_adv_1.responseTypeProblem)((0, oauth_adv_1.clientResponseTypes)(c.body), c.body.grant_types ?? ['authorization_code', 'refresh_token']);
346
+ if (rtProblem)
347
+ throw new errors_1.EmulatorError('VALIDATION_FAILED', rtProblem, 'response_types');
348
+ const now = e.clock.now();
349
+ const secret = method === 'none' || method === 'private_key_jwt' ? undefined : `oacs_${(0, store_1.randomToken)(32)}`;
350
+ const row = e.db.oauthClients.insert({
351
+ name,
352
+ redirect_uris: uris,
353
+ allowed_scopes: c.body.allowed_scopes ?? SCOPES,
354
+ grant_types: c.body.grant_types ?? ['authorization_code', 'refresh_token'],
355
+ auth_method: method,
356
+ first_party: c.body.first_party === true,
357
+ post_logout_redirect_uris: c.body.post_logout_redirect_uris ?? [],
358
+ ...flowFields,
359
+ ...subject.value,
360
+ secret,
361
+ created_at: now,
362
+ updated_at: now,
363
+ });
364
+ e.changed({ type: 'oauth_client.created', data: clientShape(e, row) });
365
+ return { status: 201, body: clientShape(e, row, secret) };
366
+ },
367
+ },
368
+ { method: 'GET', pattern: '/v1/oauth_clients', handler: (c) => (0, util_1.listReply)(c, c.engine.db.oauthClients.filter((x) => !x.deleted), (x) => clientShape(c.engine, x)) },
369
+ { method: 'GET', pattern: '/v1/oauth_clients/:id', handler: (c) => ({ body: clientShape(c.engine, liveClient(c.engine, c.params.id)) }) },
370
+ {
371
+ method: 'PATCH',
372
+ pattern: '/v1/oauth_clients/:id',
373
+ handler: async (c) => {
374
+ const e = c.engine;
375
+ const row = liveClient(e, c.params.id);
376
+ const next = { ...row };
377
+ for (const k of ['name', 'redirect_uris', 'allowed_scopes', 'grant_types', 'first_party', 'post_logout_redirect_uris'])
378
+ if (k in c.body)
379
+ next[k] = c.body[k];
380
+ Object.assign(next, pickFlowFields(c.body, row));
381
+ const keyedProblem = (0, emu4_oauth_auth_1.keyedClientProblem)(next.auth_method, next.jwks, next.jwks_uri);
382
+ if (keyedProblem)
383
+ throw new errors_1.EmulatorError('VALIDATION_FAILED', keyedProblem, 'jwks');
384
+ if (c.body.subject_type !== undefined || c.body.sector_identifier_uri !== undefined || c.body.redirect_uris !== undefined) {
385
+ const subject = await (0, emu4_oauth_auth_1.resolveSubject)(e, { subject_type: c.body.subject_type, sector_identifier_uri: c.body.sector_identifier_uri, redirect_uris: next.redirect_uris, redirectUrisChanged: c.body.redirect_uris !== undefined, existing: row, publisherSector: (0, emu4_oauth_pub_1.publisherSectorOf)(e, row) });
386
+ if (!subject.ok)
387
+ throw new errors_1.EmulatorError('VALIDATION_FAILED', subject.message, subject.param);
388
+ Object.assign(next, subject.value);
389
+ }
390
+ const rtProblem = (0, oauth_adv_1.responseTypeProblem)((0, oauth_adv_1.clientResponseTypes)(next), next.grant_types);
391
+ if (rtProblem)
392
+ throw new errors_1.EmulatorError('VALIDATION_FAILED', rtProblem, 'response_types');
393
+ Object.assign(row, next); // validated first: a refused PATCH changes nothing
394
+ row.updated_at = e.clock.now();
395
+ e.changed({ type: 'oauth_client.updated', data: clientShape(e, row) });
396
+ return { body: clientShape(e, row) };
397
+ },
398
+ },
399
+ {
400
+ method: 'DELETE',
401
+ pattern: '/v1/oauth_clients/:id',
402
+ handler: (c) => {
403
+ const e = c.engine;
404
+ const row = liveClient(e, c.params.id);
405
+ row.deleted = true;
406
+ // Deleting a client ends everything it was issued.
407
+ for (const g of (0, exports.st)(e).grants.values())
408
+ if (g.clientId === row.id)
409
+ g.revoked = true;
410
+ for (const a of (0, exports.st)(e).access.values())
411
+ if (a.clientId === row.id)
412
+ a.revoked = true;
413
+ e.changed({ type: 'oauth_client.deleted', data: { object: 'oauth_client', id: row.id, deleted: true } });
414
+ return { body: { object: 'oauth_client', id: row.id, deleted: true } };
415
+ },
416
+ },
417
+ {
418
+ method: 'POST',
419
+ pattern: '/v1/oauth_clients/:id/rotate_secret',
420
+ handler: (c) => {
421
+ const e = c.engine;
422
+ const row = liveClient(e, c.params.id);
423
+ if (row.auth_method === 'none')
424
+ throw new errors_1.EmulatorError('VALIDATION_FAILED', 'A public client has no secret.');
425
+ // The previous secret keeps working for overlap_seconds (default 24 h, 0 revokes it now), like the real API.
426
+ const overlap = typeof c.body.overlap_seconds === 'number' ? c.body.overlap_seconds : 86_400;
427
+ row.previous_secret = overlap > 0 ? row.secret : undefined;
428
+ row.previous_until = e.clock.now() + overlap * 1000;
429
+ row.secret = `oacs_${(0, store_1.randomToken)(32)}`;
430
+ row.updated_at = e.clock.now();
431
+ e.changed({ type: 'oauth_client.secret_rotated', data: clientShape(e, row) });
432
+ return { body: clientShape(e, row, row.secret) };
433
+ },
434
+ },
435
+ // ── discovery ──
436
+ { method: 'GET', pattern: '/.well-known/oauth-authorization-server', public: true, handler: (c) => ({ body: discoveryDocument(c.engine) }) },
437
+ // ── authorize ──
438
+ {
439
+ // The emulator is its own user agent: it signs in the `login_hint` user (id or email), else the user set at
440
+ // `POST /__emulator/oauth/login`, else the only user, and approves without a consent screen. `emulator_decision=deny`
441
+ // answers access_denied, `prompt=none` with nobody to sign in answers login_required.
442
+ method: 'GET',
443
+ pattern: '/oauth2/authorize',
444
+ public: true,
445
+ handler: async (c) => {
446
+ const e = c.engine;
447
+ const rawUri = c.query.get('request_uri');
448
+ // An https request_uri names a request object to fetch (RFC 9101); anything else is a pushed request (RFC 9126).
449
+ const jarUri = rawUri && /^https:\/\//i.test(rawUri) ? rawUri : null;
450
+ const resolved = jarUri ? { query: c.query } : (0, oauth_flows_1.resolveRequestUri)(e, c.query);
451
+ if ('error' in resolved)
452
+ return (0, exports.fail)(400, resolved.error.code, resolved.error.description);
453
+ const parUsed = !!rawUri && !jarUri;
454
+ const q = resolved.query;
455
+ const client = e.db.oauthClients.get(q.get('client_id') ?? undefined);
456
+ if (!client || client.deleted)
457
+ return (0, exports.fail)(400, 'invalid_request', 'Unknown client_id.');
458
+ if (client.require_pushed_authorization_requests && !parUsed)
459
+ return (0, exports.fail)(400, 'invalid_request', 'This client must push its authorization request (POST /oauth2/par) and send the request_uri.');
460
+ const p = Object.fromEntries(q.entries());
461
+ let rarRaw = p.authorization_details;
462
+ if (client.require_signed_request_object === true && !parUsed && !p.request && !jarUri)
463
+ return (0, exports.fail)(400, 'invalid_request', 'This client must send a signed request object.');
464
+ // A signed request object supersedes the query parameters; it is verified BEFORE the redirect_uri is trusted.
465
+ if (p.request || jarUri) {
466
+ const loaded = await (0, oauth_adv_1.loadRequestObject)(e, client, { request: p.request, requestUri: jarUri ?? undefined });
467
+ if (!loaded.ok)
468
+ return (0, exports.fail)(400, loaded.error, loaded.description);
469
+ for (const k of oauth_adv_1.REQUEST_OBJECT_PARAMS)
470
+ if (typeof loaded.claims[k] === 'string')
471
+ p[k] = loaded.claims[k];
472
+ if (loaded.claims.authorization_details !== undefined)
473
+ rarRaw = loaded.claims.authorization_details;
474
+ delete p.request;
475
+ }
476
+ const redirectUri = p.redirect_uri ?? (client.redirect_uris.length === 1 ? client.redirect_uris[0] : null);
477
+ if (!redirectUri || !client.redirect_uris.includes(redirectUri))
478
+ return (0, exports.fail)(400, 'invalid_request', 'redirect_uri is not registered for this client.');
479
+ const state = p.state;
480
+ const rt = (0, oauth_adv_1.parseResponseType)(p.response_type);
481
+ const mode = p.response_mode ?? null;
482
+ const back = (params) => (0, emu4_oauth_auth_1.authReply)(e, client, redirectUri, (0, oauth_adv_1.authUrl)(e, redirectUri, { ...params, state, iss: e.issuer }, !!rt?.frontChannelTokens, mode), mode);
483
+ if (!rt)
484
+ return back({ error: 'unsupported_response_type', error_description: 'The response_type is not supported.' });
485
+ if (!(0, oauth_adv_1.clientResponseTypes)(client).includes(rt.canonical))
486
+ return back({ error: 'unauthorized_client', error_description: 'This client is not registered for this response_type.' });
487
+ if (mode && !['query', 'fragment', ...emu4_oauth_auth_1.EXT_RESPONSE_MODES].includes(mode))
488
+ return back({ error: 'invalid_request', error_description: 'The response_mode is not supported.' });
489
+ if (mode && !(0, oauth_adv_1.responseModeAllowed)(rt, mode))
490
+ return back({ error: 'invalid_request', error_description: 'A response_type that returns tokens cannot use the query response mode.' });
491
+ // OIDC Core 3.2.2.1 / 3.3.2.1: the nonce is required whenever an ID token comes back from the authorization endpoint.
492
+ if (rt.idToken && !p.nonce)
493
+ return back({ error: 'invalid_request', error_description: 'The nonce parameter is required for this response_type.' });
494
+ const gmParsed = (0, oauth_adv_1.parseGrantManagement)(p.grant_management_action, p.grant_id);
495
+ if (!gmParsed.ok)
496
+ return back({ error: 'invalid_request', error_description: gmParsed.message });
497
+ const gm = gmParsed.value;
498
+ if (rt.code && !client.grant_types.includes('authorization_code'))
499
+ return back({ error: 'unauthorized_client', error_description: 'This client is not permitted the authorization_code grant.' });
500
+ if (rt.frontChannelTokens && !client.grant_types.includes('implicit'))
501
+ return back({ error: 'unauthorized_client', error_description: 'This client is not permitted the implicit grant.' });
502
+ const scope = (0, exports.splitScope)(p.scope ?? 'openid');
503
+ const outside = scope.filter((s) => !client.allowed_scopes.includes(s) && !SCOPES.includes(s));
504
+ if (outside.length)
505
+ return back({ error: 'invalid_scope', error_description: `Scope not allowed: ${outside.join(' ')}.` });
506
+ const challenge = p.code_challenge ?? null;
507
+ if (challenge && (p.code_challenge_method ?? 'plain') !== 'S256')
508
+ return back({ error: 'invalid_request', error_description: 'code_challenge_method must be S256.' });
509
+ if (!challenge && rt.code && client.auth_method === 'none')
510
+ return back({ error: 'invalid_request', error_description: 'A public client must use PKCE (code_challenge).' });
511
+ // RFC 9396: authorization_details against the instance's type allowlist (an empty allowlist refuses any type).
512
+ const rar = (0, oauth_adv_1.validateAuthorizationDetails)(rarRaw, (0, oauth_adv_1.adv)(e).cfg.authorization_details_types);
513
+ if (!rar.ok)
514
+ return back({ error: 'invalid_authorization_details', error_description: rar.message });
515
+ if (q.get('emulator_decision') === 'deny')
516
+ return back({ error: 'access_denied', error_description: 'The user denied the request.' });
517
+ const hint = q.get('login_hint');
518
+ const live = e.db.users.filter((u) => !u.deleted);
519
+ const user = (hint && live.find((u) => u.id === hint || u.emails.some((m) => m.email_address.toLowerCase() === hint.toLowerCase()))) ||
520
+ // A hint that names nobody never falls back to another user.
521
+ (hint ? undefined : (0, exports.st)(e).autoUser ? live.find((u) => u.id === (0, exports.st)(e).autoUser) : undefined) ||
522
+ (!hint && live.length === 1 ? live[0] : undefined);
523
+ if (!user)
524
+ return back({ error: 'login_required', error_description: 'No user to sign in. Pass login_hint or POST /__emulator/oauth/login.' });
525
+ if (user.banned)
526
+ return back({ error: 'access_denied', error_description: 'The user is banned.' });
527
+ const details = rar.value ?? null;
528
+ // Grant management: a merge or replace names this client's and this user's active grant; adding anything to it asks the user again.
529
+ const gmProblem = (0, oauth_adv_1.gmValid)(e, gm, client.id, user.id);
530
+ if (gmProblem)
531
+ return back({ error: 'invalid_grant', error_description: gmProblem });
532
+ const widens = !!gm && gm.action !== 'create' && (0, oauth_adv_1.gmWidens)(gm, (0, oauth_adv_1.managedFor)(e, gm.grantId, client.id, user.id), scope, details);
533
+ const ext = { rt: rt.canonical, mode, details, gm };
534
+ const gate = await (0, oauth_flows_1.consentGate)(e, { client, user, scope, redirectUri, challenge, nonce: p.nonce ?? null, state, prompt: q.get('prompt'), ext, force: widens });
535
+ if (gate)
536
+ return gate;
537
+ return (0, emu4_oauth_auth_1.authReply)(e, client, redirectUri, await (0, oauth_adv_1.completeAuth)(e, { client, userId: user.id, scope, redirectUri, challenge, nonce: p.nonce ?? null, state, ext }), mode);
538
+ },
539
+ },
540
+ { method: 'POST', pattern: '/__emulator/oauth/login', public: true, handler: (c) => (((0, exports.st)(c.engine).autoUser = typeof c.body.user_id === 'string' ? c.body.user_id : null), { body: { ok: true } }) },
541
+ // ── token ──
542
+ {
543
+ method: 'POST',
544
+ pattern: '/oauth2/token',
545
+ public: true,
546
+ handler: async (c) => {
547
+ const e = c.engine;
548
+ const auth = await authenticateClient(e, c);
549
+ if ('error' in auth)
550
+ return auth.error;
551
+ const client = auth.client;
552
+ const type = String(c.body.grant_type ?? '');
553
+ if (!type)
554
+ return (0, exports.fail)(400, 'invalid_request', 'grant_type is required.');
555
+ const umaPat = type === 'client_credentials' && !c.body.audience && (0, exports.splitScope)(c.body.scope).includes('uma_protection') && (0, oauth_adv_1.umaEnabled)(e);
556
+ if (type !== EXCHANGE && type !== oauth_adv_1.UMA_GRANT_TYPE && !umaPat && !client.grant_types.includes(type) && !(type === 'refresh_token'))
557
+ return (0, exports.fail)(400, 'unauthorized_client', `This client may not use ${type}.`);
558
+ const s = (0, exports.st)(e);
559
+ const now = e.clock.now();
560
+ const pr = await provedOf(c, client);
561
+ if ('error' in pr)
562
+ return pr.error;
563
+ const cnf = cnfFor(pr.proved);
564
+ const ext = await (0, oauth_flows_1.extensionGrant)(c, client, type, cnf);
565
+ if (ext)
566
+ return ext;
567
+ if (umaPat)
568
+ return client.auth_method === 'none' ? (0, exports.fail)(401, 'invalid_client', 'A protection API token requires a confidential client.') : (0, oauth_adv_1.umaProtectionToken)(c, client);
569
+ if (type === oauth_adv_1.UMA_GRANT_TYPE && (0, oauth_adv_1.umaEnabled)(e))
570
+ return (0, oauth_adv_1.umaTicketGrant)(c, client);
571
+ if (type === 'authorization_code') {
572
+ const rec = s.codes.get(String(c.body.code ?? ''));
573
+ if (!rec)
574
+ return (0, exports.fail)(400, 'invalid_grant', 'The authorization code is invalid.');
575
+ const grant = s.grants.get(rec.grantId);
576
+ if (rec.used) {
577
+ // RFC 6749 section 4.1.2: a replayed code revokes what it already produced.
578
+ grant.revoked = true;
579
+ for (const j of grant.jtis) {
580
+ const a = s.access.get(j);
581
+ if (a)
582
+ a.revoked = true;
583
+ }
584
+ return (0, exports.fail)(400, 'invalid_grant', 'The authorization code was already used.');
585
+ }
586
+ if (grant.clientId !== client.id)
587
+ return (0, exports.fail)(400, 'invalid_grant', 'The code was issued to another client.');
588
+ if (rec.expiresAt <= now)
589
+ return (0, exports.fail)(400, 'invalid_grant', 'The authorization code has expired.');
590
+ if (rec.redirectUri !== (c.body.redirect_uri ?? rec.redirectUri))
591
+ return (0, exports.fail)(400, 'invalid_grant', 'redirect_uri does not match the authorization request.');
592
+ if (rec.challenge) {
593
+ const verifier = String(c.body.code_verifier ?? '');
594
+ if (!verifier || (await (0, crypto_util_1.pkceChallenge)(verifier)) !== rec.challenge)
595
+ return (0, exports.fail)(400, 'invalid_grant', 'PKCE verification failed.');
596
+ }
597
+ rec.used = true;
598
+ if (pr.proved.jkt)
599
+ grant.jkt = pr.proved.jkt;
600
+ // OpenID grant management: apply the create / merge / replace the code carried; the tokens are for the grant as it stands afterwards.
601
+ if (rec.ext?.gm) {
602
+ const applied = (0, oauth_adv_1.applyGrantManagement)(e, grant, rec.ext.gm);
603
+ if (applied)
604
+ return (0, exports.fail)(400, applied.error, applied.message);
605
+ }
606
+ return { body: await issueTokens(e, client, grant, rec.nonce, rec.authTime, undefined, cnf), headers: { 'cache-control': 'no-store', pragma: 'no-cache' } };
607
+ }
608
+ if (type === 'refresh_token') {
609
+ const token = String(c.body.refresh_token ?? '');
610
+ const rec = s.refresh.get(token);
611
+ const grant = rec ? s.grants.get(rec.grantId) : undefined;
612
+ if (!rec || !grant || grant.clientId !== client.id)
613
+ return (0, exports.fail)(400, 'invalid_grant', 'The refresh token is invalid.');
614
+ if (grant.revoked || (grant.managedId && (0, oauth_adv_1.adv)(e).managed.get(grant.managedId)?.status !== 'active'))
615
+ return (0, exports.fail)(400, 'invalid_grant', 'The grant was revoked.');
616
+ // A DPoP-bound refresh token is usable only with the key it was bound to (RFC 9449 section 8).
617
+ if (grant.jkt && pr.proved.jkt !== grant.jkt)
618
+ return (0, exports.fail)(400, 'invalid_dpop_proof', 'This refresh token is bound to a DPoP key: send a proof signed with it.');
619
+ if (rec.rotatedAt !== undefined) {
620
+ if (now - rec.rotatedAt <= GRACE_MS && rec.successor)
621
+ return { body: await issueTokens(e, client, grant, null, now, rec.successor, cnf), headers: { 'cache-control': 'no-store' } };
622
+ grant.revoked = true; // reuse: end the whole family
623
+ for (const j of grant.jtis) {
624
+ const a = s.access.get(j);
625
+ if (a)
626
+ a.revoked = true;
627
+ }
628
+ return (0, exports.fail)(400, 'invalid_grant', 'The refresh token was already used; the grant was revoked.');
629
+ }
630
+ const asked = (0, exports.splitScope)(c.body.scope);
631
+ if (asked.some((x) => !grant.scope.includes(x)))
632
+ return (0, exports.fail)(400, 'invalid_scope', 'A refresh cannot widen the granted scope.');
633
+ const narrowed = asked.length ? { ...grant, scope: asked } : grant;
634
+ const next = `ort_${(0, store_1.randomToken)(32)}`;
635
+ rec.rotatedAt = now;
636
+ rec.successor = next;
637
+ s.refresh.set(next, { grantId: grant.id });
638
+ return { body: await issueTokens(e, client, narrowed, null, now, next, cnf), headers: { 'cache-control': 'no-store' } };
639
+ }
640
+ if (type === 'client_credentials') {
641
+ if (client.auth_method === 'none')
642
+ return (0, exports.fail)(401, 'invalid_client', 'A public client cannot use client_credentials.');
643
+ const asked = (0, exports.splitScope)(c.body.scope);
644
+ const scope = asked.length ? asked : client.allowed_scopes.filter((x) => !SCOPES.includes(x));
645
+ if (asked.some((x) => !client.allowed_scopes.includes(x)))
646
+ return (0, exports.fail)(400, 'invalid_scope', 'Scope not allowed for this client.');
647
+ const a = await mintAccess(e, { client, userId: null, scope, grant: null, audience: typeof c.body.audience === 'string' ? c.body.audience : undefined, cnf });
648
+ return { body: { access_token: a.token, token_type: cnf?.jkt ? 'DPoP' : 'Bearer', expires_in: ACCESS_TTL_S, scope: scope.join(' ') }, headers: { 'cache-control': 'no-store' } };
649
+ }
650
+ if (type === EXCHANGE && c.body.subject_token_type === emu4_oauth_pub_1.GOOGLE_ACCESS_TOKEN_TYPE)
651
+ return (0, emu4_oauth_pub_1.googleTokenExchange)(c, client);
652
+ if (type === EXCHANGE) {
653
+ const subject = String(c.body.subject_token ?? '');
654
+ if (!subject)
655
+ return (0, exports.fail)(400, 'invalid_request', 'subject_token is required.');
656
+ const verified = await e.tokens.verifyAny(subject);
657
+ if (!verified.ok)
658
+ return (0, exports.fail)(400, 'invalid_grant', 'The subject token is not valid.');
659
+ const claims = verified.claims;
660
+ const rec = typeof claims.jti === 'string' ? s.access.get(claims.jti) : undefined;
661
+ if (rec?.revoked || (rec && s.grants.get(rec.grantId ?? '')?.revoked))
662
+ return (0, exports.fail)(400, 'invalid_grant', 'The subject token was revoked.');
663
+ // A session token may be exchanged too: its subject is the user, its scope the client's allowed set.
664
+ const held = typeof claims.scope === 'string' ? (0, exports.splitScope)(claims.scope) : client.allowed_scopes;
665
+ const asked = (0, exports.splitScope)(c.body.scope);
666
+ if (asked.some((x) => !held.includes(x)))
667
+ return (0, exports.fail)(400, 'invalid_scope', 'Token exchange can only narrow scope.');
668
+ let actor;
669
+ if (c.body.actor_token) {
670
+ const a = await e.tokens.verifyAny(String(c.body.actor_token));
671
+ if (!a.ok)
672
+ return (0, exports.fail)(400, 'invalid_grant', 'The actor token is not valid.');
673
+ actor = { sub: a.claims.sub, ...(a.claims.act ? { act: a.claims.act } : {}) };
674
+ }
675
+ const scope = asked.length ? asked : held;
676
+ const out = await mintAccess(e, { client, userId: typeof claims.sub === 'string' && !String(claims.sub).startsWith('oac_') ? claims.sub : null, scope, grant: null, audience: typeof c.body.audience === 'string' ? c.body.audience : undefined, extra: actor ? { act: actor } : undefined, cnf });
677
+ return { body: { access_token: out.token, issued_token_type: ACCESS_TYPE, token_type: cnf?.jkt ? 'DPoP' : 'Bearer', expires_in: ACCESS_TTL_S, scope: scope.join(' ') }, headers: { 'cache-control': 'no-store' } };
678
+ }
679
+ return (0, exports.fail)(400, 'unsupported_grant_type', `Unsupported grant_type ${type}.`);
680
+ },
681
+ },
682
+ // ── revoke / introspect / userinfo ──
683
+ {
684
+ method: 'POST',
685
+ pattern: '/oauth2/revoke',
686
+ public: true,
687
+ handler: async (c) => {
688
+ const e = c.engine;
689
+ const auth = await authenticateClient(e, c);
690
+ if ('error' in auth)
691
+ return auth.error;
692
+ const token = String(c.body.token ?? '');
693
+ if (!token)
694
+ return (0, exports.fail)(400, 'invalid_request', 'token is required.');
695
+ const s = (0, exports.st)(e);
696
+ // RFC 7009: always 200, whatever the token was; only the owning client's tokens are touched.
697
+ const rt = s.refresh.get(token);
698
+ const grantOf = rt ? s.grants.get(rt.grantId) : undefined;
699
+ if (grantOf && grantOf.clientId === auth.client.id) {
700
+ grantOf.revoked = true;
701
+ for (const j of grantOf.jtis) {
702
+ const a = s.access.get(j);
703
+ if (a)
704
+ a.revoked = true;
705
+ }
706
+ }
707
+ else {
708
+ try {
709
+ const claims = JSON.parse(atob(token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/')));
710
+ const a = s.access.get(claims.jti);
711
+ if (a && a.clientId === auth.client.id)
712
+ a.revoked = true;
713
+ }
714
+ catch {
715
+ /* not a token we issued: nothing to revoke */
716
+ }
717
+ }
718
+ return { status: 200, headers: { 'cache-control': 'no-store' } };
719
+ },
720
+ },
721
+ {
722
+ method: 'POST',
723
+ pattern: '/oauth2/introspect',
724
+ public: true,
725
+ handler: async (c) => {
726
+ const e = c.engine;
727
+ const auth = await authenticateClient(e, c);
728
+ if ('error' in auth)
729
+ return auth.error;
730
+ const token = String(c.body.token ?? '');
731
+ const s = (0, exports.st)(e);
732
+ const inactive = { body: { active: false }, headers: { 'cache-control': 'no-store' } };
733
+ const rt = s.refresh.get(token);
734
+ if (rt) {
735
+ const g = s.grants.get(rt.grantId);
736
+ if (!g || g.revoked || rt.rotatedAt !== undefined || g.clientId !== auth.client.id)
737
+ return inactive;
738
+ return { body: { active: true, token_use: 'refresh_token', client_id: g.clientId, ...(g.userId ? { sub: await (0, emu4_oauth_auth_1.subjectFor)(e, auth.client, g.userId) } : {}), scope: g.scope.join(' '), ...(g.details?.length ? { authorization_details: g.details } : {}), ...(g.managedId ? { grant_id: g.managedId } : {}) }, headers: { 'cache-control': 'no-store' } };
739
+ }
740
+ const v = await e.tokens.verifyAny(token);
741
+ if (!v.ok || v.claims.token_use !== 'access_token')
742
+ return inactive;
743
+ const rec = s.access.get(String(v.claims.jti));
744
+ if (!rec || rec.revoked || (rec.grantId && s.grants.get(rec.grantId)?.revoked))
745
+ return inactive;
746
+ // another client's token: do not leak (a resource server may introspect the tokens issued for it)
747
+ if (rec.clientId !== auth.client.id && v.claims.uma_rs !== auth.client.id)
748
+ return inactive;
749
+ const granted = rec.grantId ? s.grants.get(rec.grantId) : undefined;
750
+ return { body: { active: true, token_type: rec.cnf?.jkt ? 'DPoP' : 'Bearer', ...(granted?.details?.length ? { authorization_details: granted.details } : {}), ...(v.claims.grant_id ? { grant_id: v.claims.grant_id } : {}), ...(v.claims.permissions ? { permissions: v.claims.permissions, uma_rs: v.claims.uma_rs } : {}), ...(rec.cnf ? { cnf: rec.cnf } : {}), token_use: 'access_token', client_id: rec.clientId, sub: v.claims.sub, aud: v.claims.aud, iss: v.claims.iss, scope: rec.scope.join(' '), exp: v.claims.exp, iat: v.claims.iat, jti: v.claims.jti, ...(v.claims.act ? { act: v.claims.act } : {}) }, headers: { 'cache-control': 'no-store' } };
751
+ },
752
+ },
753
+ ...['GET', 'POST'].map((method) => ({
754
+ method,
755
+ pattern: '/oauth2/userinfo',
756
+ public: true,
757
+ handler: async (c) => {
758
+ const e = c.engine;
759
+ const m = /^(Bearer|DPoP)\s+(.+)$/i.exec(c.request.headers.get('authorization') ?? '');
760
+ const bearer = m?.[2];
761
+ if (!bearer)
762
+ return { status: 401, headers: { 'www-authenticate': 'Bearer error="invalid_token"' }, body: { error: 'invalid_token', error_description: 'A bearer access token is required.' } };
763
+ const v = await e.tokens.verifyAny(bearer);
764
+ const rec = v.ok ? (0, exports.st)(e).access.get(String(v.claims.jti)) : undefined;
765
+ if (!v.ok || v.claims.token_use !== 'access_token' || !rec || rec.revoked || (rec.grantId && (0, exports.st)(e).grants.get(rec.grantId)?.revoked) || !rec.userId) {
766
+ return { status: 401, headers: { 'www-authenticate': 'Bearer error="invalid_token"' }, body: { error: 'invalid_token', error_description: 'The access token is not valid.' } };
767
+ }
768
+ if (rec.cnf?.jkt) {
769
+ // A DPoP-bound token is presented with the DPoP scheme and a proof bound to this request and this token (RFC 9449 section 7).
770
+ const proof = c.request.headers.get('dpop');
771
+ const bad = { status: 401, headers: { 'www-authenticate': 'DPoP error="invalid_dpop_proof", algs="ES256 ES384 RS256 PS256 EdDSA"' }, body: { error: 'invalid_dpop_proof', error_description: 'A DPoP proof bound to this access token is required.' } };
772
+ if (m?.[1]?.toLowerCase() !== 'dpop' || !proof)
773
+ return bad;
774
+ try {
775
+ const r = await (0, jws_1.verifyDpopProof)({ proof, htm: c.request.method, htu: c.request.url, nowMs: e.clock.now(), accessToken: bearer });
776
+ if (r.jkt !== rec.cnf.jkt || !(0, oauth_flows_1.claimDpopJti)(e, r.jkt, r.jti, (jws_1.DPOP_IAT_WINDOW_S + 5) * 1000))
777
+ return bad;
778
+ }
779
+ catch {
780
+ return bad;
781
+ }
782
+ }
783
+ else if (rec.cnf?.['x5t#S256'] && (await (0, jws_1.forwardedCertThumbprint)(c.request.headers)) !== rec.cnf['x5t#S256']) {
784
+ return { status: 401, headers: { 'www-authenticate': 'Bearer error="invalid_token"' }, body: { error: 'invalid_token', error_description: 'The token is bound to a client certificate that was not presented.' } };
785
+ }
786
+ if (!rec.scope.includes('openid'))
787
+ return { status: 403, headers: { 'www-authenticate': 'Bearer error="insufficient_scope", scope="openid"' }, body: { error: 'insufficient_scope', error_description: 'The openid scope is required.' } };
788
+ return { body: { sub: await (0, emu4_oauth_auth_1.subjectFor)(e, e.db.oauthClients.get(rec.clientId), rec.userId), ...(0, exports.userClaims)(e, rec.userId, rec.scope) } };
789
+ },
790
+ })),
791
+ ];
792
+ }
793
+ //# sourceMappingURL=oauth.js.map