@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,184 @@
1
+ /**
2
+ * The surface {@link BffClient} and {@link DirectClient} share: subscribing to
3
+ * authentication events, starting and ending a login, reading the current
4
+ * session, and issuing authenticated requests.
5
+ *
6
+ * Program against {@link OAuth2ClientBase} when the code shouldn't care which
7
+ * transport holds the tokens — the React adapter's `<OAuth2Provider>` does
8
+ * exactly this. Reach for the concrete class when you need transport-specific
9
+ * capability: {@link BffClient.loginContinuation} or
10
+ * {@link DirectClient.getAccessToken}.
11
+ *
12
+ * @module
13
+ */
14
+ import { type OAuth2ClientEvent, type OAuth2ClientEventListener } from "./events.js";
15
+ /** Generic user-info claims returned by the userinfo or `id_token` helpers. */
16
+ export type UserInfoClaims = Record<string, unknown>;
17
+ /**
18
+ * The headers a wrapped `fetch` should send for `(input, init)`: the ones
19
+ * already on a `Request` input, overlaid with `init.headers`.
20
+ *
21
+ * Both client `fetch` wrappers need this because passing `headers` in an init
22
+ * *replaces* a `Request` input's header list rather than adding to it, which
23
+ * silently drops every header a caller set on the `Request`.
24
+ */
25
+ export declare function mergedHeaders(input: RequestInfo | URL, init: RequestInit): Headers;
26
+ /** Options accepted by every client. */
27
+ export interface BaseOptions {
28
+ /**
29
+ * Optional fetch implementation. Defaults to `globalThis.fetch`.
30
+ *
31
+ * Inject a custom `fetch` for:
32
+ *
33
+ * - **Co-located auth servers** — pass `localAuthServerFetch(authServer)`
34
+ * to dispatch token-endpoint calls directly into an in-process
35
+ * `AuthorizationServer` without a socket round-trip.
36
+ * - **Tests** — pass the `fetch` from `createMemoryAuthorizationServer()`
37
+ * (or any other `fetch`-compatible responder) so the client talks to
38
+ * an in-memory server. Preferred over stubbing `globalThis.fetch`,
39
+ * which would also intercept unrelated outbound requests the app may
40
+ * make.
41
+ */
42
+ fetch?: typeof fetch;
43
+ }
44
+ /**
45
+ * The `login` options every client understands.
46
+ *
47
+ * Named apart from {@link LoginOptions} — which keeps the richer
48
+ * `DirectClient` shape it has always had — so that a consumer importing
49
+ * `LoginOptions` and passing `scope` still compiles. Reach for this one only
50
+ * when writing against {@link OAuth2ClientBase}.
51
+ */
52
+ export interface BaseLoginOptions {
53
+ /** Path or URL to return to after a successful login. */
54
+ returnTo?: string;
55
+ }
56
+ /** Options every client's `logout` accepts. */
57
+ export interface LogoutOptions {
58
+ /** Path or URL to return to after logout. */
59
+ returnTo?: string;
60
+ }
61
+ /** A URL the caller must navigate the browser to. */
62
+ export interface LoginRedirect {
63
+ /** Where to send the browser to start (or resume) sign-in. */
64
+ url: string;
65
+ }
66
+ /**
67
+ * Where to send the browser to finish signing out, if anywhere. `url` is
68
+ * absent when the sign-out completed locally with nothing left to visit.
69
+ */
70
+ export interface LogoutRedirect {
71
+ /** The logout / end-session URL, when the flow needs a redirect. */
72
+ url?: string;
73
+ }
74
+ /**
75
+ * The signed-out {@link SessionState}, frozen because it is returned by
76
+ * reference from every client's `getSession`.
77
+ */
78
+ export declare const SIGNED_OUT: Readonly<SessionState>;
79
+ /** A snapshot of who is signed in, as the client currently understands it. */
80
+ export interface SessionState {
81
+ /** True iff a session is active. */
82
+ isAuthenticated: boolean;
83
+ /** Cached user claims, or `null`. */
84
+ user: UserInfoClaims | null;
85
+ /**
86
+ * Seconds until the session's access token expires, or `null` when unknown.
87
+ * A UI can use it to schedule {@link OAuth2ClientBase.renewSession} before
88
+ * expiry.
89
+ */
90
+ sessionExpiresIn: number | null;
91
+ /**
92
+ * A URL that ends the session, or `null` when the client has no such URL to
93
+ * offer (a direct client revokes locally instead — see
94
+ * {@link OAuth2ClientBase.logout}).
95
+ */
96
+ logoutUrl: string | null;
97
+ }
98
+ /**
99
+ * Base class for the OAuth2 clients. Owns the event bus and the resolved
100
+ * `fetch`; subclasses supply the transport.
101
+ *
102
+ * Not constructible on its own — build a {@link BffClient} (tokens live
103
+ * server-side behind a session cookie) or a {@link DirectClient} (this
104
+ * process holds the tokens) and pass it wherever an `OAuth2ClientBase` is
105
+ * accepted.
106
+ *
107
+ * @example Accept either client
108
+ * ```ts
109
+ * import type { OAuth2ClientBase } from "@udibo/oauth2/client";
110
+ *
111
+ * async function greet(client: OAuth2ClientBase): Promise<string> {
112
+ * const { isAuthenticated, user } = await client.getSession();
113
+ * return isAuthenticated ? `hello ${user?.sub}` : "hello stranger";
114
+ * }
115
+ * ```
116
+ */
117
+ export declare abstract class OAuth2ClientBase {
118
+ #private;
119
+ /**
120
+ * Called by the concrete clients; there is nothing to construct here on its
121
+ * own.
122
+ *
123
+ * @param options Only `fetch` is read at this level — subclasses consume
124
+ * the rest of their own options.
125
+ */
126
+ protected constructor(options?: BaseOptions);
127
+ /**
128
+ * Subscribes to `authenticated` / `logged_out` / `token_refreshed` /
129
+ * `error` events. Returns a function that removes the listener.
130
+ *
131
+ * @param listener Called for every event, in subscription order. One
132
+ * listener throwing does not stop delivery to the rest.
133
+ */
134
+ subscribe(listener: OAuth2ClientEventListener): () => void;
135
+ /** Publishes an event to every subscriber. */
136
+ protected emit(event: OAuth2ClientEvent): void;
137
+ /**
138
+ * The configured fetch, resolved at call time so the client picks up a
139
+ * polyfilled global if the runtime installs one after construction.
140
+ */
141
+ protected fetchImpl(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
142
+ /**
143
+ * Builds the URL that starts sign-in. The caller navigates the browser
144
+ * there; nothing is signed in until that round trip completes.
145
+ */
146
+ abstract login(options?: BaseLoginOptions): Promise<LoginRedirect>;
147
+ /**
148
+ * Ends the session and returns the URL to finish signing out, if the flow
149
+ * needs one. Emits `logged_out`.
150
+ */
151
+ abstract logout(options?: LogoutOptions): Promise<LogoutRedirect>;
152
+ /**
153
+ * The claims of the signed-in user, or `null` when nobody is signed in.
154
+ * Use {@link getSession} when you also need expiry or the logout URL.
155
+ */
156
+ abstract getUser(): Promise<UserInfoClaims | null>;
157
+ /**
158
+ * The current {@link SessionState}, as this client can determine it.
159
+ *
160
+ * **Never rejects.** A transport failure, an unreadable response, or claims
161
+ * that cannot be decoded all report a signed-out session and emit an `error`
162
+ * event instead of throwing, so a caller holding an `OAuth2ClientBase` needs
163
+ * no per-subclass `try`/`catch` to ask who is signed in. Rejection is
164
+ * reserved for a programming error, never a remote one.
165
+ */
166
+ abstract getSession(): Promise<SessionState>;
167
+ /**
168
+ * Brings the session up to date — renewing credentials where this client
169
+ * owns them — and returns the resulting {@link SessionState}. Call it
170
+ * shortly before {@link SessionState.sessionExpiresIn} elapses.
171
+ *
172
+ * **Never rejects**, on the same terms as {@link getSession}. Because this
173
+ * runs on a background timer, a transport failure emits no `error` event
174
+ * either: a blip that will simply be retried must not surface as a
175
+ * user-visible error over a session that is still valid. A session that has
176
+ * genuinely ended still emits `logged_out`.
177
+ */
178
+ abstract renewSession(): Promise<SessionState>;
179
+ /**
180
+ * Drop-in `fetch` that authenticates the request the way this client's
181
+ * transport requires, and recovers from a `401` where it can.
182
+ */
183
+ abstract fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
184
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * The surface {@link BffClient} and {@link DirectClient} share: subscribing to
3
+ * authentication events, starting and ending a login, reading the current
4
+ * session, and issuing authenticated requests.
5
+ *
6
+ * Program against {@link OAuth2ClientBase} when the code shouldn't care which
7
+ * transport holds the tokens — the React adapter's `<OAuth2Provider>` does
8
+ * exactly this. Reach for the concrete class when you need transport-specific
9
+ * capability: {@link BffClient.loginContinuation} or
10
+ * {@link DirectClient.getAccessToken}.
11
+ *
12
+ * @module
13
+ */
14
+ import { EventBus, } from "./events.js";
15
+ /**
16
+ * The headers a wrapped `fetch` should send for `(input, init)`: the ones
17
+ * already on a `Request` input, overlaid with `init.headers`.
18
+ *
19
+ * Both client `fetch` wrappers need this because passing `headers` in an init
20
+ * *replaces* a `Request` input's header list rather than adding to it, which
21
+ * silently drops every header a caller set on the `Request`.
22
+ */
23
+ export function mergedHeaders(input, init) {
24
+ const headers = new Headers(input instanceof Request ? input.headers : undefined);
25
+ for (const [name, value] of new Headers(init.headers)) {
26
+ headers.set(name, value);
27
+ }
28
+ return headers;
29
+ }
30
+ /**
31
+ * The signed-out {@link SessionState}, frozen because it is returned by
32
+ * reference from every client's `getSession`.
33
+ */
34
+ export const SIGNED_OUT = Object.freeze({
35
+ isAuthenticated: false,
36
+ user: null,
37
+ sessionExpiresIn: null,
38
+ logoutUrl: null,
39
+ });
40
+ /**
41
+ * Base class for the OAuth2 clients. Owns the event bus and the resolved
42
+ * `fetch`; subclasses supply the transport.
43
+ *
44
+ * Not constructible on its own — build a {@link BffClient} (tokens live
45
+ * server-side behind a session cookie) or a {@link DirectClient} (this
46
+ * process holds the tokens) and pass it wherever an `OAuth2ClientBase` is
47
+ * accepted.
48
+ *
49
+ * @example Accept either client
50
+ * ```ts
51
+ * import type { OAuth2ClientBase } from "@udibo/oauth2/client";
52
+ *
53
+ * async function greet(client: OAuth2ClientBase): Promise<string> {
54
+ * const { isAuthenticated, user } = await client.getSession();
55
+ * return isAuthenticated ? `hello ${user?.sub}` : "hello stranger";
56
+ * }
57
+ * ```
58
+ */
59
+ export class OAuth2ClientBase {
60
+ #events = new EventBus();
61
+ #customFetch;
62
+ /**
63
+ * Called by the concrete clients; there is nothing to construct here on its
64
+ * own.
65
+ *
66
+ * @param options Only `fetch` is read at this level — subclasses consume
67
+ * the rest of their own options.
68
+ */
69
+ constructor(options = {}) {
70
+ this.#customFetch = options.fetch;
71
+ }
72
+ /**
73
+ * Subscribes to `authenticated` / `logged_out` / `token_refreshed` /
74
+ * `error` events. Returns a function that removes the listener.
75
+ *
76
+ * @param listener Called for every event, in subscription order. One
77
+ * listener throwing does not stop delivery to the rest.
78
+ */
79
+ subscribe(listener) {
80
+ return this.#events.subscribe(listener);
81
+ }
82
+ /** Publishes an event to every subscriber. */
83
+ emit(event) {
84
+ this.#events.emit(event);
85
+ }
86
+ /**
87
+ * The configured fetch, resolved at call time so the client picks up a
88
+ * polyfilled global if the runtime installs one after construction.
89
+ */
90
+ fetchImpl(input, init) {
91
+ return (this.#customFetch ?? globalThis.fetch)(input, init);
92
+ }
93
+ }
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Browser client for an app fronted by a Backend-for-Frontend.
3
+ *
4
+ * The BFF holds the tokens; this client only ever talks to the BFF's
5
+ * `/auth/*` routes with the session cookie attached, so no token reaches the
6
+ * browser. Pair it with the `@udibo/oauth2/hono/bff` adapter, or any BFF
7
+ * serving the same four routes.
8
+ *
9
+ * @module
10
+ */
11
+ import { type BaseLoginOptions, type BaseOptions, type LoginRedirect, type LogoutOptions, type LogoutRedirect, OAuth2ClientBase, type SessionState, type UserInfoClaims } from "./base.js";
12
+ /** Paths (or absolute URLs) for the BFF session endpoints. */
13
+ export interface BffEndpoints {
14
+ /** Base URL the BFF is mounted under (e.g. `""` for same-origin). */
15
+ baseUrl?: string;
16
+ /** Path to the BFF's login route. Defaults to `/auth/login`. */
17
+ login?: string;
18
+ /**
19
+ * Path to the BFF's logout route. Defaults to `/auth/logout`.
20
+ *
21
+ * {@link BffClient.logout} only builds this URL; the caller navigates to it,
22
+ * so sign-out travels the BFF's `GET` route and is guarded by its
23
+ * same-origin check rather than the `x-csrf` header. Post to this path
24
+ * yourself, with that header, for the stronger check.
25
+ */
26
+ logout?: string;
27
+ /** Path to `GET /auth/session`. Defaults to `/auth/session`. */
28
+ session?: string;
29
+ }
30
+ /** Options accepted by {@link BffClient}. */
31
+ export interface BffClientOptions extends BaseOptions {
32
+ /** Override the default `/auth/*` BFF session endpoint paths. */
33
+ endpoints?: BffEndpoints;
34
+ /**
35
+ * Path (or URL) of the authorization endpoint the BFF redirects to (e.g.
36
+ * `"/api/oauth2/authorize"`). Lets {@link BffClient.loginContinuation}
37
+ * recognize an in-flight authorize URL to resume. Optional — without it,
38
+ * `loginContinuation` always starts a fresh login.
39
+ */
40
+ authorizePath?: string;
41
+ /**
42
+ * Custom header sent on credentialed BFF requests — the `GET /auth/session`
43
+ * probe and the wrapped {@link BffClient.fetch} — so the BFF's CSRF guard
44
+ * accepts them. The header makes the request "non-simple", forcing a CORS
45
+ * preflight a cross-site page cannot pass.
46
+ *
47
+ * Defaults to `{ name: "x-csrf", value: "1" }`, matching `HonoBff`'s
48
+ * default. Set `false` to send no header (only if the BFF disables its
49
+ * CSRF check).
50
+ */
51
+ csrfHeader?: {
52
+ name?: string;
53
+ value?: string;
54
+ } | false;
55
+ }
56
+ /**
57
+ * OAuth2 client for a browser app whose sessions live in a
58
+ * Backend-for-Frontend.
59
+ *
60
+ * Every method here is a same-origin call to the BFF with `credentials:
61
+ * "include"`, never a call to the authorization server. There is no token
62
+ * storage, no PKCE, and no access token to read — if you need one, the app
63
+ * wants a {@link DirectClient} instead.
64
+ *
65
+ * @example Same-origin BFF
66
+ * ```ts
67
+ * import { BffClient } from "@udibo/oauth2/client";
68
+ *
69
+ * const client = new BffClient();
70
+ *
71
+ * const session = await client.getSession();
72
+ * if (!session.isAuthenticated) {
73
+ * const { url } = await client.login({ returnTo: location.pathname });
74
+ * location.assign(url);
75
+ * }
76
+ *
77
+ * const res = await client.fetch("/api/items");
78
+ * ```
79
+ *
80
+ * @example BFF mounted elsewhere, with a custom CSRF header
81
+ * ```ts
82
+ * import { BffClient } from "@udibo/oauth2/client";
83
+ *
84
+ * const client = new BffClient({
85
+ * endpoints: { baseUrl: "https://api.example.com", login: "/session/start" },
86
+ * csrfHeader: { name: "x-requested-with", value: "spa" },
87
+ * });
88
+ * ```
89
+ */
90
+ export declare class BffClient extends OAuth2ClientBase {
91
+ #private;
92
+ /**
93
+ * Builds a client against the BFF's `/auth/*` routes.
94
+ *
95
+ * @param options Endpoint overrides, the CSRF header to send, and the
96
+ * `fetch` to use. Every field is optional; the defaults match `HonoBff`
97
+ * mounted at `/auth` on the same origin.
98
+ */
99
+ constructor(options?: BffClientOptions);
100
+ /**
101
+ * Builds the BFF login URL. Navigate the browser there; the BFF runs the
102
+ * authorization-code flow and drops the session cookie before redirecting
103
+ * back to `returnTo`.
104
+ *
105
+ * @param options `returnTo` is passed through as the `return_to` query
106
+ * parameter. The BFF is responsible for validating it against open
107
+ * redirects.
108
+ * @returns The URL to navigate to.
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * import { BffClient } from "@udibo/oauth2/client";
113
+ *
114
+ * const client = new BffClient();
115
+ * const { url } = await client.login({ returnTo: "/dashboard" });
116
+ * location.assign(url);
117
+ * ```
118
+ */
119
+ login(options?: BaseLoginOptions): Promise<LoginRedirect>;
120
+ /**
121
+ * Builds the BFF logout URL and emits `logged_out` so the UI can render
122
+ * signed-out immediately. Navigate the browser to the returned URL; the BFF
123
+ * destroys the session and clears the cookie.
124
+ *
125
+ * @param options `returnTo` is passed through as the `return_to` query
126
+ * parameter.
127
+ * @returns The URL to navigate to.
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * import { BffClient } from "@udibo/oauth2/client";
132
+ *
133
+ * const client = new BffClient();
134
+ * const { url } = await client.logout({ returnTo: "/" });
135
+ * location.assign(url!);
136
+ * ```
137
+ */
138
+ logout(options?: LogoutOptions): Promise<LogoutRedirect>;
139
+ /**
140
+ * Probes `GET /auth/session` and returns the full {@link SessionState} —
141
+ * `isAuthenticated`, `user`, plus the `sessionExpiresIn` and `logoutUrl` a
142
+ * SPA uses to schedule a re-probe and render a sign-out link.
143
+ *
144
+ * **Never rejects**, per {@link OAuth2ClientBase.getSession}: an
145
+ * unreachable BFF, a non-OK status, and a body that is not a JSON object
146
+ * (an HTML error page from a misrouted path is the common one) all read as
147
+ * signed out. A failure that was not simply "nobody is signed in" — anything
148
+ * other than a `401` — also emits an `error` event so a UI can tell the two
149
+ * apart. The read is bounded and deadlined like every other call this
150
+ * package makes.
151
+ *
152
+ * Only `isAuthenticated: true` carries `user` / `sessionExpiresIn` /
153
+ * `logoutUrl` through; an anonymous payload always reports them as `null`,
154
+ * whatever the BFF put in the body.
155
+ *
156
+ * @returns The session as the BFF reports it. Tokens are never included.
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * import { BffClient } from "@udibo/oauth2/client";
161
+ *
162
+ * const { isAuthenticated, logoutUrl } = await new BffClient().getSession();
163
+ * ```
164
+ */
165
+ getSession(): Promise<SessionState>;
166
+ /**
167
+ * The signed-in user's claims from the session probe, or `null`.
168
+ *
169
+ * @returns The `user` field of {@link getSession}.
170
+ */
171
+ getUser(): Promise<UserInfoClaims | null>;
172
+ /**
173
+ * Re-probes `GET /auth/session`.
174
+ *
175
+ * The BFF owns credential renewal — its `attachToken` middleware refreshes
176
+ * the access token server-side when a proxied call needs one — so this
177
+ * reports the current state rather than driving a refresh itself. Call it
178
+ * shortly before {@link SessionState.sessionExpiresIn} elapses to keep the
179
+ * UI honest about whether the session is still alive.
180
+ *
181
+ * Emits no `error` event, per {@link OAuth2ClientBase.renewSession}: this
182
+ * runs on a background timer, where a failed probe means "ask again shortly",
183
+ * not "show the user a problem".
184
+ *
185
+ * @returns The refreshed {@link SessionState}.
186
+ */
187
+ renewSession(): Promise<SessionState>;
188
+ /**
189
+ * Fetches with the session cookie and the CSRF header attached, so the BFF
190
+ * authenticates the call and its CSRF guard accepts it.
191
+ *
192
+ * A `401` emits `logged_out` with reason `session_expired` and is returned
193
+ * as-is — the BFF, not the browser, decides whether a refresh is possible.
194
+ *
195
+ * @param input Request target, as for `fetch`. A `Request` keeps the headers
196
+ * and body it was built with.
197
+ * @param init Request options. A caller-set CSRF header is left alone, and
198
+ * `init.headers` override same-named headers on a `Request` input.
199
+ * @returns The BFF's response, unmodified.
200
+ *
201
+ * @example
202
+ * ```ts
203
+ * import { BffClient } from "@udibo/oauth2/client";
204
+ *
205
+ * const res = await new BffClient().fetch("/api/items");
206
+ * ```
207
+ */
208
+ fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
209
+ /**
210
+ * Builds the URL that (re)starts sign-in toward `returnTo`, for an app whose
211
+ * own login surface (a sign-in form, create-account, forgot-password) hands
212
+ * back to the BFF flow after authenticating a user: resume an in-flight
213
+ * authorize URL, or start a fresh BFF login.
214
+ *
215
+ * Pure and synchronous — safe in render. The mirror of
216
+ * `HonoBff.loginContinuation`, so both sides of the app agree on where
217
+ * "sign in" goes.
218
+ *
219
+ * Recognizing an in-flight authorize URL requires `authorizePath` in the
220
+ * constructor options; without it every `returnTo` starts a fresh login.
221
+ *
222
+ * @param returnTo Where the user was headed. `null` / `undefined` fall back
223
+ * to `/`.
224
+ * @returns The URL to redirect to.
225
+ *
226
+ * @example
227
+ * ```ts
228
+ * import { BffClient } from "@udibo/oauth2/client";
229
+ *
230
+ * const client = new BffClient({ authorizePath: "/api/oauth2/authorize" });
231
+ * const href = client.loginContinuation("/api/oauth2/authorize?client_id=a");
232
+ * ```
233
+ */
234
+ loginContinuation(returnTo: string | null | undefined): string;
235
+ }