@udibo/oauth2 0.1.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 (375) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +174 -0
  3. package/SECURITY.md +77 -0
  4. package/esm/_dnt.polyfills.d.ts +17 -0
  5. package/esm/_dnt.polyfills.js +15 -0
  6. package/esm/adapters/hono/_common.d.ts +66 -0
  7. package/esm/adapters/hono/_common.js +108 -0
  8. package/esm/adapters/hono/authorization-server.d.ts +211 -0
  9. package/esm/adapters/hono/authorization-server.js +214 -0
  10. package/esm/adapters/hono/bff/auth-request-store.d.ts +164 -0
  11. package/esm/adapters/hono/bff/auth-request-store.js +154 -0
  12. package/esm/adapters/hono/bff/bff.d.ts +852 -0
  13. package/esm/adapters/hono/bff/bff.js +1194 -0
  14. package/esm/adapters/hono/bff/cookie-prefix.d.ts +72 -0
  15. package/esm/adapters/hono/bff/cookie-prefix.js +85 -0
  16. package/esm/adapters/hono/bff/mod.d.ts +19 -0
  17. package/esm/adapters/hono/bff/mod.js +19 -0
  18. package/esm/adapters/hono/bff/proxy.d.ts +108 -0
  19. package/esm/adapters/hono/bff/proxy.js +314 -0
  20. package/esm/adapters/hono/bff/session-store.d.ts +250 -0
  21. package/esm/adapters/hono/bff/session-store.js +216 -0
  22. package/esm/adapters/hono/bff/testing.d.ts +165 -0
  23. package/esm/adapters/hono/bff/testing.js +254 -0
  24. package/esm/adapters/hono/identity.d.ts +117 -0
  25. package/esm/adapters/hono/identity.js +215 -0
  26. package/esm/adapters/hono/log.d.ts +49 -0
  27. package/esm/adapters/hono/log.js +66 -0
  28. package/esm/adapters/hono/resource-server.d.ts +101 -0
  29. package/esm/adapters/hono/resource-server.js +108 -0
  30. package/esm/client/_http.d.ts +91 -0
  31. package/esm/client/_http.js +303 -0
  32. package/esm/client/base.d.ts +184 -0
  33. package/esm/client/base.js +93 -0
  34. package/esm/client/bff-client.d.ts +235 -0
  35. package/esm/client/bff-client.js +280 -0
  36. package/esm/client/browser-storage.d.ts +152 -0
  37. package/esm/client/browser-storage.js +246 -0
  38. package/esm/client/check.d.ts +114 -0
  39. package/esm/client/check.js +97 -0
  40. package/esm/client/direct-client.d.ts +707 -0
  41. package/esm/client/direct-client.js +1237 -0
  42. package/esm/client/discovery-cache.d.ts +138 -0
  43. package/esm/client/discovery-cache.js +124 -0
  44. package/esm/client/events.d.ts +54 -0
  45. package/esm/client/events.js +37 -0
  46. package/esm/client/mod.d.ts +35 -0
  47. package/esm/client/mod.js +33 -0
  48. package/esm/client/storage.d.ts +127 -0
  49. package/esm/client/storage.js +81 -0
  50. package/esm/deps/jsr.io/@std/assert/1.0.19/almost_equals.d.ts +29 -0
  51. package/esm/deps/jsr.io/@std/assert/1.0.19/almost_equals.js +47 -0
  52. package/esm/deps/jsr.io/@std/assert/1.0.19/array_includes.d.ts +28 -0
  53. package/esm/deps/jsr.io/@std/assert/1.0.19/array_includes.js +65 -0
  54. package/esm/deps/jsr.io/@std/assert/1.0.19/assert.d.ts +16 -0
  55. package/esm/deps/jsr.io/@std/assert/1.0.19/assert.js +23 -0
  56. package/esm/deps/jsr.io/@std/assert/1.0.19/assertion_error.d.ts +25 -0
  57. package/esm/deps/jsr.io/@std/assert/1.0.19/assertion_error.js +30 -0
  58. package/esm/deps/jsr.io/@std/assert/1.0.19/equal.d.ts +24 -0
  59. package/esm/deps/jsr.io/@std/assert/1.0.19/equal.js +193 -0
  60. package/esm/deps/jsr.io/@std/assert/1.0.19/equals.d.ts +36 -0
  61. package/esm/deps/jsr.io/@std/assert/1.0.19/equals.js +61 -0
  62. package/esm/deps/jsr.io/@std/assert/1.0.19/exists.d.ts +17 -0
  63. package/esm/deps/jsr.io/@std/assert/1.0.19/exists.js +27 -0
  64. package/esm/deps/jsr.io/@std/assert/1.0.19/fail.d.ts +14 -0
  65. package/esm/deps/jsr.io/@std/assert/1.0.19/fail.js +20 -0
  66. package/esm/deps/jsr.io/@std/assert/1.0.19/false.d.ts +18 -0
  67. package/esm/deps/jsr.io/@std/assert/1.0.19/false.js +23 -0
  68. package/esm/deps/jsr.io/@std/assert/1.0.19/greater.d.ts +19 -0
  69. package/esm/deps/jsr.io/@std/assert/1.0.19/greater.js +29 -0
  70. package/esm/deps/jsr.io/@std/assert/1.0.19/greater_or_equal.d.ts +19 -0
  71. package/esm/deps/jsr.io/@std/assert/1.0.19/greater_or_equal.js +29 -0
  72. package/esm/deps/jsr.io/@std/assert/1.0.19/instance_of.d.ts +22 -0
  73. package/esm/deps/jsr.io/@std/assert/1.0.19/instance_of.js +52 -0
  74. package/esm/deps/jsr.io/@std/assert/1.0.19/is_error.d.ts +24 -0
  75. package/esm/deps/jsr.io/@std/assert/1.0.19/is_error.js +53 -0
  76. package/esm/deps/jsr.io/@std/assert/1.0.19/less.d.ts +18 -0
  77. package/esm/deps/jsr.io/@std/assert/1.0.19/less.js +28 -0
  78. package/esm/deps/jsr.io/@std/assert/1.0.19/less_or_equal.d.ts +19 -0
  79. package/esm/deps/jsr.io/@std/assert/1.0.19/less_or_equal.js +29 -0
  80. package/esm/deps/jsr.io/@std/assert/1.0.19/match.d.ts +17 -0
  81. package/esm/deps/jsr.io/@std/assert/1.0.19/match.js +26 -0
  82. package/esm/deps/jsr.io/@std/assert/1.0.19/mod.d.ts +43 -0
  83. package/esm/deps/jsr.io/@std/assert/1.0.19/mod.js +45 -0
  84. package/esm/deps/jsr.io/@std/assert/1.0.19/not_equals.d.ts +26 -0
  85. package/esm/deps/jsr.io/@std/assert/1.0.19/not_equals.js +39 -0
  86. package/esm/deps/jsr.io/@std/assert/1.0.19/not_instance_of.d.ts +19 -0
  87. package/esm/deps/jsr.io/@std/assert/1.0.19/not_instance_of.js +29 -0
  88. package/esm/deps/jsr.io/@std/assert/1.0.19/not_match.d.ts +17 -0
  89. package/esm/deps/jsr.io/@std/assert/1.0.19/not_match.js +26 -0
  90. package/esm/deps/jsr.io/@std/assert/1.0.19/not_strict_equals.d.ts +22 -0
  91. package/esm/deps/jsr.io/@std/assert/1.0.19/not_strict_equals.js +32 -0
  92. package/esm/deps/jsr.io/@std/assert/1.0.19/object_match.d.ts +28 -0
  93. package/esm/deps/jsr.io/@std/assert/1.0.19/object_match.js +177 -0
  94. package/esm/deps/jsr.io/@std/assert/1.0.19/rejects.d.ts +41 -0
  95. package/esm/deps/jsr.io/@std/assert/1.0.19/rejects.js +53 -0
  96. package/esm/deps/jsr.io/@std/assert/1.0.19/strict_equals.d.ts +23 -0
  97. package/esm/deps/jsr.io/@std/assert/1.0.19/strict_equals.js +58 -0
  98. package/esm/deps/jsr.io/@std/assert/1.0.19/string_includes.d.ts +17 -0
  99. package/esm/deps/jsr.io/@std/assert/1.0.19/string_includes.js +26 -0
  100. package/esm/deps/jsr.io/@std/assert/1.0.19/throws.d.ts +44 -0
  101. package/esm/deps/jsr.io/@std/assert/1.0.19/throws.js +44 -0
  102. package/esm/deps/jsr.io/@std/assert/1.0.19/unimplemented.d.ts +14 -0
  103. package/esm/deps/jsr.io/@std/assert/1.0.19/unimplemented.js +20 -0
  104. package/esm/deps/jsr.io/@std/assert/1.0.19/unreachable.d.ts +14 -0
  105. package/esm/deps/jsr.io/@std/assert/1.0.19/unreachable.js +20 -0
  106. package/esm/deps/jsr.io/@std/async/1.5.0/delay.d.ts +44 -0
  107. package/esm/deps/jsr.io/@std/async/1.5.0/delay.js +84 -0
  108. package/esm/deps/jsr.io/@std/crypto/1.1.0/timing_safe_equal.d.ts +39 -0
  109. package/esm/deps/jsr.io/@std/crypto/1.1.0/timing_safe_equal.js +61 -0
  110. package/esm/deps/jsr.io/@std/encoding/1.0.11/_common16.d.ts +22 -0
  111. package/esm/deps/jsr.io/@std/encoding/1.0.11/_common16.js +51 -0
  112. package/esm/deps/jsr.io/@std/encoding/1.0.11/_common32.d.ts +34 -0
  113. package/esm/deps/jsr.io/@std/encoding/1.0.11/_common32.js +192 -0
  114. package/esm/deps/jsr.io/@std/encoding/1.0.11/_common64.d.ts +34 -0
  115. package/esm/deps/jsr.io/@std/encoding/1.0.11/_common64.js +113 -0
  116. package/esm/deps/jsr.io/@std/encoding/1.0.11/_common_detach.d.ts +3 -0
  117. package/esm/deps/jsr.io/@std/encoding/1.0.11/_common_detach.js +13 -0
  118. package/esm/deps/jsr.io/@std/encoding/1.0.11/_types.d.ts +8 -0
  119. package/esm/deps/jsr.io/@std/encoding/1.0.11/_types.js +2 -0
  120. package/esm/deps/jsr.io/@std/encoding/1.0.11/_validate_binary_like.d.ts +1 -0
  121. package/esm/deps/jsr.io/@std/encoding/1.0.11/_validate_binary_like.js +26 -0
  122. package/esm/deps/jsr.io/@std/encoding/1.0.11/ascii85.d.ts +60 -0
  123. package/esm/deps/jsr.io/@std/encoding/1.0.11/ascii85.js +152 -0
  124. package/esm/deps/jsr.io/@std/encoding/1.0.11/base32.d.ts +39 -0
  125. package/esm/deps/jsr.io/@std/encoding/1.0.11/base32.js +87 -0
  126. package/esm/deps/jsr.io/@std/encoding/1.0.11/base58.d.ts +39 -0
  127. package/esm/deps/jsr.io/@std/encoding/1.0.11/base58.js +131 -0
  128. package/esm/deps/jsr.io/@std/encoding/1.0.11/base64.d.ts +39 -0
  129. package/esm/deps/jsr.io/@std/encoding/1.0.11/base64.js +82 -0
  130. package/esm/deps/jsr.io/@std/encoding/1.0.11/base64url.d.ts +39 -0
  131. package/esm/deps/jsr.io/@std/encoding/1.0.11/base64url.js +72 -0
  132. package/esm/deps/jsr.io/@std/encoding/1.0.11/hex.d.ts +38 -0
  133. package/esm/deps/jsr.io/@std/encoding/1.0.11/hex.js +87 -0
  134. package/esm/deps/jsr.io/@std/encoding/1.0.11/mod.d.ts +97 -0
  135. package/esm/deps/jsr.io/@std/encoding/1.0.11/mod.js +99 -0
  136. package/esm/deps/jsr.io/@std/encoding/1.0.11/varint.d.ts +119 -0
  137. package/esm/deps/jsr.io/@std/encoding/1.0.11/varint.js +209 -0
  138. package/esm/deps/jsr.io/@std/http/1.1.3/status.d.ts +343 -0
  139. package/esm/deps/jsr.io/@std/http/1.1.3/status.js +343 -0
  140. package/esm/deps/jsr.io/@std/internal/1.0.14/assertion_state.d.ts +159 -0
  141. package/esm/deps/jsr.io/@std/internal/1.0.14/assertion_state.js +223 -0
  142. package/esm/deps/jsr.io/@std/internal/1.0.14/build_message.d.ts +79 -0
  143. package/esm/deps/jsr.io/@std/internal/1.0.14/build_message.js +116 -0
  144. package/esm/deps/jsr.io/@std/internal/1.0.14/diff.d.ts +139 -0
  145. package/esm/deps/jsr.io/@std/internal/1.0.14/diff.js +277 -0
  146. package/esm/deps/jsr.io/@std/internal/1.0.14/diff_str.d.ts +98 -0
  147. package/esm/deps/jsr.io/@std/internal/1.0.14/diff_str.js +181 -0
  148. package/esm/deps/jsr.io/@std/internal/1.0.14/format.d.ts +29 -0
  149. package/esm/deps/jsr.io/@std/internal/1.0.14/format.js +68 -0
  150. package/esm/deps/jsr.io/@std/internal/1.0.14/styles.d.ts +158 -0
  151. package/esm/deps/jsr.io/@std/internal/1.0.14/styles.js +206 -0
  152. package/esm/deps/jsr.io/@std/internal/1.0.14/types.d.ts +24 -0
  153. package/esm/deps/jsr.io/@std/internal/1.0.14/types.js +3 -0
  154. package/esm/deps/jsr.io/@std/testing/1.0.20/_test_suite.d.ts +91 -0
  155. package/esm/deps/jsr.io/@std/testing/1.0.20/_test_suite.js +347 -0
  156. package/esm/deps/jsr.io/@std/testing/1.0.20/bdd.d.ts +382 -0
  157. package/esm/deps/jsr.io/@std/testing/1.0.20/bdd.js +1110 -0
  158. package/esm/deps/jsr.io/@udibo/http-error/0.11.1/mod.d.ts +363 -0
  159. package/esm/deps/jsr.io/@udibo/http-error/0.11.1/mod.js +616 -0
  160. package/esm/errors.d.ts +203 -0
  161. package/esm/errors.js +287 -0
  162. package/esm/identity/captcha.d.ts +147 -0
  163. package/esm/identity/captcha.js +64 -0
  164. package/esm/identity/delivery.d.ts +96 -0
  165. package/esm/identity/delivery.js +41 -0
  166. package/esm/identity/errors.d.ts +39 -0
  167. package/esm/identity/errors.js +46 -0
  168. package/esm/identity/events.d.ts +242 -0
  169. package/esm/identity/events.js +39 -0
  170. package/esm/identity/external/_shared.d.ts +86 -0
  171. package/esm/identity/external/_shared.js +107 -0
  172. package/esm/identity/external/_token-exchange.d.ts +61 -0
  173. package/esm/identity/external/_token-exchange.js +179 -0
  174. package/esm/identity/external/apple-client-secret.d.ts +82 -0
  175. package/esm/identity/external/apple-client-secret.js +133 -0
  176. package/esm/identity/external/apple.d.ts +64 -0
  177. package/esm/identity/external/apple.js +248 -0
  178. package/esm/identity/external/discord.d.ts +38 -0
  179. package/esm/identity/external/discord.js +62 -0
  180. package/esm/identity/external/errors.d.ts +48 -0
  181. package/esm/identity/external/errors.js +36 -0
  182. package/esm/identity/external/flow.d.ts +107 -0
  183. package/esm/identity/external/flow.js +158 -0
  184. package/esm/identity/external/github.d.ts +42 -0
  185. package/esm/identity/external/github.js +164 -0
  186. package/esm/identity/external/google.d.ts +52 -0
  187. package/esm/identity/external/google.js +38 -0
  188. package/esm/identity/external/mod.d.ts +63 -0
  189. package/esm/identity/external/mod.js +61 -0
  190. package/esm/identity/external/oauth2.d.ts +110 -0
  191. package/esm/identity/external/oauth2.js +158 -0
  192. package/esm/identity/external/oidc.d.ts +81 -0
  193. package/esm/identity/external/oidc.js +250 -0
  194. package/esm/identity/external/provider.d.ts +132 -0
  195. package/esm/identity/external/provider.js +12 -0
  196. package/esm/identity/hibp.d.ts +67 -0
  197. package/esm/identity/hibp.js +87 -0
  198. package/esm/identity/identifier.d.ts +44 -0
  199. package/esm/identity/identifier.js +47 -0
  200. package/esm/identity/lockout.d.ts +155 -0
  201. package/esm/identity/lockout.js +117 -0
  202. package/esm/identity/mfa/mod.d.ts +42 -0
  203. package/esm/identity/mfa/mod.js +42 -0
  204. package/esm/identity/mfa/recovery-codes.d.ts +96 -0
  205. package/esm/identity/mfa/recovery-codes.js +80 -0
  206. package/esm/identity/mfa/service.d.ts +338 -0
  207. package/esm/identity/mfa/service.js +351 -0
  208. package/esm/identity/mfa/totp.d.ts +186 -0
  209. package/esm/identity/mfa/totp.js +194 -0
  210. package/esm/identity/migration.d.ts +155 -0
  211. package/esm/identity/migration.js +243 -0
  212. package/esm/identity/mod.d.ts +65 -0
  213. package/esm/identity/mod.js +64 -0
  214. package/esm/identity/otp.d.ts +255 -0
  215. package/esm/identity/otp.js +245 -0
  216. package/esm/identity/password-policy.d.ts +45 -0
  217. package/esm/identity/password-policy.js +60 -0
  218. package/esm/identity/password.d.ts +171 -0
  219. package/esm/identity/password.js +160 -0
  220. package/esm/identity/rate-limit.d.ts +192 -0
  221. package/esm/identity/rate-limit.js +168 -0
  222. package/esm/identity/service.d.ts +579 -0
  223. package/esm/identity/service.js +835 -0
  224. package/esm/identity/session.d.ts +122 -0
  225. package/esm/identity/session.js +48 -0
  226. package/esm/identity/token-flow.d.ts +214 -0
  227. package/esm/identity/token-flow.js +173 -0
  228. package/esm/models/authentication.d.ts +24 -0
  229. package/esm/models/authentication.js +1 -0
  230. package/esm/models/authorization-code.d.ts +63 -0
  231. package/esm/models/authorization-code.js +1 -0
  232. package/esm/models/authorization.d.ts +165 -0
  233. package/esm/models/authorization.js +160 -0
  234. package/esm/models/client.d.ts +78 -0
  235. package/esm/models/client.js +1 -0
  236. package/esm/models/device-authorization.d.ts +30 -0
  237. package/esm/models/device-authorization.js +1 -0
  238. package/esm/models/responses.d.ts +93 -0
  239. package/esm/models/responses.js +1 -0
  240. package/esm/models/scope.d.ts +81 -0
  241. package/esm/models/scope.js +125 -0
  242. package/esm/models/token.d.ts +135 -0
  243. package/esm/models/token.js +1 -0
  244. package/esm/package.json +3 -0
  245. package/esm/react/callback.d.ts +52 -0
  246. package/esm/react/callback.js +71 -0
  247. package/esm/react/components/class-names.d.ts +91 -0
  248. package/esm/react/components/class-names.js +118 -0
  249. package/esm/react/components/internal.d.ts +62 -0
  250. package/esm/react/components/internal.js +102 -0
  251. package/esm/react/components/mfa-challenge-form.d.ts +66 -0
  252. package/esm/react/components/mfa-challenge-form.js +46 -0
  253. package/esm/react/components/mfa-enrollment-form.d.ts +83 -0
  254. package/esm/react/components/mfa-enrollment-form.js +41 -0
  255. package/esm/react/components/mod.d.ts +103 -0
  256. package/esm/react/components/mod.js +93 -0
  257. package/esm/react/components/request-password-reset-form.d.ts +61 -0
  258. package/esm/react/components/request-password-reset-form.js +41 -0
  259. package/esm/react/components/reset-password-form.d.ts +66 -0
  260. package/esm/react/components/reset-password-form.js +56 -0
  261. package/esm/react/components/sign-in-form.d.ts +74 -0
  262. package/esm/react/components/sign-in-form.js +43 -0
  263. package/esm/react/components/sign-up-form.d.ts +85 -0
  264. package/esm/react/components/sign-up-form.js +57 -0
  265. package/esm/react/components/types.d.ts +208 -0
  266. package/esm/react/components/types.js +14 -0
  267. package/esm/react/components/use-auth-form.d.ts +118 -0
  268. package/esm/react/components/use-auth-form.js +172 -0
  269. package/esm/react/components/user-menu.d.ts +79 -0
  270. package/esm/react/components/user-menu.js +73 -0
  271. package/esm/react/context.d.ts +51 -0
  272. package/esm/react/context.js +16 -0
  273. package/esm/react/mod.d.ts +26 -0
  274. package/esm/react/mod.js +26 -0
  275. package/esm/react/provider.d.ts +40 -0
  276. package/esm/react/provider.js +189 -0
  277. package/esm/react/require-auth.d.ts +31 -0
  278. package/esm/react/require-auth.js +37 -0
  279. package/esm/react/testing.d.ts +322 -0
  280. package/esm/react/testing.js +461 -0
  281. package/esm/react/use-authorization.d.ts +31 -0
  282. package/esm/react/use-authorization.js +36 -0
  283. package/esm/react/use-oauth2.d.ts +112 -0
  284. package/esm/react/use-oauth2.js +127 -0
  285. package/esm/server/authorization-server.d.ts +736 -0
  286. package/esm/server/authorization-server.js +1304 -0
  287. package/esm/server/client-authentication.d.ts +28 -0
  288. package/esm/server/client-authentication.js +51 -0
  289. package/esm/server/grants/authorization-code.d.ts +179 -0
  290. package/esm/server/grants/authorization-code.js +232 -0
  291. package/esm/server/grants/client-credentials.d.ts +52 -0
  292. package/esm/server/grants/client-credentials.js +53 -0
  293. package/esm/server/grants/device-authorization.d.ts +103 -0
  294. package/esm/server/grants/device-authorization.js +156 -0
  295. package/esm/server/grants/grant.d.ts +165 -0
  296. package/esm/server/grants/grant.js +173 -0
  297. package/esm/server/grants/password.d.ts +80 -0
  298. package/esm/server/grants/password.js +92 -0
  299. package/esm/server/grants/refresh-token.d.ts +80 -0
  300. package/esm/server/grants/refresh-token.js +136 -0
  301. package/esm/server/introspection-token-reader.d.ts +153 -0
  302. package/esm/server/introspection-token-reader.js +164 -0
  303. package/esm/server/jwks-token-reader.d.ts +263 -0
  304. package/esm/server/jwks-token-reader.js +508 -0
  305. package/esm/server/mod.d.ts +23 -0
  306. package/esm/server/mod.js +15 -0
  307. package/esm/server/public-suffix/mod.d.ts +62 -0
  308. package/esm/server/public-suffix/mod.js +103 -0
  309. package/esm/server/public-suffix/rules.d.ts +23 -0
  310. package/esm/server/public-suffix/rules.js +23 -0
  311. package/esm/server/redirect-uri.d.ts +187 -0
  312. package/esm/server/redirect-uri.js +331 -0
  313. package/esm/server/resource-server.d.ts +286 -0
  314. package/esm/server/resource-server.js +365 -0
  315. package/esm/server/services/authorization-code.d.ts +84 -0
  316. package/esm/server/services/authorization-code.js +35 -0
  317. package/esm/server/services/client.d.ts +35 -0
  318. package/esm/server/services/client.js +1 -0
  319. package/esm/server/services/device-authorization.d.ts +134 -0
  320. package/esm/server/services/device-authorization.js +7 -0
  321. package/esm/server/services/token.d.ts +242 -0
  322. package/esm/server/services/token.js +78 -0
  323. package/esm/server/services/user.d.ts +29 -0
  324. package/esm/server/services/user.js +7 -0
  325. package/esm/server/signing-keys.d.ts +201 -0
  326. package/esm/server/signing-keys.js +225 -0
  327. package/esm/server/utils/hash.d.ts +6 -0
  328. package/esm/server/utils/hash.js +11 -0
  329. package/esm/testing/contract/authorization-code.d.ts +37 -0
  330. package/esm/testing/contract/authorization-code.js +93 -0
  331. package/esm/testing/contract/client.d.ts +85 -0
  332. package/esm/testing/contract/client.js +126 -0
  333. package/esm/testing/contract/device-authorization.d.ts +37 -0
  334. package/esm/testing/contract/device-authorization.js +193 -0
  335. package/esm/testing/contract/lockout-store.d.ts +47 -0
  336. package/esm/testing/contract/lockout-store.js +122 -0
  337. package/esm/testing/contract/mfa-store.d.ts +50 -0
  338. package/esm/testing/contract/mfa-store.js +226 -0
  339. package/esm/testing/contract/mod.d.ts +39 -0
  340. package/esm/testing/contract/mod.js +39 -0
  341. package/esm/testing/contract/otp-store.d.ts +48 -0
  342. package/esm/testing/contract/otp-store.js +197 -0
  343. package/esm/testing/contract/rate-limit-store.d.ts +52 -0
  344. package/esm/testing/contract/rate-limit-store.js +105 -0
  345. package/esm/testing/contract/token-flow-store.d.ts +54 -0
  346. package/esm/testing/contract/token-flow-store.js +179 -0
  347. package/esm/testing/contract/token-reader.d.ts +77 -0
  348. package/esm/testing/contract/token-reader.js +78 -0
  349. package/esm/testing/contract/token.d.ts +61 -0
  350. package/esm/testing/contract/token.js +287 -0
  351. package/esm/testing/contract/user.d.ts +63 -0
  352. package/esm/testing/contract/user.js +75 -0
  353. package/esm/testing/mod.d.ts +34 -0
  354. package/esm/testing/mod.js +34 -0
  355. package/esm/testing/server.d.ts +197 -0
  356. package/esm/testing/server.js +193 -0
  357. package/esm/testing/services.d.ts +373 -0
  358. package/esm/testing/services.js +703 -0
  359. package/esm/utils/_buffer.d.ts +10 -0
  360. package/esm/utils/_buffer.js +12 -0
  361. package/esm/utils/_timing-safe.d.ts +13 -0
  362. package/esm/utils/_timing-safe.js +22 -0
  363. package/esm/utils/authentication-context.d.ts +3 -0
  364. package/esm/utils/authentication-context.js +14 -0
  365. package/esm/utils/basic-auth.d.ts +50 -0
  366. package/esm/utils/basic-auth.js +106 -0
  367. package/esm/utils/crypto.d.ts +97 -0
  368. package/esm/utils/crypto.js +162 -0
  369. package/esm/utils/pkce.d.ts +151 -0
  370. package/esm/utils/pkce.js +179 -0
  371. package/esm/utils/text.d.ts +24 -0
  372. package/esm/utils/text.js +28 -0
  373. package/esm/utils/url.d.ts +66 -0
  374. package/esm/utils/url.js +90 -0
  375. package/package.json +92 -0
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Client authentication shared by the authorization server's endpoints and by
3
+ * the grants, so every endpoint answers an unauthenticated or wrongly
4
+ * authenticated request the same way (RFC 6749 Section 2.3).
5
+ *
6
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-2.3
7
+ * @module
8
+ */
9
+ import type { ClientCredentials, ClientInterface } from "../models/client.js";
10
+ import type { ClientServiceInterface } from "./services/client.js";
11
+ /**
12
+ * Reads the credentials a request presents: HTTP Basic first (RFC 6749 Section
13
+ * 2.3.1), then `client_id`/`client_secret` in `body`, the form body the
14
+ * endpoint already parsed. An `Authorization` header that is not usable Basic
15
+ * falls through to the body, so a request that also carries body credentials
16
+ * authenticates by the method it did present.
17
+ *
18
+ * @throws {InvalidClientError} If the request presents no usable credentials.
19
+ */
20
+ export declare function extractClientCredentials(request: Request, body: FormData): ClientCredentials;
21
+ /**
22
+ * Looks up the client the credentials authenticate. Pair it with
23
+ * {@linkcode extractClientCredentials} unless the caller applies its own
24
+ * credential rules (as the authorization-code grant does for PKCE).
25
+ *
26
+ * @throws {InvalidClientError} If no client matches the credentials.
27
+ */
28
+ export declare function authenticateClientCredentials<Client extends ClientInterface, User>(credentials: ClientCredentials, clientService: ClientServiceInterface<Client, User>): Promise<Client>;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Client authentication shared by the authorization server's endpoints and by
3
+ * the grants, so every endpoint answers an unauthenticated or wrongly
4
+ * authenticated request the same way (RFC 6749 Section 2.3).
5
+ *
6
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-2.3
7
+ * @module
8
+ */
9
+ import { InvalidClientError } from "../errors.js";
10
+ import { parseBasicAuth, tryParseBasicAuth } from "../utils/basic-auth.js";
11
+ /**
12
+ * Reads the credentials a request presents: HTTP Basic first (RFC 6749 Section
13
+ * 2.3.1), then `client_id`/`client_secret` in `body`, the form body the
14
+ * endpoint already parsed. An `Authorization` header that is not usable Basic
15
+ * falls through to the body, so a request that also carries body credentials
16
+ * authenticates by the method it did present.
17
+ *
18
+ * @throws {InvalidClientError} If the request presents no usable credentials.
19
+ */
20
+ export function extractClientCredentials(request, body) {
21
+ const authorization = request.headers.get("authorization");
22
+ const basicAuth = tryParseBasicAuth(authorization);
23
+ if (basicAuth) {
24
+ return { clientId: basicAuth.name, clientSecret: basicAuth.pass };
25
+ }
26
+ const clientId = body.get("client_id");
27
+ if (typeof clientId === "string") {
28
+ const credentials = { clientId };
29
+ const clientSecret = body.get("client_secret");
30
+ if (typeof clientSecret === "string") {
31
+ credentials.clientSecret = clientSecret;
32
+ }
33
+ return credentials;
34
+ }
35
+ if (authorization)
36
+ throw parseBasicAuth(authorization);
37
+ throw new InvalidClientError("client authentication required");
38
+ }
39
+ /**
40
+ * Looks up the client the credentials authenticate. Pair it with
41
+ * {@linkcode extractClientCredentials} unless the caller applies its own
42
+ * credential rules (as the authorization-code grant does for PKCE).
43
+ *
44
+ * @throws {InvalidClientError} If no client matches the credentials.
45
+ */
46
+ export async function authenticateClientCredentials(credentials, clientService) {
47
+ const client = await clientService.getAuthenticated(credentials.clientId, credentials.clientSecret);
48
+ if (!client)
49
+ throw new InvalidClientError("client authentication failed");
50
+ return client;
51
+ }
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Authorization Code grant type implementation with PKCE support.
3
+ * RFC 6749 Section 4.1 and RFC 7636.
4
+ * @module
5
+ */
6
+ import type { AuthorizationCode } from "../../models/authorization-code.js";
7
+ import type { AuthenticationContext } from "../../models/authentication.js";
8
+ import type { ClientCredentials, ClientInterface } from "../../models/client.js";
9
+ import type { Token } from "../../models/token.js";
10
+ import type { AbstractScope, BasicScope } from "../../models/scope.js";
11
+ import type { ChallengeMethod, ChallengeMethods } from "../../utils/pkce.js";
12
+ import type { ClientServiceInterface } from "../services/client.js";
13
+ import type { AuthorizationCodeServiceInterface } from "../services/authorization-code.js";
14
+ import { AbstractGrant, type GrantOptions, type GrantServices } from "./grant.js";
15
+ /** Services required by the authorization code grant. */
16
+ export interface AuthorizationCodeGrantServices<Client extends ClientInterface, User, S extends AbstractScope> extends GrantServices<Client, User, S> {
17
+ /** Generates, stores, and looks up authorization codes for the exchange. */
18
+ authorizationCodeService: AuthorizationCodeServiceInterface<Client, User, S>;
19
+ }
20
+ /**
21
+ * Options for configuring the authorization code grant.
22
+ *
23
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1
24
+ * @see https://datatracker.ietf.org/doc/html/rfc7636
25
+ */
26
+ export interface AuthorizationCodeGrantOptions<Client extends ClientInterface, User, S extends AbstractScope> extends GrantOptions<Client, User, S, AuthorizationCodeGrantServices<Client, User, S>> {
27
+ /** Allow optional refresh token. Defaults to true. */
28
+ allowRefreshToken?: boolean;
29
+ /** Custom PKCE challenge methods. Defaults to S256 only. */
30
+ challengeMethods?: ChallengeMethods;
31
+ /**
32
+ * Whether to require PKCE for all authorization requests.
33
+ *
34
+ * Per OAuth 2.1 and security best practices, PKCE is required for all OAuth
35
+ * clients, including confidential clients. Defaults to `true`; set it to
36
+ * `false` only to interoperate with legacy clients that cannot send a
37
+ * `code_challenge`.
38
+ *
39
+ * @default true
40
+ * @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1#section-7.5.2
41
+ */
42
+ requirePKCE?: boolean;
43
+ /**
44
+ * Whether a client that presents a `code_verifier` must still authenticate
45
+ * with its client credentials.
46
+ *
47
+ * PKCE proves the token request comes from the same party that started the
48
+ * authorization request; it says nothing about *which client* that party is.
49
+ * OAuth 2.1 and RFC 7636 both treat PKCE as additional to client
50
+ * authentication, never a replacement for it, so a confidential client's
51
+ * secret is still required on this grant.
52
+ *
53
+ * Defaults to `true`: confidential clients authenticate alongside PKCE.
54
+ * Public clients, which have no secret, are unaffected. Setting `false`
55
+ * explicitly enables legacy behavior where PKCE replaces client credentials;
56
+ * use that opt-out only for an integration that requires it.
57
+ *
58
+ * @default true
59
+ * @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1#section-4.1.3
60
+ * @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.5
61
+ * @see https://datatracker.ietf.org/doc/html/rfc9700#section-2.1.1
62
+ */
63
+ requireClientAuthentication?: boolean;
64
+ }
65
+ /** Client credentials extended with optional PKCE code_verifier. */
66
+ export interface PKCEClientCredentials extends ClientCredentials {
67
+ /** The PKCE `code_verifier`, present when the client used PKCE (RFC 7636). */
68
+ codeVerifier?: string;
69
+ }
70
+ /** Options for generating an authorization code. */
71
+ export interface GenerateAuthorizationCodeOptions<Client extends ClientInterface, User, S extends AbstractScope> {
72
+ /** Verified event to capture on this code, supplied by authentication policy. */
73
+ authenticationContext?: AuthenticationContext;
74
+ /** The client the code is issued to. */
75
+ client: Client;
76
+ /** The resource owner the code authorizes. */
77
+ user: User;
78
+ /** The scope to bind to the code. */
79
+ scope?: S | null;
80
+ /** Redirect URI to bind to the code; must match again at the token exchange. */
81
+ redirectUri?: string | null;
82
+ /** PKCE `code_challenge` to bind to the code (RFC 7636). */
83
+ challenge?: string | null;
84
+ /** PKCE `code_challenge_method` (e.g. `S256`). Defaults to `S256`. */
85
+ challengeMethod?: string | null;
86
+ /** OIDC nonce to bind to the code (echoed into the id_token). */
87
+ nonce?: string | null;
88
+ }
89
+ /**
90
+ * The authorization code grant type.
91
+ *
92
+ * Implements RFC 6749 Section 4.1 with PKCE support (RFC 7636).
93
+ *
94
+ * PKCE (Proof Key for Code Exchange) is **required by default** for all
95
+ * clients, including confidential ones, per OAuth 2.1. Set `requirePKCE: false`
96
+ * only to interoperate with legacy clients that cannot send a `code_challenge`.
97
+ *
98
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1
99
+ * @see https://datatracker.ietf.org/doc/html/rfc7636
100
+ * @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-2.1.1
101
+ */
102
+ export declare class AuthorizationCodeGrant<Client extends ClientInterface, User, S extends AbstractScope = BasicScope> extends AbstractGrant<Client, User, S, AuthorizationCodeGrantServices<Client, User, S>> {
103
+ /** The OAuth2 grant type: `authorization_code`. */
104
+ readonly grantType = "authorization_code";
105
+ /** The PKCE challenge methods this grant accepts. Defaults to `S256` only. */
106
+ challengeMethods: ChallengeMethods;
107
+ /**
108
+ * Whether PKCE is required for all authorization requests.
109
+ * @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1#section-7.5.2
110
+ */
111
+ requirePKCE: boolean;
112
+ /**
113
+ * Whether a client presenting a `code_verifier` must still authenticate with
114
+ * its client credentials. Defaults to `true`.
115
+ * @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.5
116
+ */
117
+ requireClientAuthentication: boolean;
118
+ /** Creates the grant from its {@linkcode AuthorizationCodeGrantOptions}. */
119
+ constructor(options: AuthorizationCodeGrantOptions<Client, User, S>);
120
+ /**
121
+ * Extracts client credentials from a request, carrying the PKCE
122
+ * `code_verifier` alongside them when the client sent one.
123
+ *
124
+ * When {@linkcode requireClientAuthentication} is explicitly `false`, a
125
+ * `code_verifier` drops the `client_secret` from the returned credentials,
126
+ * so the client is looked up rather than authenticated.
127
+ */
128
+ protected getClientCredentials(request: Request, body: FormData): PKCEClientCredentials;
129
+ /** Gets a client by ID without authentication. */
130
+ getClient(clientId: string, clientService: ClientServiceInterface<Client, User>): Promise<Client>;
131
+ /**
132
+ * Authenticates a client from a request.
133
+ *
134
+ * Confidential clients authenticate with their registered secret by default,
135
+ * including when they send a `code_verifier`. Setting
136
+ * {@linkcode requireClientAuthentication} to `false` makes a request carrying
137
+ * a `code_verifier` look up the client by id without authenticating it.
138
+ *
139
+ * @throws {InvalidClientError} When the client is unknown or its credentials do not authenticate it.
140
+ */
141
+ getAuthenticatedClient(request: Request, body: FormData): Promise<Client>;
142
+ /**
143
+ * Resolves an allowed challenge method by name, defaulting to `S256`.
144
+ *
145
+ * Only names {@linkcode challengeMethods} holds as its own callable
146
+ * properties resolve, so a client cannot reach an inherited
147
+ * `Object.prototype` member such as `toString` by naming it as its
148
+ * `code_challenge_method`.
149
+ */
150
+ getChallengeMethod(challengeMethod?: string | null): ChallengeMethod | undefined;
151
+ /** Validates that the challenge method is allowed. */
152
+ validateChallengeMethod(challengeMethod?: string | null): boolean;
153
+ /**
154
+ * Verifies that a code verifier matches the authorization code's challenge.
155
+ * https://datatracker.ietf.org/doc/html/rfc7636#section-4.6
156
+ */
157
+ verifyCode(code: AuthorizationCode<Client, User, S>, verifier: string): Promise<boolean>;
158
+ /**
159
+ * Generates and saves an authorization code. Resolves its own
160
+ * `authorizationCodeService` from the request.
161
+ */
162
+ generateAuthorizationCode(options: GenerateAuthorizationCodeOptions<Client, User, S>, request: Request): Promise<AuthorizationCode<Client, User, S>>;
163
+ /**
164
+ * Handles a token request for the authorization code grant.
165
+ *
166
+ * Exchanges an authorization code for an access token. If PKCE was used
167
+ * during authorization, the code_verifier must be provided.
168
+ *
169
+ * The code is claimed by revoking it: only the caller whose revoke wins
170
+ * receives a token, so concurrent exchanges of one code cannot both succeed.
171
+ *
172
+ * @throws {InvalidRequestError} When `code` is missing, a required `code_verifier` is absent, or the verifier is malformed.
173
+ * @throws {InvalidGrantError} When the code is unknown, expired, already claimed, or its verifier or `redirect_uri` does not match.
174
+ * @throws {InvalidClientError} When the code was issued to another client.
175
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3
176
+ * @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.5
177
+ */
178
+ token(request: Request, client: Client, body: FormData): Promise<Token<Client, User, S>>;
179
+ }
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Authorization Code grant type implementation with PKCE support.
3
+ * RFC 6749 Section 4.1 and RFC 7636.
4
+ * @module
5
+ */
6
+ import { snapshotAuthenticationContext } from "../../utils/authentication-context.js";
7
+ import { InvalidClientError, InvalidGrantError, InvalidRequestError, ServerError, } from "../../errors.js";
8
+ import { challengeMethods as defaultChallengeMethods, getChallengeMethod, validateCodeVerifier, } from "../../utils/pkce.js";
9
+ import { authenticateClientCredentials } from "../client-authentication.js";
10
+ import { AbstractGrant, } from "./grant.js";
11
+ /**
12
+ * The authorization code grant type.
13
+ *
14
+ * Implements RFC 6749 Section 4.1 with PKCE support (RFC 7636).
15
+ *
16
+ * PKCE (Proof Key for Code Exchange) is **required by default** for all
17
+ * clients, including confidential ones, per OAuth 2.1. Set `requirePKCE: false`
18
+ * only to interoperate with legacy clients that cannot send a `code_challenge`.
19
+ *
20
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1
21
+ * @see https://datatracker.ietf.org/doc/html/rfc7636
22
+ * @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-2.1.1
23
+ */
24
+ export class AuthorizationCodeGrant extends AbstractGrant {
25
+ /** The OAuth2 grant type: `authorization_code`. */
26
+ grantType = "authorization_code";
27
+ /** The PKCE challenge methods this grant accepts. Defaults to `S256` only. */
28
+ challengeMethods;
29
+ /**
30
+ * Whether PKCE is required for all authorization requests.
31
+ * @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1#section-7.5.2
32
+ */
33
+ requirePKCE;
34
+ /**
35
+ * Whether a client presenting a `code_verifier` must still authenticate with
36
+ * its client credentials. Defaults to `true`.
37
+ * @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.5
38
+ */
39
+ requireClientAuthentication;
40
+ /** Creates the grant from its {@linkcode AuthorizationCodeGrantOptions}. */
41
+ constructor(options) {
42
+ super({
43
+ ...options,
44
+ allowRefreshToken: options.allowRefreshToken ?? true,
45
+ });
46
+ this.challengeMethods = options.challengeMethods ?? defaultChallengeMethods;
47
+ this.requirePKCE = options.requirePKCE ?? true;
48
+ this.requireClientAuthentication = options.requireClientAuthentication ??
49
+ true;
50
+ }
51
+ /**
52
+ * Extracts client credentials from a request, carrying the PKCE
53
+ * `code_verifier` alongside them when the client sent one.
54
+ *
55
+ * When {@linkcode requireClientAuthentication} is explicitly `false`, a
56
+ * `code_verifier` drops the `client_secret` from the returned credentials,
57
+ * so the client is looked up rather than authenticated.
58
+ */
59
+ getClientCredentials(request, body) {
60
+ const credentials = super.getClientCredentials(request, body);
61
+ const codeVerifier = body.get("code_verifier");
62
+ if (typeof codeVerifier === "string") {
63
+ return this.requireClientAuthentication
64
+ ? { ...credentials, codeVerifier }
65
+ : { ...credentials, codeVerifier, clientSecret: undefined };
66
+ }
67
+ return credentials;
68
+ }
69
+ /** Gets a client by ID without authentication. */
70
+ async getClient(clientId, clientService) {
71
+ const client = await clientService.get(clientId);
72
+ if (!client) {
73
+ throw new InvalidClientError("client not found");
74
+ }
75
+ return client;
76
+ }
77
+ /**
78
+ * Authenticates a client from a request.
79
+ *
80
+ * Confidential clients authenticate with their registered secret by default,
81
+ * including when they send a `code_verifier`. Setting
82
+ * {@linkcode requireClientAuthentication} to `false` makes a request carrying
83
+ * a `code_verifier` look up the client by id without authenticating it.
84
+ *
85
+ * @throws {InvalidClientError} When the client is unknown or its credentials do not authenticate it.
86
+ */
87
+ async getAuthenticatedClient(request, body) {
88
+ const { clientService } = await this.resolveServices(request);
89
+ const { clientId, clientSecret, codeVerifier } = this.getClientCredentials(request, body);
90
+ if (codeVerifier && !this.requireClientAuthentication) {
91
+ const client = await clientService.get(clientId);
92
+ if (!client) {
93
+ throw new InvalidClientError("client authentication failed");
94
+ }
95
+ return client;
96
+ }
97
+ return await authenticateClientCredentials({ clientId, clientSecret }, clientService);
98
+ }
99
+ /**
100
+ * Resolves an allowed challenge method by name, defaulting to `S256`.
101
+ *
102
+ * Only names {@linkcode challengeMethods} holds as its own callable
103
+ * properties resolve, so a client cannot reach an inherited
104
+ * `Object.prototype` member such as `toString` by naming it as its
105
+ * `code_challenge_method`.
106
+ */
107
+ getChallengeMethod(challengeMethod) {
108
+ return getChallengeMethod(this.challengeMethods, challengeMethod);
109
+ }
110
+ /** Validates that the challenge method is allowed. */
111
+ validateChallengeMethod(challengeMethod) {
112
+ return !!this.getChallengeMethod(challengeMethod);
113
+ }
114
+ /**
115
+ * Verifies that a code verifier matches the authorization code's challenge.
116
+ * https://datatracker.ietf.org/doc/html/rfc7636#section-4.6
117
+ */
118
+ async verifyCode(code, verifier) {
119
+ if (!code.challenge)
120
+ return false;
121
+ const challengeMethod = this.getChallengeMethod(code.challengeMethod);
122
+ if (challengeMethod) {
123
+ const challenge = await challengeMethod(verifier);
124
+ if (challenge !== code.challenge)
125
+ return false;
126
+ }
127
+ else {
128
+ throw new ServerError("code_challenge_method not implemented");
129
+ }
130
+ return true;
131
+ }
132
+ /**
133
+ * Generates and saves an authorization code. Resolves its own
134
+ * `authorizationCodeService` from the request.
135
+ */
136
+ async generateAuthorizationCode(options, request) {
137
+ const authenticationContext = snapshotAuthenticationContext(options.authenticationContext);
138
+ const { authorizationCodeService } = await this.resolveServices(request);
139
+ const { client, user, scope, redirectUri, challenge, challengeMethod, nonce, } = options;
140
+ const authorizationCode = {
141
+ code: await authorizationCodeService.generateCode(client, user, scope),
142
+ expiresAt: await authorizationCodeService.expiresAt(client, user, scope),
143
+ client,
144
+ user,
145
+ };
146
+ if (scope)
147
+ authorizationCode.scope = scope;
148
+ if (redirectUri)
149
+ authorizationCode.redirectUri = redirectUri;
150
+ if (challenge)
151
+ authorizationCode.challenge = challenge;
152
+ if (challengeMethod)
153
+ authorizationCode.challengeMethod = challengeMethod;
154
+ if (nonce)
155
+ authorizationCode.nonce = nonce;
156
+ if (authenticationContext) {
157
+ authorizationCode.authenticationContext = authenticationContext;
158
+ }
159
+ return await authorizationCodeService.save(authorizationCode);
160
+ }
161
+ /**
162
+ * Handles a token request for the authorization code grant.
163
+ *
164
+ * Exchanges an authorization code for an access token. If PKCE was used
165
+ * during authorization, the code_verifier must be provided.
166
+ *
167
+ * The code is claimed by revoking it: only the caller whose revoke wins
168
+ * receives a token, so concurrent exchanges of one code cannot both succeed.
169
+ *
170
+ * @throws {InvalidRequestError} When `code` is missing, a required `code_verifier` is absent, or the verifier is malformed.
171
+ * @throws {InvalidGrantError} When the code is unknown, expired, already claimed, or its verifier or `redirect_uri` does not match.
172
+ * @throws {InvalidClientError} When the code was issued to another client.
173
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3
174
+ * @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.5
175
+ */
176
+ async token(request, client, body) {
177
+ const { authorizationCodeService, tokenService } = await this
178
+ .resolveServices(request);
179
+ const code = body.get("code");
180
+ if (typeof code !== "string") {
181
+ throw new InvalidRequestError("code parameter required");
182
+ }
183
+ // RFC 6819 §4.4.1.1: code replay revokes already-issued tokens and rejects.
184
+ if (await tokenService.revokeCode(code)) {
185
+ throw new InvalidGrantError("code already used");
186
+ }
187
+ const authorizationCode = await authorizationCodeService.get(code);
188
+ if (!authorizationCode || authorizationCode.expiresAt < new Date()) {
189
+ throw new InvalidGrantError("invalid code");
190
+ }
191
+ if (!await authorizationCodeService.revoke(authorizationCode)) {
192
+ throw new InvalidGrantError("code already used");
193
+ }
194
+ const codeVerifier = body.get("code_verifier");
195
+ if (typeof codeVerifier === "string") {
196
+ if (!validateCodeVerifier(codeVerifier)) {
197
+ throw new InvalidRequestError("code_verifier must be 43-128 characters using [A-Z] / [a-z] / [0-9] / - / . / _ / ~");
198
+ }
199
+ if (!await this.verifyCode(authorizationCode, codeVerifier)) {
200
+ throw new InvalidGrantError("code_verifier verification failed");
201
+ }
202
+ }
203
+ else if (authorizationCode.challenge) {
204
+ throw new InvalidRequestError("code_verifier required");
205
+ }
206
+ else if (this.requirePKCE) {
207
+ throw new InvalidRequestError("PKCE is required for this grant");
208
+ }
209
+ const { client: codeClient, user, scope, redirectUri: expectedRedirectUri, } = authorizationCode;
210
+ if (client.id.toString() !== codeClient.id.toString()) {
211
+ throw new InvalidClientError("code was issued to another client");
212
+ }
213
+ const redirectUri = body.get("redirect_uri");
214
+ if (expectedRedirectUri) {
215
+ if (typeof redirectUri !== "string") {
216
+ throw new InvalidGrantError("redirect_uri parameter required");
217
+ }
218
+ else if (redirectUri !== expectedRedirectUri) {
219
+ throw new InvalidGrantError("incorrect redirect_uri");
220
+ }
221
+ }
222
+ else if (redirectUri) {
223
+ throw new InvalidGrantError("did not expect redirect_uri parameter");
224
+ }
225
+ const token = await this.generateToken(client, user, scope, tokenService, authorizationCode.authenticationContext);
226
+ token.code = code;
227
+ const saved = await tokenService.save(token);
228
+ if (authorizationCode.nonce)
229
+ saved.nonce = authorizationCode.nonce;
230
+ return saved;
231
+ }
232
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Client Credentials grant type implementation.
3
+ * RFC 6749 Section 4.4.
4
+ * @module
5
+ */
6
+ import type { ClientInterface } from "../../models/client.js";
7
+ import type { Token } from "../../models/token.js";
8
+ import type { AbstractScope, BasicScope } from "../../models/scope.js";
9
+ import { AbstractGrant, type GrantOptions, type GrantServices } from "./grant.js";
10
+ /** Services required by the client credentials grant. */
11
+ export type ClientCredentialsGrantServices<Client extends ClientInterface, User, S extends AbstractScope> = GrantServices<Client, User, S>;
12
+ /**
13
+ * Options for configuring the client credentials grant. This grant never issues
14
+ * a refresh token (RFC 6749 §4.4.3), so `allowRefreshToken` is not offered.
15
+ */
16
+ export type ClientCredentialsGrantOptions<Client extends ClientInterface, User, S extends AbstractScope> = Omit<GrantOptions<Client, User, S, ClientCredentialsGrantServices<Client, User, S>>, "allowRefreshToken">;
17
+ /**
18
+ * The client credentials grant type.
19
+ * https://datatracker.ietf.org/doc/html/rfc6749.html#section-4.4
20
+ *
21
+ * Used for machine-to-machine authentication where the client itself
22
+ * is the resource owner. Does not support refresh tokens.
23
+ *
24
+ * **The token need not carry a user.** RFC 6749 §4.4 has no resource owner, so
25
+ * a `ClientServiceInterface.getUser` that resolves nothing is the conformant
26
+ * case, not an error: the grant issues a token whose {@linkcode Token.user} is
27
+ * unset, and `createJwtAccessTokenGenerator` names the client as its subject
28
+ * (RFC 9068 §2.2). Resolving a human owner instead makes the machine
29
+ * indistinguishable from that person — the _Client Impersonating Resource
30
+ * Owner_ attack of RFC 9700 §4.15 — so map a client to a user only when that
31
+ * user is a principal of its own, such as a per-application service account.
32
+ */
33
+ export declare class ClientCredentialsGrant<Client extends ClientInterface, User, S extends AbstractScope = BasicScope> extends AbstractGrant<Client, User, S, ClientCredentialsGrantServices<Client, User, S>> {
34
+ /** The OAuth2 grant type: `client_credentials`. */
35
+ readonly grantType = "client_credentials";
36
+ /** Machine flow — no end-user authentication event, so no id_token. */
37
+ readonly issuesIdToken = false;
38
+ /** Creates the grant from its {@linkcode ClientCredentialsGrantOptions}. */
39
+ constructor(options: ClientCredentialsGrantOptions<Client, User, S>);
40
+ /**
41
+ * Handles a token request for the client credentials grant. Issues an access
42
+ * token (never a refresh token) to the client acting as its own resource
43
+ * owner.
44
+ *
45
+ * The token's {@linkcode Token.user} is whatever
46
+ * `ClientServiceInterface.getUser` resolves for the client, and is left
47
+ * unset when it resolves nothing — a user-less machine token, not a failure.
48
+ *
49
+ * @throws {InvalidScopeError} If the requested scope is rejected.
50
+ */
51
+ token(request: Request, client: Client, body: FormData): Promise<Token<Client, User, S>>;
52
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Client Credentials grant type implementation.
3
+ * RFC 6749 Section 4.4.
4
+ * @module
5
+ */
6
+ import { AbstractGrant, } from "./grant.js";
7
+ /**
8
+ * The client credentials grant type.
9
+ * https://datatracker.ietf.org/doc/html/rfc6749.html#section-4.4
10
+ *
11
+ * Used for machine-to-machine authentication where the client itself
12
+ * is the resource owner. Does not support refresh tokens.
13
+ *
14
+ * **The token need not carry a user.** RFC 6749 §4.4 has no resource owner, so
15
+ * a `ClientServiceInterface.getUser` that resolves nothing is the conformant
16
+ * case, not an error: the grant issues a token whose {@linkcode Token.user} is
17
+ * unset, and `createJwtAccessTokenGenerator` names the client as its subject
18
+ * (RFC 9068 §2.2). Resolving a human owner instead makes the machine
19
+ * indistinguishable from that person — the _Client Impersonating Resource
20
+ * Owner_ attack of RFC 9700 §4.15 — so map a client to a user only when that
21
+ * user is a principal of its own, such as a per-application service account.
22
+ */
23
+ export class ClientCredentialsGrant extends AbstractGrant {
24
+ /** The OAuth2 grant type: `client_credentials`. */
25
+ grantType = "client_credentials";
26
+ /** Machine flow — no end-user authentication event, so no id_token. */
27
+ issuesIdToken = false;
28
+ /** Creates the grant from its {@linkcode ClientCredentialsGrantOptions}. */
29
+ constructor(options) {
30
+ super({ ...options, allowRefreshToken: false });
31
+ }
32
+ /**
33
+ * Handles a token request for the client credentials grant. Issues an access
34
+ * token (never a refresh token) to the client acting as its own resource
35
+ * owner.
36
+ *
37
+ * The token's {@linkcode Token.user} is whatever
38
+ * `ClientServiceInterface.getUser` resolves for the client, and is left
39
+ * unset when it resolves nothing — a user-less machine token, not a failure.
40
+ *
41
+ * @throws {InvalidScopeError} If the requested scope is rejected.
42
+ */
43
+ async token(request, client, body) {
44
+ const { tokenService, clientService } = await this.resolveServices(request);
45
+ const scopeText = body.get("scope");
46
+ let scope = this.parseScope(typeof scopeText === "string" ? scopeText : undefined);
47
+ const user = await clientService.getUser(client);
48
+ const acceptedScope = await this.acceptedScope(client, user, scope, tokenService);
49
+ scope = acceptedScope ?? undefined;
50
+ const token = await this.generateToken(client, user, scope, tokenService);
51
+ return await tokenService.save(token);
52
+ }
53
+ }