@atproto/oauth-provider 0.19.9 → 0.20.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 (308) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/account/account-manager.d.ts +11 -11
  3. package/dist/account/account-manager.d.ts.map +1 -1
  4. package/dist/account/account-manager.js.map +1 -1
  5. package/dist/account/account-store.d.ts +5 -10
  6. package/dist/account/account-store.d.ts.map +1 -1
  7. package/dist/account/account-store.js +0 -5
  8. package/dist/account/account-store.js.map +1 -1
  9. package/dist/client/client-auth.d.ts +1 -1
  10. package/dist/client/client-auth.d.ts.map +1 -1
  11. package/dist/client/client-auth.js.map +1 -1
  12. package/dist/client/client-data.d.ts +2 -2
  13. package/dist/client/client-data.d.ts.map +1 -1
  14. package/dist/client/client-data.js.map +1 -1
  15. package/dist/client/client-id.d.ts +1 -1
  16. package/dist/client/client-id.d.ts.map +1 -1
  17. package/dist/client/client-id.js.map +1 -1
  18. package/dist/client/client-manager.d.ts +8 -8
  19. package/dist/client/client-manager.d.ts.map +1 -1
  20. package/dist/client/client-manager.js +1 -1
  21. package/dist/client/client-manager.js.map +1 -1
  22. package/dist/client/client-store.d.ts +4 -6
  23. package/dist/client/client-store.d.ts.map +1 -1
  24. package/dist/client/client-store.js +0 -3
  25. package/dist/client/client-store.js.map +1 -1
  26. package/dist/client/client-utils.d.ts +1 -1
  27. package/dist/client/client-utils.d.ts.map +1 -1
  28. package/dist/client/client-utils.js.map +1 -1
  29. package/dist/client/client.d.ts +6 -6
  30. package/dist/client/client.d.ts.map +1 -1
  31. package/dist/client/client.js +1 -1
  32. package/dist/client/client.js.map +1 -1
  33. package/dist/customization/branding.d.ts +1 -1
  34. package/dist/customization/branding.d.ts.map +1 -1
  35. package/dist/customization/branding.js.map +1 -1
  36. package/dist/customization/build-customization-css.d.ts +1 -1
  37. package/dist/customization/build-customization-css.d.ts.map +1 -1
  38. package/dist/customization/build-customization-css.js.map +1 -1
  39. package/dist/customization/customization.d.ts +1 -1
  40. package/dist/customization/customization.d.ts.map +1 -1
  41. package/dist/customization/customization.js.map +1 -1
  42. package/dist/device/device-data.d.ts +2 -0
  43. package/dist/device/device-data.d.ts.map +1 -1
  44. package/dist/device/device-data.js.map +1 -1
  45. package/dist/device/device-id.js +1 -1
  46. package/dist/device/device-id.js.map +1 -1
  47. package/dist/device/device-manager.d.ts +3 -3
  48. package/dist/device/device-manager.d.ts.map +1 -1
  49. package/dist/device/device-manager.js +1 -1
  50. package/dist/device/device-manager.js.map +1 -1
  51. package/dist/device/device-store.d.ts +5 -7
  52. package/dist/device/device-store.d.ts.map +1 -1
  53. package/dist/device/device-store.js +0 -4
  54. package/dist/device/device-store.js.map +1 -1
  55. package/dist/device/session-id.d.ts.map +1 -1
  56. package/dist/device/session-id.js +1 -1
  57. package/dist/device/session-id.js.map +1 -1
  58. package/dist/dpop/dpop-manager.d.ts +2 -2
  59. package/dist/dpop/dpop-manager.d.ts.map +1 -1
  60. package/dist/dpop/dpop-manager.js +1 -1
  61. package/dist/dpop/dpop-manager.js.map +1 -1
  62. package/dist/dpop/dpop-nonce.js +1 -1
  63. package/dist/dpop/dpop-nonce.js.map +1 -1
  64. package/dist/errors/access-denied-error.d.ts +1 -1
  65. package/dist/errors/access-denied-error.d.ts.map +1 -1
  66. package/dist/errors/access-denied-error.js.map +1 -1
  67. package/dist/errors/account-selection-required-error.d.ts +1 -1
  68. package/dist/errors/account-selection-required-error.d.ts.map +1 -1
  69. package/dist/errors/account-selection-required-error.js.map +1 -1
  70. package/dist/errors/authorization-error.d.ts +2 -2
  71. package/dist/errors/authorization-error.d.ts.map +1 -1
  72. package/dist/errors/authorization-error.js.map +1 -1
  73. package/dist/errors/consent-required-error.d.ts +1 -1
  74. package/dist/errors/consent-required-error.d.ts.map +1 -1
  75. package/dist/errors/consent-required-error.js.map +1 -1
  76. package/dist/errors/handle-unavailable-error.d.ts +1 -1
  77. package/dist/errors/handle-unavailable-error.d.ts.map +1 -1
  78. package/dist/errors/handle-unavailable-error.js.map +1 -1
  79. package/dist/errors/invalid-authorization-details-error.d.ts +1 -1
  80. package/dist/errors/invalid-authorization-details-error.d.ts.map +1 -1
  81. package/dist/errors/invalid-authorization-details-error.js.map +1 -1
  82. package/dist/errors/invalid-client-metadata-error.js +1 -1
  83. package/dist/errors/invalid-client-metadata-error.js.map +1 -1
  84. package/dist/errors/invalid-credentials-error.d.ts +1 -1
  85. package/dist/errors/invalid-credentials-error.d.ts.map +1 -1
  86. package/dist/errors/invalid-credentials-error.js.map +1 -1
  87. package/dist/errors/invalid-scope-error.d.ts +1 -1
  88. package/dist/errors/invalid-scope-error.d.ts.map +1 -1
  89. package/dist/errors/invalid-scope-error.js.map +1 -1
  90. package/dist/errors/login-required-error.d.ts +1 -1
  91. package/dist/errors/login-required-error.d.ts.map +1 -1
  92. package/dist/errors/login-required-error.js.map +1 -1
  93. package/dist/lexicon/lexicon-data.d.ts +1 -1
  94. package/dist/lexicon/lexicon-data.d.ts.map +1 -1
  95. package/dist/lexicon/lexicon-data.js.map +1 -1
  96. package/dist/lexicon/lexicon-getter.d.ts +3 -3
  97. package/dist/lexicon/lexicon-getter.d.ts.map +1 -1
  98. package/dist/lexicon/lexicon-getter.js +1 -1
  99. package/dist/lexicon/lexicon-getter.js.map +1 -1
  100. package/dist/lexicon/lexicon-manager.d.ts +4 -4
  101. package/dist/lexicon/lexicon-manager.d.ts.map +1 -1
  102. package/dist/lexicon/lexicon-manager.js.map +1 -1
  103. package/dist/lexicon/lexicon-store.d.ts +4 -3
  104. package/dist/lexicon/lexicon-store.d.ts.map +1 -1
  105. package/dist/lexicon/lexicon-store.js.map +1 -1
  106. package/dist/lib/csp/index.d.ts +1 -1
  107. package/dist/lib/csp/index.d.ts.map +1 -1
  108. package/dist/lib/csp/index.js.map +1 -1
  109. package/dist/lib/hcaptcha.d.ts +1 -1
  110. package/dist/lib/hcaptcha.d.ts.map +1 -1
  111. package/dist/lib/hcaptcha.js +1 -1
  112. package/dist/lib/hcaptcha.js.map +1 -1
  113. package/dist/lib/html/build-document.d.ts +1 -1
  114. package/dist/lib/html/build-document.d.ts.map +1 -1
  115. package/dist/lib/html/build-document.js.map +1 -1
  116. package/dist/lib/html/escapers.d.ts +1 -1
  117. package/dist/lib/html/escapers.d.ts.map +1 -1
  118. package/dist/lib/html/escapers.js.map +1 -1
  119. package/dist/lib/html/tags.d.ts +1 -1
  120. package/dist/lib/html/tags.d.ts.map +1 -1
  121. package/dist/lib/html/tags.js +1 -0
  122. package/dist/lib/html/tags.js.map +1 -1
  123. package/dist/lib/http/accept.d.ts +2 -2
  124. package/dist/lib/http/accept.d.ts.map +1 -1
  125. package/dist/lib/http/accept.js.map +1 -1
  126. package/dist/lib/http/middleware.d.ts +1 -1
  127. package/dist/lib/http/middleware.d.ts.map +1 -1
  128. package/dist/lib/http/middleware.js.map +1 -1
  129. package/dist/lib/http/request.d.ts +2 -2
  130. package/dist/lib/http/request.d.ts.map +1 -1
  131. package/dist/lib/http/request.js.map +1 -1
  132. package/dist/lib/http/response.d.ts +1 -1
  133. package/dist/lib/http/response.d.ts.map +1 -1
  134. package/dist/lib/http/response.js.map +1 -1
  135. package/dist/lib/http/route.d.ts +4 -4
  136. package/dist/lib/http/route.d.ts.map +1 -1
  137. package/dist/lib/http/route.js.map +1 -1
  138. package/dist/lib/http/router.d.ts +5 -5
  139. package/dist/lib/http/router.d.ts.map +1 -1
  140. package/dist/lib/http/router.js.map +1 -1
  141. package/dist/lib/http/stream.d.ts +2 -2
  142. package/dist/lib/http/stream.d.ts.map +1 -1
  143. package/dist/lib/http/stream.js.map +1 -1
  144. package/dist/lib/util/time.d.ts +1 -1
  145. package/dist/lib/util/time.d.ts.map +1 -1
  146. package/dist/lib/util/time.js.map +1 -1
  147. package/dist/lib/util/zod-error.d.ts +1 -1
  148. package/dist/lib/util/zod-error.d.ts.map +1 -1
  149. package/dist/lib/util/zod-error.js.map +1 -1
  150. package/dist/lib/write-form-redirect.d.ts +1 -1
  151. package/dist/lib/write-form-redirect.d.ts.map +1 -1
  152. package/dist/lib/write-form-redirect.js.map +1 -1
  153. package/dist/lib/write-html.d.ts +3 -3
  154. package/dist/lib/write-html.d.ts.map +1 -1
  155. package/dist/lib/write-html.js.map +1 -1
  156. package/dist/metadata/build-metadata.d.ts +2 -2
  157. package/dist/metadata/build-metadata.d.ts.map +1 -1
  158. package/dist/metadata/build-metadata.js.map +1 -1
  159. package/dist/{constants.d.ts → oauth-constants.d.ts} +1 -1
  160. package/dist/oauth-constants.d.ts.map +1 -0
  161. package/dist/{constants.js → oauth-constants.js} +1 -1
  162. package/dist/oauth-constants.js.map +1 -0
  163. package/dist/oauth-errors.d.ts +2 -3
  164. package/dist/oauth-errors.d.ts.map +1 -1
  165. package/dist/oauth-errors.js +4 -4
  166. package/dist/oauth-errors.js.map +1 -1
  167. package/dist/oauth-hooks.d.ts +20 -24
  168. package/dist/oauth-hooks.d.ts.map +1 -1
  169. package/dist/oauth-hooks.js +1 -8
  170. package/dist/oauth-hooks.js.map +1 -1
  171. package/dist/oauth-middleware.d.ts +4 -4
  172. package/dist/oauth-middleware.d.ts.map +1 -1
  173. package/dist/oauth-middleware.js.map +1 -1
  174. package/dist/oauth-provider.d.ts +46 -37
  175. package/dist/oauth-provider.d.ts.map +1 -1
  176. package/dist/oauth-provider.js +17 -8
  177. package/dist/oauth-provider.js.map +1 -1
  178. package/dist/oauth-store.d.ts +0 -4
  179. package/dist/oauth-store.d.ts.map +1 -1
  180. package/dist/oauth-store.js +0 -4
  181. package/dist/oauth-store.js.map +1 -1
  182. package/dist/oauth-utils.d.ts +14 -0
  183. package/dist/oauth-utils.d.ts.map +1 -0
  184. package/dist/oauth-utils.js +5 -0
  185. package/dist/oauth-utils.js.map +1 -0
  186. package/dist/oauth-verifier.d.ts +6 -6
  187. package/dist/oauth-verifier.d.ts.map +1 -1
  188. package/dist/oauth-verifier.js.map +1 -1
  189. package/dist/replay/replay-manager.d.ts +2 -2
  190. package/dist/replay/replay-manager.d.ts.map +1 -1
  191. package/dist/replay/replay-manager.js +1 -1
  192. package/dist/replay/replay-manager.js.map +1 -1
  193. package/dist/replay/replay-store-redis.d.ts +1 -1
  194. package/dist/replay/replay-store-redis.d.ts.map +1 -1
  195. package/dist/replay/replay-store-redis.js.map +1 -1
  196. package/dist/replay/replay-store.d.ts +1 -1
  197. package/dist/replay/replay-store.d.ts.map +1 -1
  198. package/dist/replay/replay-store.js.map +1 -1
  199. package/dist/request/code.js +1 -1
  200. package/dist/request/code.js.map +1 -1
  201. package/dist/request/request-data.d.ts +7 -7
  202. package/dist/request/request-data.d.ts.map +1 -1
  203. package/dist/request/request-data.js.map +1 -1
  204. package/dist/request/request-id.d.ts.map +1 -1
  205. package/dist/request/request-id.js +1 -1
  206. package/dist/request/request-id.js.map +1 -1
  207. package/dist/request/request-manager.d.ts +13 -13
  208. package/dist/request/request-manager.d.ts.map +1 -1
  209. package/dist/request/request-manager.js +1 -1
  210. package/dist/request/request-manager.js.map +1 -1
  211. package/dist/request/request-store.d.ts +6 -8
  212. package/dist/request/request-store.d.ts.map +1 -1
  213. package/dist/request/request-store.js +0 -4
  214. package/dist/request/request-store.js.map +1 -1
  215. package/dist/request/request-uri.d.ts +1 -1
  216. package/dist/request/request-uri.d.ts.map +1 -1
  217. package/dist/request/request-uri.js.map +1 -1
  218. package/dist/result/authorization-redirect-parameters.d.ts +2 -2
  219. package/dist/result/authorization-redirect-parameters.d.ts.map +1 -1
  220. package/dist/result/authorization-redirect-parameters.js.map +1 -1
  221. package/dist/result/authorization-result-redirect.d.ts +2 -2
  222. package/dist/result/authorization-result-redirect.d.ts.map +1 -1
  223. package/dist/result/authorization-result-redirect.js.map +1 -1
  224. package/dist/router/assets/assets-manifest.d.ts +2 -2
  225. package/dist/router/assets/assets-manifest.d.ts.map +1 -1
  226. package/dist/router/assets/assets-manifest.js.map +1 -1
  227. package/dist/router/assets/assets.d.ts +4 -4
  228. package/dist/router/assets/assets.d.ts.map +1 -1
  229. package/dist/router/assets/assets.js.map +1 -1
  230. package/dist/router/assets/csrf.js.map +1 -1
  231. package/dist/router/assets/send-account-page.d.ts +2 -2
  232. package/dist/router/assets/send-account-page.d.ts.map +1 -1
  233. package/dist/router/assets/send-account-page.js.map +1 -1
  234. package/dist/router/assets/send-authorization-page.d.ts +3 -3
  235. package/dist/router/assets/send-authorization-page.d.ts.map +1 -1
  236. package/dist/router/assets/send-authorization-page.js.map +1 -1
  237. package/dist/router/assets/send-cookie-error-page.d.ts +2 -2
  238. package/dist/router/assets/send-cookie-error-page.d.ts.map +1 -1
  239. package/dist/router/assets/send-cookie-error-page.js.map +1 -1
  240. package/dist/router/assets/send-error-page.d.ts +2 -2
  241. package/dist/router/assets/send-error-page.d.ts.map +1 -1
  242. package/dist/router/assets/send-error-page.js.map +1 -1
  243. package/dist/router/assets/send-redirect.d.ts +4 -4
  244. package/dist/router/assets/send-redirect.d.ts.map +1 -1
  245. package/dist/router/assets/send-redirect.js.map +1 -1
  246. package/dist/router/create-account-page-middleware.d.ts +1 -1
  247. package/dist/router/create-account-page-middleware.d.ts.map +1 -1
  248. package/dist/router/create-account-page-middleware.js.map +1 -1
  249. package/dist/router/create-api-middleware.d.ts +2 -2
  250. package/dist/router/create-api-middleware.d.ts.map +1 -1
  251. package/dist/router/create-api-middleware.js +1 -1
  252. package/dist/router/create-api-middleware.js.map +1 -1
  253. package/dist/router/create-authorization-page-middleware.d.ts +1 -1
  254. package/dist/router/create-authorization-page-middleware.d.ts.map +1 -1
  255. package/dist/router/create-authorization-page-middleware.js.map +1 -1
  256. package/dist/router/create-oauth-middleware.d.ts +1 -1
  257. package/dist/router/create-oauth-middleware.d.ts.map +1 -1
  258. package/dist/router/create-oauth-middleware.js +1 -1
  259. package/dist/router/create-oauth-middleware.js.map +1 -1
  260. package/dist/signer/api-token-payload.d.ts +1 -1
  261. package/dist/signer/api-token-payload.d.ts.map +1 -1
  262. package/dist/signer/api-token-payload.js +1 -1
  263. package/dist/signer/api-token-payload.js.map +1 -1
  264. package/dist/signer/signer.d.ts +4 -4
  265. package/dist/signer/signer.d.ts.map +1 -1
  266. package/dist/signer/signer.js +2 -2
  267. package/dist/signer/signer.js.map +1 -1
  268. package/dist/token/refresh-token.js +1 -1
  269. package/dist/token/refresh-token.js.map +1 -1
  270. package/dist/token/token-claims.d.ts +4 -4
  271. package/dist/token/token-claims.d.ts.map +1 -1
  272. package/dist/token/token-claims.js.map +1 -1
  273. package/dist/token/token-data.d.ts +7 -7
  274. package/dist/token/token-data.d.ts.map +1 -1
  275. package/dist/token/token-data.js.map +1 -1
  276. package/dist/token/token-id.js +1 -1
  277. package/dist/token/token-id.js.map +1 -1
  278. package/dist/token/token-manager.d.ts +14 -14
  279. package/dist/token/token-manager.d.ts.map +1 -1
  280. package/dist/token/token-manager.js +1 -1
  281. package/dist/token/token-manager.js.map +1 -1
  282. package/dist/token/token-store.d.ts +7 -9
  283. package/dist/token/token-store.d.ts.map +1 -1
  284. package/dist/token/token-store.js +0 -4
  285. package/dist/token/token-store.js.map +1 -1
  286. package/dist/types/handle.js.map +1 -1
  287. package/dist/types/rgb-color.d.ts +1 -1
  288. package/dist/types/rgb-color.d.ts.map +1 -1
  289. package/dist/types/rgb-color.js.map +1 -1
  290. package/package.json +47 -20
  291. package/dist/constants.d.ts.map +0 -1
  292. package/dist/constants.js.map +0 -1
  293. package/dist/errors/invalid-invite-code-error.d.ts +0 -5
  294. package/dist/errors/invalid-invite-code-error.d.ts.map +0 -1
  295. package/dist/errors/invalid-invite-code-error.js +0 -7
  296. package/dist/errors/invalid-invite-code-error.js.map +0 -1
  297. package/dist/index.d.ts +0 -15
  298. package/dist/index.d.ts.map +0 -1
  299. package/dist/index.js +0 -16
  300. package/dist/index.js.map +0 -1
  301. package/dist/oauth-client.d.ts +0 -4
  302. package/dist/oauth-client.d.ts.map +0 -1
  303. package/dist/oauth-client.js +0 -3
  304. package/dist/oauth-client.js.map +0 -1
  305. package/dist/oauth-dpop.d.ts +0 -3
  306. package/dist/oauth-dpop.d.ts.map +0 -1
  307. package/dist/oauth-dpop.js +0 -3
  308. package/dist/oauth-dpop.js.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
2
  import { z } from 'zod';
3
- import { RequestMetadata } from '../lib/http/request.js';
4
- import { DeviceId } from './device-id.js';
5
- import { DeviceStore } from './device-store.js';
3
+ import { type RequestMetadata } from '../lib/http/request.js';
4
+ import { type DeviceId } from './device-id.js';
5
+ import type { DeviceStore } from './device-store.js';
6
6
  /**
7
7
  * @see {@link https://www.npmjs.com/package/keygrip | Keygrip}
8
8
  */
@@ -1 +1 @@
1
- {"version":3,"file":"device-manager.d.ts","sourceRoot":"","sources":["../../src/device/device-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAChE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,OAAO,EACL,eAAe,EAGhB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAE,QAAQ,EAAoC,MAAM,gBAAgB,CAAA;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAG/C;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;EAIxB,CAAA;AAEF,eAAO,MAAM,0BAA0B;IACrC;;;OAGG;;IAOH;;;;OAIG;;IAEH;;OAEG;;;;;;;;;;;;;;;QAIC;;;;;;WAMG;;QAKH;;;;WAIG;;QAEH;;;;WAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAIP,CAAA;AAEF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAA;AAO7E,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,QAAQ,CAAA;IAClB,cAAc,EAAE,eAAe,CAAA;CAChC,CAAA;AAED;;;;GAIG;AACH,qBAAa,aAAa;IAItB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAHxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6C;IAErE,YACmB,KAAK,EAAE,WAAW,EACnC,OAAO,GAAE,oBAAyB,EAGnC;IAEY,UAAU,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAG9D;IAEY,IAAI,CACf,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,WAAW,UAAQ,GAClB,OAAO,CAAC,UAAU,CAAC,CAYrB;YAEa,MAAM;YAuBN,OAAO;YA0CP,MAAM;YAiBN,UAAU;IAyBxB,OAAO,CAAC,WAAW;YA4BL,UAAU;IASxB,OAAO,CAAC,WAAW;IAqBZ,kBAAkB,CAAC,GAAG,EAAE,eAAe,mBAE7C;CACF"}
1
+ {"version":3,"file":"device-manager.d.ts","sourceRoot":"","sources":["../../src/device/device-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAChE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EACL,KAAK,eAAe,EAGrB,MAAM,wBAAwB,CAAA;AAG/B,OAAO,EAAE,KAAK,QAAQ,EAAoC,MAAM,gBAAgB,CAAA;AAChF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAGpD;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;EAIxB,CAAA;AAEF,eAAO,MAAM,0BAA0B;IACrC;;;OAGG;;IAOH;;;;OAIG;;IAEH;;OAEG;;;;;;;;;;;;;;;QAIC;;;;;;WAMG;;QAKH;;;;WAIG;;QAEH;;;;WAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAIP,CAAA;AAEF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAA;AAO7E,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,QAAQ,CAAA;IAClB,cAAc,EAAE,eAAe,CAAA;CAChC,CAAA;AAED;;;;GAIG;AACH,qBAAa,aAAa;IAItB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAHxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6C;IAErE,YACmB,KAAK,EAAE,WAAW,EACnC,OAAO,GAAE,oBAAyB,EAGnC;IAEY,UAAU,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAG9D;IAEY,IAAI,CACf,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,WAAW,UAAQ,GAClB,OAAO,CAAC,UAAU,CAAC,CAYrB;YAEa,MAAM;YAuBN,OAAO;YA0CP,MAAM;YAiBN,UAAU;IAyBxB,OAAO,CAAC,WAAW;YA4BL,UAAU;IASxB,OAAO,CAAC,WAAW;IAqBZ,kBAAkB,CAAC,GAAG,EAAE,eAAe,mBAE7C;CACF"}
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
- import { SESSION_FIXATION_MAX_AGE } from '../constants.js';
3
2
  import { parseHttpCookies } from '../lib/http/index.js';
4
3
  import { extractRequestMetadata, setCookie, } from '../lib/http/request.js';
4
+ import { SESSION_FIXATION_MAX_AGE } from '../oauth-constants.js';
5
5
  import { deviceIdSchema, generateDeviceId } from './device-id.js';
6
6
  import { generateSessionId, sessionIdSchema } from './session-id.js';
7
7
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"device-manager.js","sourceRoot":"","sources":["../../src/device/device-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { z } from 'zod'\nimport { SESSION_FIXATION_MAX_AGE } from '../constants.js'\nimport { parseHttpCookies } from '../lib/http/index.js'\nimport {\n RequestMetadata,\n extractRequestMetadata,\n setCookie,\n} from '../lib/http/request.js'\nimport { DeviceData } from './device-data.js'\nimport { DeviceId, deviceIdSchema, generateDeviceId } from './device-id.js'\nimport { DeviceStore } from './device-store.js'\nimport { generateSessionId, sessionIdSchema } from './session-id.js'\n\n/**\n * @see {@link https://www.npmjs.com/package/keygrip | Keygrip}\n */\nexport const keygripSchema = z.object({\n sign: z.function().args(z.any()).returns(z.string()),\n verify: z.function().args(z.any(), z.string()).returns(z.boolean()),\n index: z.function().args(z.any(), z.string()).returns(z.number()),\n})\n\nexport const deviceManagerOptionsSchema = z.object({\n /**\n * Controls whether the IP address is read from the `X-Forwarded-For` header\n * (if `true`), or from the `req.socket.remoteAddress` property (if `false`).\n */\n trustProxy: z\n .function()\n .args<[addr: z.ZodString, i: z.ZodNumber]>(z.string(), z.number())\n .returns(z.boolean())\n .optional(),\n\n /**\n * Amount of time (in ms) after which session IDs will be rotated\n *\n * @default 300e3 // (5 minutes)\n */\n rotationRate: z.number().default(300e3),\n /**\n * Cookie options\n */\n cookie: z\n .object({\n keys: keygripSchema.optional(),\n /**\n * Amount of time (in ms) after which the session cookie will expire.\n * If set to `null`, the cookie will be a session cookie (deleted when the\n * browser is closed).\n *\n * @default 10 years\n */\n age: z\n .number()\n .nullable()\n .default(10 * 365.2 * 24 * 60 * 60e3),\n /**\n * Controls whether the cookie is only sent over HTTPS (if `true`), or also\n * over HTTP (if `false`). This should **NOT** be set to `false` in\n * production.\n */\n secure: z.boolean().default(true),\n /**\n * Controls whether the cookie is sent along with cross-site requests.\n *\n * @default 'lax'\n */\n sameSite: z.enum(['lax', 'strict']).default('lax'),\n })\n .default({}),\n})\n\nexport type DeviceManagerOptions = z.input<typeof deviceManagerOptionsSchema>\n\ntype CookieValue = {\n deviceId: DeviceId\n sessionId: string\n}\n\nexport type DeviceInfo = {\n deviceId: DeviceId\n deviceMetadata: RequestMetadata\n}\n\n/**\n * This class provides an abstraction for keeping track of DEVICE sessions. It\n * relies on a {@link DeviceStore} to persist session data and a cookie to\n * identify the session.\n */\nexport class DeviceManager {\n private readonly options: z.output<typeof deviceManagerOptionsSchema>\n\n constructor(\n private readonly store: DeviceStore,\n options: DeviceManagerOptions = {},\n ) {\n this.options = deviceManagerOptionsSchema.parse(options)\n }\n\n public async hasSession(req: IncomingMessage): Promise<boolean> {\n const cookies = await this.getCookies(req)\n return cookies !== null\n }\n\n public async load(\n req: IncomingMessage,\n res: ServerResponse,\n forceRotate = false,\n ): Promise<DeviceInfo> {\n const cookie = await this.getCookies(req)\n if (cookie) {\n return this.refresh(\n req,\n res,\n cookie.value,\n forceRotate || cookie.mustRotate,\n )\n } else {\n return this.create(req, res)\n }\n }\n\n private async create(\n req: IncomingMessage,\n res: ServerResponse,\n ): Promise<DeviceInfo> {\n const deviceMetadata = this.getRequestMetadata(req)\n\n const [deviceId, sessionId] = await Promise.all([\n generateDeviceId(),\n generateSessionId(),\n ] as const)\n\n await this.store.createDevice(deviceId, {\n sessionId,\n lastSeenAt: new Date(),\n userAgent: deviceMetadata.userAgent ?? null,\n ipAddress: deviceMetadata.ipAddress,\n })\n\n await this.setCookies(req, res, { deviceId, sessionId })\n\n return { deviceId, deviceMetadata }\n }\n\n private async refresh(\n req: IncomingMessage,\n res: ServerResponse,\n { deviceId, sessionId }: CookieValue,\n forceRotate = false,\n ): Promise<DeviceInfo> {\n const data = await this.store.readDevice(deviceId)\n if (!data) return this.create(req, res)\n\n const lastSeenAt = new Date(data.lastSeenAt)\n const age = Date.now() - lastSeenAt.getTime()\n\n if (sessionId !== data.sessionId) {\n if (age <= SESSION_FIXATION_MAX_AGE) {\n // The cookie was probably rotated by a concurrent request. Let's\n // update the cookie with the new sessionId.\n forceRotate = true\n } else {\n // Something's wrong. Let's create a new session.\n await this.store.deleteDevice(deviceId)\n return this.create(req, res)\n }\n }\n\n const deviceMetadata = this.getRequestMetadata(req)\n\n const shouldRotate =\n forceRotate ||\n deviceMetadata.ipAddress !== data.ipAddress ||\n deviceMetadata.userAgent !== data.userAgent ||\n age > this.options.rotationRate\n\n if (shouldRotate) {\n await this.rotate(req, res, deviceId, {\n ipAddress: deviceMetadata.ipAddress,\n userAgent: deviceMetadata.userAgent || data.userAgent,\n })\n }\n\n return { deviceId, deviceMetadata }\n }\n\n private async rotate(\n req: IncomingMessage,\n res: ServerResponse,\n deviceId: DeviceId,\n data?: Partial<Omit<DeviceData, 'sessionId' | 'lastSeenAt'>>,\n ): Promise<void> {\n const sessionId = await generateSessionId()\n\n await this.store.updateDevice(deviceId, {\n ...data,\n sessionId,\n lastSeenAt: new Date(),\n })\n\n await this.setCookies(req, res, { deviceId, sessionId })\n }\n\n private async getCookies(\n req: IncomingMessage,\n ): Promise<{ value: CookieValue; mustRotate: boolean } | null> {\n const cookies = parseHttpCookies(req)\n\n const device = this.parseCookie(cookies, `dev-id`, deviceIdSchema)\n const session = this.parseCookie(cookies, `ses-id`, sessionIdSchema)\n\n const deviceId = device?.value\n const sessionId = session?.value\n\n // Silently ignore invalid cookies\n if (!deviceId || !sessionId) {\n // If the device cookie is still present, let's cleanup the DB\n if (deviceId) await this.store.deleteDevice(deviceId)\n\n return null\n }\n\n return {\n value: { deviceId, sessionId },\n mustRotate: device.mustRotate || session.mustRotate,\n }\n }\n\n private parseCookie<T>(\n cookies: Record<string, string | undefined>,\n name: string,\n schema: z.ZodType<T> | z.ZodEffects<z.ZodTypeAny, T, string>,\n ): null | { value: T; mustRotate: boolean } {\n const rawValue = Object.hasOwn(cookies, name) ? cookies[name] : null\n if (!rawValue) return null\n\n const result = schema.safeParse(rawValue)\n if (!result.success) return null\n\n const value = result.data\n\n if (this.options.cookie.keys) {\n const hashName = `${name}:hash`\n\n const hash = Object.hasOwn(cookies, hashName) ? cookies[hashName] : null\n if (!hash) return null\n\n const idx = this.options.cookie.keys.index(rawValue, hash)\n if (idx < 0) return null\n\n return { value, mustRotate: idx !== 0 }\n }\n\n return { value, mustRotate: false }\n }\n\n private async setCookies(\n req: IncomingMessage,\n res: ServerResponse,\n { deviceId, sessionId }: CookieValue,\n ) {\n this.writeCookie(res, `dev-id`, deviceId)\n this.writeCookie(res, `ses-id`, sessionId)\n }\n\n private writeCookie(res: ServerResponse, name: string, value?: string) {\n const cookieOptions = {\n maxAge: value\n ? this.options.cookie.age == null\n ? undefined\n : this.options.cookie.age / 1000\n : 0,\n httpOnly: true,\n path: '/',\n secure: this.options.cookie.secure !== false,\n sameSite: this.options.cookie.sameSite,\n } as const\n\n setCookie(res, name, value || '', cookieOptions)\n\n if (this.options.cookie.keys) {\n const hash = value ? this.options.cookie.keys.sign(value) : ''\n setCookie(res, `${name}:hash`, hash, cookieOptions)\n }\n }\n\n public getRequestMetadata(req: IncomingMessage) {\n return extractRequestMetadata(req, this.options)\n }\n}\n"]}
1
+ {"version":3,"file":"device-manager.js","sourceRoot":"","sources":["../../src/device/device-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { z } from 'zod'\nimport { parseHttpCookies } from '../lib/http/index.js'\nimport {\n type RequestMetadata,\n extractRequestMetadata,\n setCookie,\n} from '../lib/http/request.js'\nimport { SESSION_FIXATION_MAX_AGE } from '../oauth-constants.js'\nimport type { DeviceData } from './device-data.js'\nimport { type DeviceId, deviceIdSchema, generateDeviceId } from './device-id.js'\nimport type { DeviceStore } from './device-store.js'\nimport { generateSessionId, sessionIdSchema } from './session-id.js'\n\n/**\n * @see {@link https://www.npmjs.com/package/keygrip | Keygrip}\n */\nexport const keygripSchema = z.object({\n sign: z.function().args(z.any()).returns(z.string()),\n verify: z.function().args(z.any(), z.string()).returns(z.boolean()),\n index: z.function().args(z.any(), z.string()).returns(z.number()),\n})\n\nexport const deviceManagerOptionsSchema = z.object({\n /**\n * Controls whether the IP address is read from the `X-Forwarded-For` header\n * (if `true`), or from the `req.socket.remoteAddress` property (if `false`).\n */\n trustProxy: z\n .function()\n .args<[addr: z.ZodString, i: z.ZodNumber]>(z.string(), z.number())\n .returns(z.boolean())\n .optional(),\n\n /**\n * Amount of time (in ms) after which session IDs will be rotated\n *\n * @default 300e3 // (5 minutes)\n */\n rotationRate: z.number().default(300e3),\n /**\n * Cookie options\n */\n cookie: z\n .object({\n keys: keygripSchema.optional(),\n /**\n * Amount of time (in ms) after which the session cookie will expire.\n * If set to `null`, the cookie will be a session cookie (deleted when the\n * browser is closed).\n *\n * @default 10 years\n */\n age: z\n .number()\n .nullable()\n .default(10 * 365.2 * 24 * 60 * 60e3),\n /**\n * Controls whether the cookie is only sent over HTTPS (if `true`), or also\n * over HTTP (if `false`). This should **NOT** be set to `false` in\n * production.\n */\n secure: z.boolean().default(true),\n /**\n * Controls whether the cookie is sent along with cross-site requests.\n *\n * @default 'lax'\n */\n sameSite: z.enum(['lax', 'strict']).default('lax'),\n })\n .default({}),\n})\n\nexport type DeviceManagerOptions = z.input<typeof deviceManagerOptionsSchema>\n\ntype CookieValue = {\n deviceId: DeviceId\n sessionId: string\n}\n\nexport type DeviceInfo = {\n deviceId: DeviceId\n deviceMetadata: RequestMetadata\n}\n\n/**\n * This class provides an abstraction for keeping track of DEVICE sessions. It\n * relies on a {@link DeviceStore} to persist session data and a cookie to\n * identify the session.\n */\nexport class DeviceManager {\n private readonly options: z.output<typeof deviceManagerOptionsSchema>\n\n constructor(\n private readonly store: DeviceStore,\n options: DeviceManagerOptions = {},\n ) {\n this.options = deviceManagerOptionsSchema.parse(options)\n }\n\n public async hasSession(req: IncomingMessage): Promise<boolean> {\n const cookies = await this.getCookies(req)\n return cookies !== null\n }\n\n public async load(\n req: IncomingMessage,\n res: ServerResponse,\n forceRotate = false,\n ): Promise<DeviceInfo> {\n const cookie = await this.getCookies(req)\n if (cookie) {\n return this.refresh(\n req,\n res,\n cookie.value,\n forceRotate || cookie.mustRotate,\n )\n } else {\n return this.create(req, res)\n }\n }\n\n private async create(\n req: IncomingMessage,\n res: ServerResponse,\n ): Promise<DeviceInfo> {\n const deviceMetadata = this.getRequestMetadata(req)\n\n const [deviceId, sessionId] = await Promise.all([\n generateDeviceId(),\n generateSessionId(),\n ] as const)\n\n await this.store.createDevice(deviceId, {\n sessionId,\n lastSeenAt: new Date(),\n userAgent: deviceMetadata.userAgent ?? null,\n ipAddress: deviceMetadata.ipAddress,\n })\n\n await this.setCookies(req, res, { deviceId, sessionId })\n\n return { deviceId, deviceMetadata }\n }\n\n private async refresh(\n req: IncomingMessage,\n res: ServerResponse,\n { deviceId, sessionId }: CookieValue,\n forceRotate = false,\n ): Promise<DeviceInfo> {\n const data = await this.store.readDevice(deviceId)\n if (!data) return this.create(req, res)\n\n const lastSeenAt = new Date(data.lastSeenAt)\n const age = Date.now() - lastSeenAt.getTime()\n\n if (sessionId !== data.sessionId) {\n if (age <= SESSION_FIXATION_MAX_AGE) {\n // The cookie was probably rotated by a concurrent request. Let's\n // update the cookie with the new sessionId.\n forceRotate = true\n } else {\n // Something's wrong. Let's create a new session.\n await this.store.deleteDevice(deviceId)\n return this.create(req, res)\n }\n }\n\n const deviceMetadata = this.getRequestMetadata(req)\n\n const shouldRotate =\n forceRotate ||\n deviceMetadata.ipAddress !== data.ipAddress ||\n deviceMetadata.userAgent !== data.userAgent ||\n age > this.options.rotationRate\n\n if (shouldRotate) {\n await this.rotate(req, res, deviceId, {\n ipAddress: deviceMetadata.ipAddress,\n userAgent: deviceMetadata.userAgent || data.userAgent,\n })\n }\n\n return { deviceId, deviceMetadata }\n }\n\n private async rotate(\n req: IncomingMessage,\n res: ServerResponse,\n deviceId: DeviceId,\n data?: Partial<Omit<DeviceData, 'sessionId' | 'lastSeenAt'>>,\n ): Promise<void> {\n const sessionId = await generateSessionId()\n\n await this.store.updateDevice(deviceId, {\n ...data,\n sessionId,\n lastSeenAt: new Date(),\n })\n\n await this.setCookies(req, res, { deviceId, sessionId })\n }\n\n private async getCookies(\n req: IncomingMessage,\n ): Promise<{ value: CookieValue; mustRotate: boolean } | null> {\n const cookies = parseHttpCookies(req)\n\n const device = this.parseCookie(cookies, `dev-id`, deviceIdSchema)\n const session = this.parseCookie(cookies, `ses-id`, sessionIdSchema)\n\n const deviceId = device?.value\n const sessionId = session?.value\n\n // Silently ignore invalid cookies\n if (!deviceId || !sessionId) {\n // If the device cookie is still present, let's cleanup the DB\n if (deviceId) await this.store.deleteDevice(deviceId)\n\n return null\n }\n\n return {\n value: { deviceId, sessionId },\n mustRotate: device.mustRotate || session.mustRotate,\n }\n }\n\n private parseCookie<T>(\n cookies: Record<string, string | undefined>,\n name: string,\n schema: z.ZodType<T> | z.ZodEffects<z.ZodTypeAny, T, string>,\n ): null | { value: T; mustRotate: boolean } {\n const rawValue = Object.hasOwn(cookies, name) ? cookies[name] : null\n if (!rawValue) return null\n\n const result = schema.safeParse(rawValue)\n if (!result.success) return null\n\n const value = result.data\n\n if (this.options.cookie.keys) {\n const hashName = `${name}:hash`\n\n const hash = Object.hasOwn(cookies, hashName) ? cookies[hashName] : null\n if (!hash) return null\n\n const idx = this.options.cookie.keys.index(rawValue, hash)\n if (idx < 0) return null\n\n return { value, mustRotate: idx !== 0 }\n }\n\n return { value, mustRotate: false }\n }\n\n private async setCookies(\n req: IncomingMessage,\n res: ServerResponse,\n { deviceId, sessionId }: CookieValue,\n ) {\n this.writeCookie(res, `dev-id`, deviceId)\n this.writeCookie(res, `ses-id`, sessionId)\n }\n\n private writeCookie(res: ServerResponse, name: string, value?: string) {\n const cookieOptions = {\n maxAge: value\n ? this.options.cookie.age == null\n ? undefined\n : this.options.cookie.age / 1000\n : 0,\n httpOnly: true,\n path: '/',\n secure: this.options.cookie.secure !== false,\n sameSite: this.options.cookie.sameSite,\n } as const\n\n setCookie(res, name, value || '', cookieOptions)\n\n if (this.options.cookie.keys) {\n const hash = value ? this.options.cookie.keys.sign(value) : ''\n setCookie(res, `${name}:hash`, hash, cookieOptions)\n }\n }\n\n public getRequestMetadata(req: IncomingMessage) {\n return extractRequestMetadata(req, this.options)\n }\n}\n"]}
@@ -1,10 +1,8 @@
1
- import { Awaitable } from '../lib/util/type.js';
2
- import { DeviceData } from './device-data.js';
3
- import { DeviceId } from './device-id.js';
4
- export * from './device-data.js';
5
- export * from './device-id.js';
6
- export * from './session-id.js';
7
- export type { Awaitable };
1
+ import type { Awaitable } from '../lib/util/type.js';
2
+ import type { DeviceData } from './device-data.js';
3
+ import type { DeviceId } from './device-id.js';
4
+ export type * from './device-data.js';
5
+ export type { Awaitable, DeviceId };
8
6
  export interface DeviceStore {
9
7
  createDevice(deviceId: DeviceId, data: DeviceData): Awaitable<void>;
10
8
  readDevice(deviceId: DeviceId): Awaitable<DeviceData | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"device-store.d.ts","sourceRoot":"","sources":["../../src/device/device-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAyB,MAAM,qBAAqB,CAAA;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAGzC,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,iBAAiB,CAAA;AAE/B,YAAY,EAAE,SAAS,EAAE,CAAA;AAEzB,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IACnE,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;IAC5D,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IAC5E,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;CAClD;AAED,eAAO,MAAM,aAAa,uHAKxB,CAAA;AAEF,wBAAgB,aAAa,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,GAAG,CAAC,GAAG,WAAW,CAKnE"}
1
+ {"version":3,"file":"device-store.d.ts","sourceRoot":"","sources":["../../src/device/device-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAEpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAG9C,mBAAmB,kBAAkB,CAAA;AACrC,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAA;AAEnC,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IACnE,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;IAC5D,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IAC5E,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;CAClD;AAED,eAAO,MAAM,aAAa,uHAKxB,CAAA;AAEF,wBAAgB,aAAa,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,GAAG,CAAC,GAAG,WAAW,CAKnE"}
@@ -1,8 +1,4 @@
1
1
  import { buildInterfaceChecker } from '../lib/util/type.js';
2
- // Export all types needed to implement the DeviceStore interface
3
- export * from './device-data.js';
4
- export * from './device-id.js';
5
- export * from './session-id.js';
6
2
  export const isDeviceStore = buildInterfaceChecker([
7
3
  'createDevice',
8
4
  'deleteDevice',
@@ -1 +1 @@
1
- {"version":3,"file":"device-store.js","sourceRoot":"","sources":["../../src/device/device-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAItE,iEAAiE;AACjE,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,iBAAiB,CAAA;AAW/B,MAAM,CAAC,MAAM,aAAa,GAAG,qBAAqB,CAAc;IAC9D,cAAc;IACd,cAAc;IACd,YAAY;IACZ,cAAc;CACf,CAAC,CAAA;AAEF,MAAM,UAAU,aAAa,CAAI,cAAiB;IAChD,IAAI,CAAC,cAAc,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAA;IACvD,CAAC;IACD,OAAO,cAAc,CAAA;AACvB,CAAC","sourcesContent":["import { Awaitable, buildInterfaceChecker } from '../lib/util/type.js'\nimport { DeviceData } from './device-data.js'\nimport { DeviceId } from './device-id.js'\n\n// Export all types needed to implement the DeviceStore interface\nexport * from './device-data.js'\nexport * from './device-id.js'\nexport * from './session-id.js'\n\nexport type { Awaitable }\n\nexport interface DeviceStore {\n createDevice(deviceId: DeviceId, data: DeviceData): Awaitable<void>\n readDevice(deviceId: DeviceId): Awaitable<DeviceData | null>\n updateDevice(deviceId: DeviceId, data: Partial<DeviceData>): Awaitable<void>\n deleteDevice(deviceId: DeviceId): Awaitable<void>\n}\n\nexport const isDeviceStore = buildInterfaceChecker<DeviceStore>([\n 'createDevice',\n 'deleteDevice',\n 'readDevice',\n 'updateDevice',\n])\n\nexport function asDeviceStore<V>(implementation: V): V & DeviceStore {\n if (!implementation || !isDeviceStore(implementation)) {\n throw new Error('Invalid DeviceStore implementation')\n }\n return implementation\n}\n"]}
1
+ {"version":3,"file":"device-store.js","sourceRoot":"","sources":["../../src/device/device-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAe3D,MAAM,CAAC,MAAM,aAAa,GAAG,qBAAqB,CAAc;IAC9D,cAAc;IACd,cAAc;IACd,YAAY;IACZ,cAAc;CACf,CAAC,CAAA;AAEF,MAAM,UAAU,aAAa,CAAI,cAAiB;IAChD,IAAI,CAAC,cAAc,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAA;IACvD,CAAC;IACD,OAAO,cAAc,CAAA;AACvB,CAAC","sourcesContent":["import type { Awaitable } from '../lib/util/type.js'\nimport { buildInterfaceChecker } from '../lib/util/type.js'\nimport type { DeviceData } from './device-data.js'\nimport type { DeviceId } from './device-id.js'\n\n// Export all types needed to implement the DeviceStore interface\nexport type * from './device-data.js'\nexport type { Awaitable, DeviceId }\n\nexport interface DeviceStore {\n createDevice(deviceId: DeviceId, data: DeviceData): Awaitable<void>\n readDevice(deviceId: DeviceId): Awaitable<DeviceData | null>\n updateDevice(deviceId: DeviceId, data: Partial<DeviceData>): Awaitable<void>\n deleteDevice(deviceId: DeviceId): Awaitable<void>\n}\n\nexport const isDeviceStore = buildInterfaceChecker<DeviceStore>([\n 'createDevice',\n 'deleteDevice',\n 'readDevice',\n 'updateDevice',\n])\n\nexport function asDeviceStore<V>(implementation: V): V & DeviceStore {\n if (!implementation || !isDeviceStore(implementation)) {\n throw new Error('Invalid DeviceStore implementation')\n }\n return implementation\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"session-id.d.ts","sourceRoot":"","sources":["../../src/device/session-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,iBAAiB,QAC0B,CAAA;AAExD,eAAO,MAAM,eAAe,oDASzB,CAAA;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AACvD,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,SAAS,CAE3D,CAAA"}
1
+ {"version":3,"file":"session-id.d.ts","sourceRoot":"","sources":["../../src/device/session-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAOvB,eAAO,MAAM,iBAAiB,QAC0B,CAAA;AAExD,eAAO,MAAM,eAAe,oDASzB,CAAA;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AACvD,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,SAAS,CAE3D,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
- import { SESSION_ID_BYTES_LENGTH, SESSION_ID_PREFIX } from '../constants.js';
3
2
  import { randomHexId } from '../lib/util/crypto.js';
3
+ import { SESSION_ID_BYTES_LENGTH, SESSION_ID_PREFIX, } from '../oauth-constants.js';
4
4
  export const SESSION_ID_LENGTH = SESSION_ID_PREFIX.length + SESSION_ID_BYTES_LENGTH * 2; // hex encoding
5
5
  export const sessionIdSchema = z
6
6
  .string()
@@ -1 +1 @@
1
- {"version":3,"file":"session-id.js","sourceRoot":"","sources":["../../src/device/session-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAEnD,MAAM,CAAC,MAAM,iBAAiB,GAC5B,iBAAiB,CAAC,MAAM,GAAG,uBAAuB,GAAG,CAAC,CAAA,CAAC,eAAe;AAExE,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC;KAC7B,MAAM,EAAE;KACR,MAAM,CAAC,iBAAiB,CAAC;KACzB,MAAM,CACL,CAAC,CAAC,EAA+C,EAAE,CACjD,CAAC,CAAC,UAAU,CAAC,iBAAiB,CAAC,EACjC;IACE,OAAO,EAAE,2BAA2B;CACrC,CACF,CAAA;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,IAAwB,EAAE;IAC9D,OAAO,GAAG,iBAAiB,GAAG,MAAM,WAAW,CAAC,uBAAuB,CAAC,EAAE,CAAA;AAC5E,CAAC,CAAA","sourcesContent":["import { z } from 'zod'\nimport { SESSION_ID_BYTES_LENGTH, SESSION_ID_PREFIX } from '../constants.js'\nimport { randomHexId } from '../lib/util/crypto.js'\n\nexport const SESSION_ID_LENGTH =\n SESSION_ID_PREFIX.length + SESSION_ID_BYTES_LENGTH * 2 // hex encoding\n\nexport const sessionIdSchema = z\n .string()\n .length(SESSION_ID_LENGTH)\n .refine(\n (v): v is `${typeof SESSION_ID_PREFIX}${string}` =>\n v.startsWith(SESSION_ID_PREFIX),\n {\n message: `Invalid session ID format`,\n },\n )\nexport type SessionId = z.infer<typeof sessionIdSchema>\nexport const generateSessionId = async (): Promise<SessionId> => {\n return `${SESSION_ID_PREFIX}${await randomHexId(SESSION_ID_BYTES_LENGTH)}`\n}\n"]}
1
+ {"version":3,"file":"session-id.js","sourceRoot":"","sources":["../../src/device/session-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACnD,OAAO,EACL,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,uBAAuB,CAAA;AAE9B,MAAM,CAAC,MAAM,iBAAiB,GAC5B,iBAAiB,CAAC,MAAM,GAAG,uBAAuB,GAAG,CAAC,CAAA,CAAC,eAAe;AAExE,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC;KAC7B,MAAM,EAAE;KACR,MAAM,CAAC,iBAAiB,CAAC;KACzB,MAAM,CACL,CAAC,CAAC,EAA+C,EAAE,CACjD,CAAC,CAAC,UAAU,CAAC,iBAAiB,CAAC,EACjC;IACE,OAAO,EAAE,2BAA2B;CACrC,CACF,CAAA;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,IAAwB,EAAE;IAC9D,OAAO,GAAG,iBAAiB,GAAG,MAAM,WAAW,CAAC,uBAAuB,CAAC,EAAE,CAAA;AAC5E,CAAC,CAAA","sourcesContent":["import { z } from 'zod'\nimport { randomHexId } from '../lib/util/crypto.js'\nimport {\n SESSION_ID_BYTES_LENGTH,\n SESSION_ID_PREFIX,\n} from '../oauth-constants.js'\n\nexport const SESSION_ID_LENGTH =\n SESSION_ID_PREFIX.length + SESSION_ID_BYTES_LENGTH * 2 // hex encoding\n\nexport const sessionIdSchema = z\n .string()\n .length(SESSION_ID_LENGTH)\n .refine(\n (v): v is `${typeof SESSION_ID_PREFIX}${string}` =>\n v.startsWith(SESSION_ID_PREFIX),\n {\n message: `Invalid session ID format`,\n },\n )\nexport type SessionId = z.infer<typeof sessionIdSchema>\nexport const generateSessionId = async (): Promise<SessionId> => {\n return `${SESSION_ID_PREFIX}${await randomHexId(SESSION_ID_BYTES_LENGTH)}`\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
- import { DpopNonce, DpopSecret } from './dpop-nonce.js';
3
- import { DpopProof } from './dpop-proof.js';
2
+ import { DpopNonce, type DpopSecret } from './dpop-nonce.js';
3
+ import type { DpopProof } from './dpop-proof.js';
4
4
  export { DpopNonce, type DpopSecret };
5
5
  export declare const dpopManagerOptionsSchema: z.ZodObject<{
6
6
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"dpop-manager.d.ts","sourceRoot":"","sources":["../../src/dpop/dpop-manager.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAMvB,OAAO,EACL,SAAS,EACT,UAAU,EAGX,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAI3C,OAAO,EAAE,SAAS,EAAE,KAAK,UAAU,EAAE,CAAA;AAErC,eAAO,MAAM,wBAAwB;IACnC;;;;;OAKG;;;;;;;;;EAGH,CAAA;AACF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAEzE,qBAAa,WAAW;IACtB,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAA;IAExC,YAAY,OAAO,GAAE,kBAAuB,EAO3C;IAED,SAAS,IAAI,MAAM,GAAG,SAAS,CAE9B;IAED;;OAEG;IACG,UAAU,CACd,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,EACtB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC,EAC1D,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC,CAqF3B;CACF"}
1
+ {"version":3,"file":"dpop-manager.d.ts","sourceRoot":"","sources":["../../src/dpop/dpop-manager.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAMvB,OAAO,EACL,SAAS,EACT,KAAK,UAAU,EAGhB,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAIhD,OAAO,EAAE,SAAS,EAAE,KAAK,UAAU,EAAE,CAAA;AAErC,eAAO,MAAM,wBAAwB;IACnC;;;;;OAKG;;;;;;;;;EAGH,CAAA;AACF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAEzE,qBAAa,WAAW;IACtB,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAA;IAExC,YAAY,OAAO,GAAE,kBAAuB,EAO3C;IAED,SAAS,IAAI,MAAM,GAAG,SAAS,CAE9B;IAED;;OAEG;IACG,UAAU,CACd,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,EACtB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC,EAC1D,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC,CAqF3B;CACF"}
@@ -2,10 +2,10 @@ import { createHash } from 'node:crypto';
2
2
  import { EmbeddedJWK, calculateJwkThumbprint, errors, jwtVerify } from 'jose';
3
3
  import { z } from 'zod';
4
4
  import { ValidationError } from '@atproto/jwk';
5
- import { DPOP_NONCE_MAX_AGE } from '../constants.js';
6
5
  import { InvalidDpopProofError } from '../errors/invalid-dpop-proof-error.js';
7
6
  import { UseDpopNonceError } from '../errors/use-dpop-nonce-error.js';
8
7
  import { ifURL } from '../lib/util/cast.js';
8
+ import { DPOP_NONCE_MAX_AGE } from '../oauth-constants.js';
9
9
  import { DpopNonce, dpopSecretSchema, rotationIntervalSchema, } from './dpop-nonce.js';
10
10
  const { JOSEError } = errors;
11
11
  export { DpopNonce };
@@ -1 +1 @@
1
- {"version":3,"file":"dpop-manager.js","sourceRoot":"","sources":["../../src/dpop/dpop-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,MAAM,CAAA;AAC7E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAA;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAA;AACrE,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAC3C,OAAO,EACL,SAAS,EAET,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,iBAAiB,CAAA;AAGxB,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAA;AAE5B,OAAO,EAAE,SAAS,EAAmB,CAAA;AAErC,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C;;;;;OAKG;IACH,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,QAAQ,EAAE;IACpE,oBAAoB,EAAE,sBAAsB,CAAC,QAAQ,EAAE;CACxD,CAAC,CAAA;AAGF,MAAM,OAAO,WAAW;IAGtB,YAAY,OAAO,GAAuB,EAAE;QAC1C,MAAM,EAAE,UAAU,EAAE,oBAAoB,EAAE,GACxC,wBAAwB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QACzC,IAAI,CAAC,SAAS;YACZ,UAAU,KAAK,KAAK;gBAClB,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,IAAI,SAAS,CAAC,UAAU,EAAE,oBAAoB,CAAC,CAAA;IACvD,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,CAAA;IAC/B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CACd,UAAkB,EAClB,OAAsB,EACtB,WAA0D,EAC1D,WAAoB;QAEpB,4CAA4C;QAC5C,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,SAAS,CAAC,yBAAyB,CAAC,CAAA;QAChD,CAAC;QAED,MAAM,KAAK,GAAG,YAAY,CAAC,WAAW,CAAC,CAAA;QACvC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QAEvB,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE;YACvE,GAAG,EAAE,UAAU;YACf,WAAW,EAAE,EAAE,EAAE,iDAAiD;YAClE,cAAc,EAAE,kBAAkB,GAAG,GAAG;SACzC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACf,MAAM,yBAAyB,CAAC,GAAG,EAAE,6BAA6B,CAAC,CAAA;QACrE,CAAC,CAAC,CAAA;QAEF,mEAAmE;QACnE,2EAA2E;QAC3E,4CAA4C;QAE5C,+DAA+D;QAC/D,yBAAyB;QACzB,sBAAsB;QACtB,kEAAkE;QAClE,OAAO;QAEP,2EAA2E;QAC3E,oCAAoC;QACpC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,OAAO,CAAA;QAE7C,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACrD,MAAM,IAAI,qBAAqB,CAAC,2BAA2B,CAAC,CAAA;QAC9D,CAAC;QAED,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YACpC,MAAM,IAAI,qBAAqB,CAAC,oBAAoB,CAAC,CAAA;QACvD,CAAC;QAED,yEAAyE;QACzE,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;YAC/B,MAAM,IAAI,qBAAqB,CAAC,qBAAqB,CAAC,CAAA;QACxD,CAAC;QAED,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YACpC,MAAM,IAAI,qBAAqB,CAAC,yBAAyB,CAAC,CAAA;QAC5D,CAAC;QAED,uEAAuE;QACvE,gEAAgE;QAChE,mEAAmE;QACnE,6BAA6B;QAC7B,EAAE;QACF,wGAAwG;QACxG,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC;YACvD,MAAM,IAAI,qBAAqB,CAAC,qBAAqB,CAAC,CAAA;QACxD,CAAC;QAED,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC7B,MAAM,IAAI,iBAAiB,EAAE,CAAA;QAC/B,CAAC;QAED,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3C,MAAM,IAAI,iBAAiB,CAAC,uBAAuB,CAAC,CAAA;QACtD,CAAC;QAED,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,eAAe,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,CAAA;YACzE,IAAI,GAAG,KAAK,eAAe,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;gBAClD,MAAM,IAAI,qBAAqB,CAAC,qBAAqB,CAAC,CAAA;YACxD,CAAC;QACH,CAAC;aAAM,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,IAAI,qBAAqB,CAAC,8BAA8B,CAAC,CAAA;QACjE,CAAC;QAED,oEAAoE;QACpE,oCAAoC;QACpC,MAAM,GAAG,GAAG,eAAe,CAAC,GAAI,CAAA;QAChC,MAAM,GAAG,GAAG,MAAM,sBAAsB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACpE,MAAM,yBAAyB,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAA;QACjE,CAAC,CAAC,CAAA;QAEF,0EAA0E;QAC1E,UAAU;QACV,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAA;IAC9C,CAAC;CACF;AAED,SAAS,YAAY,CACnB,WAA0D;IAE1D,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,CAAA;IACtC,QAAQ,OAAO,UAAU,EAAE,CAAC;QAC1B,KAAK,QAAQ;YACX,IAAI,UAAU;gBAAE,OAAO,UAAU,CAAA;YACjC,MAAM,IAAI,qBAAqB,CAAC,6BAA6B,CAAC,CAAA;QAChE,KAAK,QAAQ;YACX,yEAAyE;YACzE,uDAAuD;YACvD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC;gBAAE,OAAO,UAAU,CAAC,CAAC,CAAE,CAAA;YACnE,MAAM,IAAI,qBAAqB,CAAC,yCAAyC,CAAC,CAAA;QAC5E;YACE,OAAO,IAAI,CAAA;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,eAAe,CAAC,GAAkB;IACzC,mEAAmE;IACnE,OAAO,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAA;AAClC,CAAC;AAED,SAAS,QAAQ,CAAC,GAAW;IAC3B,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;IACtB,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,qBAAqB,CAAC,+BAA+B,CAAC,CAAA;IAClE,CAAC;IAED,4EAA4E;IAC5E,yEAAyE;IACzE,0BAA0B;IAE1B,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,qBAAqB,CAAC,yCAAyC,CAAC,CAAA;IAC5E,CAAC;IAED,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1D,MAAM,IAAI,qBAAqB,CAAC,kCAAkC,CAAC,CAAA;IACrE,CAAC;IAED,0EAA0E;IAC1E,2EAA2E;IAC3E,oDAAoD;IAEpD,0CAA0C;IAC1C,OAAO,eAAe,CAAC,GAAG,CAAC,CAAA;AAC7B,CAAC;AAED,SAAS,yBAAyB,CAChC,GAAY,EACZ,KAAa;IAEb,MAAM,GAAG,GACP,GAAG,YAAY,SAAS,IAAI,GAAG,YAAY,eAAe;QACxD,CAAC,CAAC,GAAG,KAAK,KAAK,GAAG,CAAC,OAAO,EAAE;QAC5B,CAAC,CAAC,KAAK,CAAA;IACX,OAAO,IAAI,qBAAqB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;AAC5C,CAAC","sourcesContent":["import { createHash } from 'node:crypto'\nimport { EmbeddedJWK, calculateJwkThumbprint, errors, jwtVerify } from 'jose'\nimport { z } from 'zod'\nimport { ValidationError } from '@atproto/jwk'\nimport { DPOP_NONCE_MAX_AGE } from '../constants.js'\nimport { InvalidDpopProofError } from '../errors/invalid-dpop-proof-error.js'\nimport { UseDpopNonceError } from '../errors/use-dpop-nonce-error.js'\nimport { ifURL } from '../lib/util/cast.js'\nimport {\n DpopNonce,\n DpopSecret,\n dpopSecretSchema,\n rotationIntervalSchema,\n} from './dpop-nonce.js'\nimport { DpopProof } from './dpop-proof.js'\n\nconst { JOSEError } = errors\n\nexport { DpopNonce, type DpopSecret }\n\nexport const dpopManagerOptionsSchema = z.object({\n /**\n * Set this to `false` to disable the use of nonces in DPoP proofs. Set this\n * to a secret Uint8Array or hex encoded string to use a predictable seed for\n * all nonces (typically useful when multiple instances are running). Leave\n * undefined to generate a random seed at startup.\n */\n dpopSecret: z.union([z.literal(false), dpopSecretSchema]).optional(),\n dpopRotationInterval: rotationIntervalSchema.optional(),\n})\nexport type DpopManagerOptions = z.input<typeof dpopManagerOptionsSchema>\n\nexport class DpopManager {\n protected readonly dpopNonce?: DpopNonce\n\n constructor(options: DpopManagerOptions = {}) {\n const { dpopSecret, dpopRotationInterval } =\n dpopManagerOptionsSchema.parse(options)\n this.dpopNonce =\n dpopSecret === false\n ? undefined\n : new DpopNonce(dpopSecret, dpopRotationInterval)\n }\n\n nextNonce(): string | undefined {\n return this.dpopNonce?.next()\n }\n\n /**\n * @see {@link https://datatracker.ietf.org/doc/html/rfc9449#section-4.3}\n */\n async checkProof(\n httpMethod: string,\n httpUrl: Readonly<URL>,\n httpHeaders: Record<string, undefined | string | string[]>,\n accessToken?: string,\n ): Promise<null | DpopProof> {\n // Fool proofing against use of empty string\n if (!httpMethod) {\n throw new TypeError('HTTP method is required')\n }\n\n const proof = extractProof(httpHeaders)\n if (!proof) return null\n\n const { protectedHeader, payload } = await jwtVerify(proof, EmbeddedJWK, {\n typ: 'dpop+jwt',\n maxTokenAge: 10, // Will ensure presence & validity of \"iat\" claim\n clockTolerance: DPOP_NONCE_MAX_AGE / 1e3,\n }).catch((err) => {\n throw wrapInvalidDpopProofError(err, 'Failed to verify DPoP proof')\n })\n\n // @NOTE For legacy & backwards compatibility reason, we cannot use\n // `jwtPayloadSchema` here as it will reject DPoP proofs containing a query\n // or fragment component in the \"htu\" claim.\n\n // const { ath, htm, htu, jti, nonce } = await jwtPayloadSchema\n // .parseAsync(payload)\n // .catch((err) => {\n // throw buildInvalidDpopProofError('Invalid DPoP proof', err)\n // })\n\n // @TODO Uncomment previous lines (and remove redundant checks bellow) once\n // we decide to drop legacy support.\n const { ath, htm, htu, jti, nonce } = payload\n\n if (nonce !== undefined && typeof nonce !== 'string') {\n throw new InvalidDpopProofError('Invalid DPoP \"nonce\" type')\n }\n\n if (!jti || typeof jti !== 'string') {\n throw new InvalidDpopProofError('DPoP \"jti\" missing')\n }\n\n // Note rfc9110#section-9.1 states that the method name is case-sensitive\n if (!htm || htm !== httpMethod) {\n throw new InvalidDpopProofError('DPoP \"htm\" mismatch')\n }\n\n if (!htu || typeof htu !== 'string') {\n throw new InvalidDpopProofError('Invalid DPoP \"htu\" type')\n }\n\n // > To reduce the likelihood of false negatives, servers SHOULD employ\n // > syntax-based normalization (Section 6.2.2 of [RFC3986]) and\n // > scheme-based normalization (Section 6.2.3 of [RFC3986]) before\n // > comparing the htu claim.\n //\n // RFC9449 section 4.3. Checking DPoP Proofs - https://datatracker.ietf.org/doc/html/rfc9449#section-4.3\n if (!htu || parseHtu(htu) !== normalizeHtuUrl(httpUrl)) {\n throw new InvalidDpopProofError('DPoP \"htu\" mismatch')\n }\n\n if (!nonce && this.dpopNonce) {\n throw new UseDpopNonceError()\n }\n\n if (nonce && !this.dpopNonce?.check(nonce)) {\n throw new UseDpopNonceError('DPoP \"nonce\" mismatch')\n }\n\n if (accessToken) {\n const accessTokenHash = createHash('sha256').update(accessToken).digest()\n if (ath !== accessTokenHash.toString('base64url')) {\n throw new InvalidDpopProofError('DPoP \"ath\" mismatch')\n }\n } else if (ath !== undefined) {\n throw new InvalidDpopProofError('DPoP \"ath\" claim not allowed')\n }\n\n // @NOTE we can assert there is a jwk because the jwtVerify used the\n // EmbeddedJWK key getter mechanism.\n const jwk = protectedHeader.jwk!\n const jkt = await calculateJwkThumbprint(jwk, 'sha256').catch((err) => {\n throw wrapInvalidDpopProofError(err, 'Failed to calculate jkt')\n })\n\n // @NOTE We freeze the proof to prevent accidental modification (esp. from\n // hooks).\n return Object.freeze({ jti, jkt, htm, htu })\n }\n}\n\nfunction extractProof(\n httpHeaders: Record<string, undefined | string | string[]>,\n): string | null {\n const dpopHeader = httpHeaders['dpop']\n switch (typeof dpopHeader) {\n case 'string':\n if (dpopHeader) return dpopHeader\n throw new InvalidDpopProofError('DPoP header cannot be empty')\n case 'object':\n // @NOTE the \"0\" case should never happen a node.js HTTP server will only\n // return an array if the header is set multiple times.\n if (dpopHeader.length === 1 && dpopHeader[0]) return dpopHeader[0]!\n throw new InvalidDpopProofError('DPoP header must contain a single proof')\n default:\n return null\n }\n}\n\n/**\n * Constructs the HTTP URI (htu) claim as defined in RFC9449.\n *\n * The htu claim is the normalized URL of the HTTP request, excluding the query\n * string and fragment. This function ensures that the URL is normalized by\n * removing the search and hash components, as well as by using an URL object to\n * simplify the pathname (e.g. removing dot segments).\n *\n * @returns The normalized URL as a string.\n * @see {@link https://datatracker.ietf.org/doc/html/rfc9449#section-4.3}\n */\nfunction normalizeHtuUrl(url: Readonly<URL>): string {\n // NodeJS's `URL` normalizes the pathname, so we can just use that.\n return url.origin + url.pathname\n}\n\nfunction parseHtu(htu: string): string {\n const url = ifURL(htu)\n if (!url) {\n throw new InvalidDpopProofError('DPoP \"htu\" is not a valid URL')\n }\n\n // @NOTE the checks bellow can be removed once once jwtPayloadSchema is used\n // to validate the DPoP proof payload as it already performs these checks\n // (though the htuSchema).\n\n if (url.password || url.username) {\n throw new InvalidDpopProofError('DPoP \"htu\" must not contain credentials')\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new InvalidDpopProofError('DPoP \"htu\" must be http or https')\n }\n\n // @NOTE For legacy & backwards compatibility reason, we allow a query and\n // fragment in the DPoP proof's htu. This is not a standard behavior as the\n // htu is not supposed to contain query or fragment.\n\n // NodeJS's `URL` normalizes the pathname.\n return normalizeHtuUrl(url)\n}\n\nfunction wrapInvalidDpopProofError(\n err: unknown,\n title: string,\n): InvalidDpopProofError {\n const msg =\n err instanceof JOSEError || err instanceof ValidationError\n ? `${title}: ${err.message}`\n : title\n return new InvalidDpopProofError(msg, err)\n}\n"]}
1
+ {"version":3,"file":"dpop-manager.js","sourceRoot":"","sources":["../../src/dpop/dpop-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto'\nimport { EmbeddedJWK, calculateJwkThumbprint, errors, jwtVerify } from 'jose'\nimport { z } from 'zod'\nimport { ValidationError } from '@atproto/jwk'\nimport { InvalidDpopProofError } from '../errors/invalid-dpop-proof-error.js'\nimport { UseDpopNonceError } from '../errors/use-dpop-nonce-error.js'\nimport { ifURL } from '../lib/util/cast.js'\nimport { DPOP_NONCE_MAX_AGE } from '../oauth-constants.js'\nimport {\n DpopNonce,\n type DpopSecret,\n dpopSecretSchema,\n rotationIntervalSchema,\n} from './dpop-nonce.js'\nimport type { DpopProof } from './dpop-proof.js'\n\nconst { JOSEError } = errors\n\nexport { DpopNonce, type DpopSecret }\n\nexport const dpopManagerOptionsSchema = z.object({\n /**\n * Set this to `false` to disable the use of nonces in DPoP proofs. Set this\n * to a secret Uint8Array or hex encoded string to use a predictable seed for\n * all nonces (typically useful when multiple instances are running). Leave\n * undefined to generate a random seed at startup.\n */\n dpopSecret: z.union([z.literal(false), dpopSecretSchema]).optional(),\n dpopRotationInterval: rotationIntervalSchema.optional(),\n})\nexport type DpopManagerOptions = z.input<typeof dpopManagerOptionsSchema>\n\nexport class DpopManager {\n protected readonly dpopNonce?: DpopNonce\n\n constructor(options: DpopManagerOptions = {}) {\n const { dpopSecret, dpopRotationInterval } =\n dpopManagerOptionsSchema.parse(options)\n this.dpopNonce =\n dpopSecret === false\n ? undefined\n : new DpopNonce(dpopSecret, dpopRotationInterval)\n }\n\n nextNonce(): string | undefined {\n return this.dpopNonce?.next()\n }\n\n /**\n * @see {@link https://datatracker.ietf.org/doc/html/rfc9449#section-4.3}\n */\n async checkProof(\n httpMethod: string,\n httpUrl: Readonly<URL>,\n httpHeaders: Record<string, undefined | string | string[]>,\n accessToken?: string,\n ): Promise<null | DpopProof> {\n // Fool proofing against use of empty string\n if (!httpMethod) {\n throw new TypeError('HTTP method is required')\n }\n\n const proof = extractProof(httpHeaders)\n if (!proof) return null\n\n const { protectedHeader, payload } = await jwtVerify(proof, EmbeddedJWK, {\n typ: 'dpop+jwt',\n maxTokenAge: 10, // Will ensure presence & validity of \"iat\" claim\n clockTolerance: DPOP_NONCE_MAX_AGE / 1e3,\n }).catch((err) => {\n throw wrapInvalidDpopProofError(err, 'Failed to verify DPoP proof')\n })\n\n // @NOTE For legacy & backwards compatibility reason, we cannot use\n // `jwtPayloadSchema` here as it will reject DPoP proofs containing a query\n // or fragment component in the \"htu\" claim.\n\n // const { ath, htm, htu, jti, nonce } = await jwtPayloadSchema\n // .parseAsync(payload)\n // .catch((err) => {\n // throw buildInvalidDpopProofError('Invalid DPoP proof', err)\n // })\n\n // @TODO Uncomment previous lines (and remove redundant checks bellow) once\n // we decide to drop legacy support.\n const { ath, htm, htu, jti, nonce } = payload\n\n if (nonce !== undefined && typeof nonce !== 'string') {\n throw new InvalidDpopProofError('Invalid DPoP \"nonce\" type')\n }\n\n if (!jti || typeof jti !== 'string') {\n throw new InvalidDpopProofError('DPoP \"jti\" missing')\n }\n\n // Note rfc9110#section-9.1 states that the method name is case-sensitive\n if (!htm || htm !== httpMethod) {\n throw new InvalidDpopProofError('DPoP \"htm\" mismatch')\n }\n\n if (!htu || typeof htu !== 'string') {\n throw new InvalidDpopProofError('Invalid DPoP \"htu\" type')\n }\n\n // > To reduce the likelihood of false negatives, servers SHOULD employ\n // > syntax-based normalization (Section 6.2.2 of [RFC3986]) and\n // > scheme-based normalization (Section 6.2.3 of [RFC3986]) before\n // > comparing the htu claim.\n //\n // RFC9449 section 4.3. Checking DPoP Proofs - https://datatracker.ietf.org/doc/html/rfc9449#section-4.3\n if (!htu || parseHtu(htu) !== normalizeHtuUrl(httpUrl)) {\n throw new InvalidDpopProofError('DPoP \"htu\" mismatch')\n }\n\n if (!nonce && this.dpopNonce) {\n throw new UseDpopNonceError()\n }\n\n if (nonce && !this.dpopNonce?.check(nonce)) {\n throw new UseDpopNonceError('DPoP \"nonce\" mismatch')\n }\n\n if (accessToken) {\n const accessTokenHash = createHash('sha256').update(accessToken).digest()\n if (ath !== accessTokenHash.toString('base64url')) {\n throw new InvalidDpopProofError('DPoP \"ath\" mismatch')\n }\n } else if (ath !== undefined) {\n throw new InvalidDpopProofError('DPoP \"ath\" claim not allowed')\n }\n\n // @NOTE we can assert there is a jwk because the jwtVerify used the\n // EmbeddedJWK key getter mechanism.\n const jwk = protectedHeader.jwk!\n const jkt = await calculateJwkThumbprint(jwk, 'sha256').catch((err) => {\n throw wrapInvalidDpopProofError(err, 'Failed to calculate jkt')\n })\n\n // @NOTE We freeze the proof to prevent accidental modification (esp. from\n // hooks).\n return Object.freeze({ jti, jkt, htm, htu })\n }\n}\n\nfunction extractProof(\n httpHeaders: Record<string, undefined | string | string[]>,\n): string | null {\n const dpopHeader = httpHeaders['dpop']\n switch (typeof dpopHeader) {\n case 'string':\n if (dpopHeader) return dpopHeader\n throw new InvalidDpopProofError('DPoP header cannot be empty')\n case 'object':\n // @NOTE the \"0\" case should never happen a node.js HTTP server will only\n // return an array if the header is set multiple times.\n if (dpopHeader.length === 1 && dpopHeader[0]) return dpopHeader[0]!\n throw new InvalidDpopProofError('DPoP header must contain a single proof')\n default:\n return null\n }\n}\n\n/**\n * Constructs the HTTP URI (htu) claim as defined in RFC9449.\n *\n * The htu claim is the normalized URL of the HTTP request, excluding the query\n * string and fragment. This function ensures that the URL is normalized by\n * removing the search and hash components, as well as by using an URL object to\n * simplify the pathname (e.g. removing dot segments).\n *\n * @returns The normalized URL as a string.\n * @see {@link https://datatracker.ietf.org/doc/html/rfc9449#section-4.3}\n */\nfunction normalizeHtuUrl(url: Readonly<URL>): string {\n // NodeJS's `URL` normalizes the pathname, so we can just use that.\n return url.origin + url.pathname\n}\n\nfunction parseHtu(htu: string): string {\n const url = ifURL(htu)\n if (!url) {\n throw new InvalidDpopProofError('DPoP \"htu\" is not a valid URL')\n }\n\n // @NOTE the checks bellow can be removed once once jwtPayloadSchema is used\n // to validate the DPoP proof payload as it already performs these checks\n // (though the htuSchema).\n\n if (url.password || url.username) {\n throw new InvalidDpopProofError('DPoP \"htu\" must not contain credentials')\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new InvalidDpopProofError('DPoP \"htu\" must be http or https')\n }\n\n // @NOTE For legacy & backwards compatibility reason, we allow a query and\n // fragment in the DPoP proof's htu. This is not a standard behavior as the\n // htu is not supposed to contain query or fragment.\n\n // NodeJS's `URL` normalizes the pathname.\n return normalizeHtuUrl(url)\n}\n\nfunction wrapInvalidDpopProofError(\n err: unknown,\n title: string,\n): InvalidDpopProofError {\n const msg =\n err instanceof JOSEError || err instanceof ValidationError\n ? `${title}: ${err.message}`\n : title\n return new InvalidDpopProofError(msg, err)\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { createHmac, randomBytes } from 'node:crypto';
2
2
  import { z } from 'zod';
3
- import { DPOP_NONCE_MAX_AGE } from '../constants.js';
3
+ import { DPOP_NONCE_MAX_AGE } from '../oauth-constants.js';
4
4
  const MAX_ROTATION_INTERVAL = DPOP_NONCE_MAX_AGE / 3;
5
5
  const MIN_ROTATION_INTERVAL = Math.min(1000, MAX_ROTATION_INTERVAL);
6
6
  export const rotationIntervalSchema = z
@@ -1 +1 @@
1
- {"version":3,"file":"dpop-nonce.js","sourceRoot":"","sources":["../../src/dpop/dpop-nonce.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHmac, randomBytes } from 'node:crypto'\nimport { z } from 'zod'\nimport { DPOP_NONCE_MAX_AGE } from '../constants.js'\n\nconst MAX_ROTATION_INTERVAL = DPOP_NONCE_MAX_AGE / 3\nconst MIN_ROTATION_INTERVAL = Math.min(1000, MAX_ROTATION_INTERVAL)\n\nexport const rotationIntervalSchema = z\n .number()\n .int()\n .min(MIN_ROTATION_INTERVAL)\n .max(MAX_ROTATION_INTERVAL)\n\nconst SECRET_BYTE_LENGTH = 32\n\nexport const secretBytesSchema = z\n .instanceof(Uint8Array<ArrayBufferLike>)\n .refine((secret) => secret.length === SECRET_BYTE_LENGTH, {\n message: `Secret must be exactly ${SECRET_BYTE_LENGTH} bytes long`,\n })\n\nexport const secretHexSchema = z\n .string()\n .regex(\n /^[0-9a-f]+$/i,\n `Secret must be a ${SECRET_BYTE_LENGTH * 2} chars hex string`,\n )\n .length(SECRET_BYTE_LENGTH * 2)\n .transform((hex): Uint8Array => Buffer.from(hex, 'hex'))\n\nexport const dpopSecretSchema = z.union([secretBytesSchema, secretHexSchema])\nexport type DpopSecret = z.input<typeof dpopSecretSchema>\n\nexport class DpopNonce {\n readonly #rotationInterval: number\n readonly #secret: Uint8Array\n\n // Nonce state\n #counter: number\n #prev: string\n #now: string\n #next: string\n\n constructor(\n secret: DpopSecret = randomBytes(SECRET_BYTE_LENGTH),\n rotationInterval = MAX_ROTATION_INTERVAL,\n ) {\n this.#rotationInterval = rotationIntervalSchema.parse(rotationInterval)\n this.#secret = Uint8Array.from(dpopSecretSchema.parse(secret))\n\n this.#counter = this.currentCounter\n this.#prev = this.compute(this.#counter - 1)\n this.#now = this.compute(this.#counter)\n this.#next = this.compute(this.#counter + 1)\n }\n\n /**\n * Returns the number of full rotations since the epoch\n */\n protected get currentCounter() {\n return (Date.now() / this.#rotationInterval) | 0\n }\n\n protected rotate() {\n const counter = this.currentCounter\n switch (counter - this.#counter) {\n case 0:\n // counter === this.#counter => nothing to do\n return\n case 1:\n // Optimization: avoid recomputing #prev & #now\n this.#prev = this.#now\n this.#now = this.#next\n this.#next = this.compute(counter + 1)\n break\n case 2:\n // Optimization: avoid recomputing #prev\n this.#prev = this.#next\n this.#now = this.compute(counter)\n this.#next = this.compute(counter + 1)\n break\n default:\n // All nonces are outdated, so we recompute all of them\n this.#prev = this.compute(counter - 1)\n this.#now = this.compute(counter)\n this.#next = this.compute(counter + 1)\n break\n }\n this.#counter = counter\n }\n\n protected compute(counter: number) {\n return createHmac('sha256', this.#secret)\n .update(numTo64bits(counter))\n .digest()\n .toString('base64url')\n }\n\n public next() {\n this.rotate()\n return this.#next\n }\n\n public check(nonce: string) {\n return this.#next === nonce || this.#now === nonce || this.#prev === nonce\n }\n}\n\nfunction numTo64bits(num: number) {\n const arr = new Uint8Array(8)\n // @NOTE Assigning to an uint8 will only keep the last 8 int bits\n arr[7] = num |= 0\n arr[6] = num >>= 8\n arr[5] = num >>= 8\n arr[4] = num >>= 8\n arr[3] = num >>= 8\n arr[2] = num >>= 8\n arr[1] = num >>= 8\n arr[0] = num >>= 8\n return arr\n}\n"]}
1
+ {"version":3,"file":"dpop-nonce.js","sourceRoot":"","sources":["../../src/dpop/dpop-nonce.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHmac, randomBytes } from 'node:crypto'\nimport { z } from 'zod'\nimport { DPOP_NONCE_MAX_AGE } from '../oauth-constants.js'\n\nconst MAX_ROTATION_INTERVAL = DPOP_NONCE_MAX_AGE / 3\nconst MIN_ROTATION_INTERVAL = Math.min(1000, MAX_ROTATION_INTERVAL)\n\nexport const rotationIntervalSchema = z\n .number()\n .int()\n .min(MIN_ROTATION_INTERVAL)\n .max(MAX_ROTATION_INTERVAL)\n\nconst SECRET_BYTE_LENGTH = 32\n\nexport const secretBytesSchema = z\n .instanceof(Uint8Array<ArrayBufferLike>)\n .refine((secret) => secret.length === SECRET_BYTE_LENGTH, {\n message: `Secret must be exactly ${SECRET_BYTE_LENGTH} bytes long`,\n })\n\nexport const secretHexSchema = z\n .string()\n .regex(\n /^[0-9a-f]+$/i,\n `Secret must be a ${SECRET_BYTE_LENGTH * 2} chars hex string`,\n )\n .length(SECRET_BYTE_LENGTH * 2)\n .transform((hex): Uint8Array => Buffer.from(hex, 'hex'))\n\nexport const dpopSecretSchema = z.union([secretBytesSchema, secretHexSchema])\nexport type DpopSecret = z.input<typeof dpopSecretSchema>\n\nexport class DpopNonce {\n readonly #rotationInterval: number\n readonly #secret: Uint8Array\n\n // Nonce state\n #counter: number\n #prev: string\n #now: string\n #next: string\n\n constructor(\n secret: DpopSecret = randomBytes(SECRET_BYTE_LENGTH),\n rotationInterval = MAX_ROTATION_INTERVAL,\n ) {\n this.#rotationInterval = rotationIntervalSchema.parse(rotationInterval)\n this.#secret = Uint8Array.from(dpopSecretSchema.parse(secret))\n\n this.#counter = this.currentCounter\n this.#prev = this.compute(this.#counter - 1)\n this.#now = this.compute(this.#counter)\n this.#next = this.compute(this.#counter + 1)\n }\n\n /**\n * Returns the number of full rotations since the epoch\n */\n protected get currentCounter() {\n return (Date.now() / this.#rotationInterval) | 0\n }\n\n protected rotate() {\n const counter = this.currentCounter\n switch (counter - this.#counter) {\n case 0:\n // counter === this.#counter => nothing to do\n return\n case 1:\n // Optimization: avoid recomputing #prev & #now\n this.#prev = this.#now\n this.#now = this.#next\n this.#next = this.compute(counter + 1)\n break\n case 2:\n // Optimization: avoid recomputing #prev\n this.#prev = this.#next\n this.#now = this.compute(counter)\n this.#next = this.compute(counter + 1)\n break\n default:\n // All nonces are outdated, so we recompute all of them\n this.#prev = this.compute(counter - 1)\n this.#now = this.compute(counter)\n this.#next = this.compute(counter + 1)\n break\n }\n this.#counter = counter\n }\n\n protected compute(counter: number) {\n return createHmac('sha256', this.#secret)\n .update(numTo64bits(counter))\n .digest()\n .toString('base64url')\n }\n\n public next() {\n this.rotate()\n return this.#next\n }\n\n public check(nonce: string) {\n return this.#next === nonce || this.#now === nonce || this.#prev === nonce\n }\n}\n\nfunction numTo64bits(num: number) {\n const arr = new Uint8Array(8)\n // @NOTE Assigning to an uint8 will only keep the last 8 int bits\n arr[7] = num |= 0\n arr[6] = num >>= 8\n arr[5] = num >>= 8\n arr[4] = num >>= 8\n arr[3] = num >>= 8\n arr[2] = num >>= 8\n arr[1] = num >>= 8\n arr[0] = num >>= 8\n return arr\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types';
1
+ import type { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types';
2
2
  import { AuthorizationError } from './authorization-error.js';
3
3
  export declare class AccessDeniedError extends AuthorizationError {
4
4
  constructor(parameters: OAuthAuthorizationRequestParameters, error_description?: string, cause?: unknown);
@@ -1 +1 @@
1
- {"version":3,"file":"access-denied-error.d.ts","sourceRoot":"","sources":["../../src/errors/access-denied-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAA;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,qBAAa,iBAAkB,SAAQ,kBAAkB;IACvD,YACE,UAAU,EAAE,mCAAmC,EAC/C,iBAAiB,SAAkB,EACnC,KAAK,CAAC,EAAE,OAAO,EAGhB;CACF"}
1
+ {"version":3,"file":"access-denied-error.d.ts","sourceRoot":"","sources":["../../src/errors/access-denied-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,qBAAa,iBAAkB,SAAQ,kBAAkB;IACvD,YACE,UAAU,EAAE,mCAAmC,EAC/C,iBAAiB,SAAkB,EACnC,KAAK,CAAC,EAAE,OAAO,EAGhB;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"access-denied-error.js","sourceRoot":"","sources":["../../src/errors/access-denied-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,MAAM,OAAO,iBAAkB,SAAQ,kBAAkB;IACvD,YACE,UAA+C,EAC/C,iBAAiB,GAAG,eAAe,EACnC,KAAe;QAEf,KAAK,CAAC,UAAU,EAAE,iBAAiB,EAAE,eAAe,EAAE,KAAK,CAAC,CAAA;IAC9D,CAAC;CACF","sourcesContent":["import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'\nimport { AuthorizationError } from './authorization-error.js'\n\nexport class AccessDeniedError extends AuthorizationError {\n constructor(\n parameters: OAuthAuthorizationRequestParameters,\n error_description = 'Access denied',\n cause?: unknown,\n ) {\n super(parameters, error_description, 'access_denied', cause)\n }\n}\n"]}
1
+ {"version":3,"file":"access-denied-error.js","sourceRoot":"","sources":["../../src/errors/access-denied-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,MAAM,OAAO,iBAAkB,SAAQ,kBAAkB;IACvD,YACE,UAA+C,EAC/C,iBAAiB,GAAG,eAAe,EACnC,KAAe;QAEf,KAAK,CAAC,UAAU,EAAE,iBAAiB,EAAE,eAAe,EAAE,KAAK,CAAC,CAAA;IAC9D,CAAC;CACF","sourcesContent":["import type { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'\nimport { AuthorizationError } from './authorization-error.js'\n\nexport class AccessDeniedError extends AuthorizationError {\n constructor(\n parameters: OAuthAuthorizationRequestParameters,\n error_description = 'Access denied',\n cause?: unknown,\n ) {\n super(parameters, error_description, 'access_denied', cause)\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types';
1
+ import type { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types';
2
2
  import { AuthorizationError } from './authorization-error.js';
3
3
  export declare class AccountSelectionRequiredError extends AuthorizationError {
4
4
  constructor(parameters: OAuthAuthorizationRequestParameters, error_description?: string, cause?: unknown);
@@ -1 +1 @@
1
- {"version":3,"file":"account-selection-required-error.d.ts","sourceRoot":"","sources":["../../src/errors/account-selection-required-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAA;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,qBAAa,6BAA8B,SAAQ,kBAAkB;IACnE,YACE,UAAU,EAAE,mCAAmC,EAC/C,iBAAiB,SAA+B,EAChD,KAAK,CAAC,EAAE,OAAO,EAGhB;CACF"}
1
+ {"version":3,"file":"account-selection-required-error.d.ts","sourceRoot":"","sources":["../../src/errors/account-selection-required-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,qBAAa,6BAA8B,SAAQ,kBAAkB;IACnE,YACE,UAAU,EAAE,mCAAmC,EAC/C,iBAAiB,SAA+B,EAChD,KAAK,CAAC,EAAE,OAAO,EAGhB;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"account-selection-required-error.js","sourceRoot":"","sources":["../../src/errors/account-selection-required-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,MAAM,OAAO,6BAA8B,SAAQ,kBAAkB;IACnE,YACE,UAA+C,EAC/C,iBAAiB,GAAG,4BAA4B,EAChD,KAAe;QAEf,KAAK,CAAC,UAAU,EAAE,iBAAiB,EAAE,4BAA4B,EAAE,KAAK,CAAC,CAAA;IAC3E,CAAC;CACF","sourcesContent":["import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'\nimport { AuthorizationError } from './authorization-error.js'\n\nexport class AccountSelectionRequiredError extends AuthorizationError {\n constructor(\n parameters: OAuthAuthorizationRequestParameters,\n error_description = 'Account selection required',\n cause?: unknown,\n ) {\n super(parameters, error_description, 'account_selection_required', cause)\n }\n}\n"]}
1
+ {"version":3,"file":"account-selection-required-error.js","sourceRoot":"","sources":["../../src/errors/account-selection-required-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,MAAM,OAAO,6BAA8B,SAAQ,kBAAkB;IACnE,YACE,UAA+C,EAC/C,iBAAiB,GAAG,4BAA4B,EAChD,KAAe;QAEf,KAAK,CAAC,UAAU,EAAE,iBAAiB,EAAE,4BAA4B,EAAE,KAAK,CAAC,CAAA;IAC3E,CAAC;CACF","sourcesContent":["import type { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'\nimport { AuthorizationError } from './authorization-error.js'\n\nexport class AccountSelectionRequiredError extends AuthorizationError {\n constructor(\n parameters: OAuthAuthorizationRequestParameters,\n error_description = 'Account selection required',\n cause?: unknown,\n ) {\n super(parameters, error_description, 'account_selection_required', cause)\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types';
2
- import { AuthorizationResponseError } from '../types/authorization-response-error.js';
1
+ import type { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types';
2
+ import { type AuthorizationResponseError } from '../types/authorization-response-error.js';
3
3
  import { OAuthError } from './oauth-error.js';
4
4
  export type { AuthorizationResponseError, OAuthAuthorizationRequestParameters };
5
5
  export declare class AuthorizationError extends OAuthError {
@@ -1 +1 @@
1
- {"version":3,"file":"authorization-error.d.ts","sourceRoot":"","sources":["../../src/errors/authorization-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAA;AAC1E,OAAO,EACL,0BAA0B,EAE3B,MAAM,0CAA0C,CAAA;AAEjD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,YAAY,EAAE,0BAA0B,EAAE,mCAAmC,EAAE,CAAA;AAE/E,qBAAa,kBAAmB,SAAQ,UAAU;aAE9B,UAAU,EAAE,mCAAmC;IADjE,YACkB,UAAU,EAAE,mCAAmC,EAC/D,iBAAiB,EAAE,MAAM,EACzB,KAAK,GAAE,0BAA8C,EACrD,KAAK,CAAC,EAAE,OAAO,EAGhB;IAED,MAAM,CAAC,IAAI,CACT,UAAU,EAAE,mCAAmC,EAC/C,KAAK,EAAE,OAAO,GACb,kBAAkB,CAapB;CACF"}
1
+ {"version":3,"file":"authorization-error.d.ts","sourceRoot":"","sources":["../../src/errors/authorization-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EACL,KAAK,0BAA0B,EAEhC,MAAM,0CAA0C,CAAA;AAEjD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,YAAY,EAAE,0BAA0B,EAAE,mCAAmC,EAAE,CAAA;AAE/E,qBAAa,kBAAmB,SAAQ,UAAU;aAE9B,UAAU,EAAE,mCAAmC;IADjE,YACkB,UAAU,EAAE,mCAAmC,EAC/D,iBAAiB,EAAE,MAAM,EACzB,KAAK,GAAE,0BAA8C,EACrD,KAAK,CAAC,EAAE,OAAO,EAGhB;IAED,MAAM,CAAC,IAAI,CACT,UAAU,EAAE,mCAAmC,EAC/C,KAAK,EAAE,OAAO,GACb,kBAAkB,CAapB;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"authorization-error.js","sourceRoot":"","sources":["../../src/errors/authorization-error.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,4BAA4B,GAC7B,MAAM,0CAA0C,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAI7C,MAAM,OAAO,kBAAmB,SAAQ,UAAU;IAChD,YACkB,UAA+C,EAC/D,iBAAyB,EACzB,KAAK,GAA+B,iBAAiB,EACrD,KAAe;QAEf,KAAK,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;0BAL3B,UAAU;IAM5B,CAAC;IAED,MAAM,CAAC,IAAI,CACT,UAA+C,EAC/C,KAAc;QAEd,IAAI,KAAK,YAAY,kBAAkB;YAAE,OAAO,KAAK,CAAA;QACrD,MAAM,OAAO,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAA;QACxC,OAAO,IAAI,kBAAkB,CAC3B,UAAU,EACV,OAAO,CAAC,iBAAiB,EACzB,4BAA4B,CAAC,OAAO,CAAC,KAAK,CAAC;YACzC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,2CAA2C;YAC3D,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,YAAY,UAAU;gBACtC,CAAC,CAAC,iBAAiB;gBACnB,CAAC,CAAC,cAAc,EACpB,KAAK,CACN,CAAA;IACH,CAAC;CACF;AAED,SAAS,SAAS,CAAC,GAAY;IAC7B,OAAO,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;QACjD,GAAG,GAAG,GAAG,CAAC,KAAK,CAAA;IACjB,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC","sourcesContent":["import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'\nimport {\n AuthorizationResponseError,\n isAuthorizationResponseError,\n} from '../types/authorization-response-error.js'\nimport { buildErrorPayload } from './error-parser.js'\nimport { OAuthError } from './oauth-error.js'\n\nexport type { AuthorizationResponseError, OAuthAuthorizationRequestParameters }\n\nexport class AuthorizationError extends OAuthError {\n constructor(\n public readonly parameters: OAuthAuthorizationRequestParameters,\n error_description: string,\n error: AuthorizationResponseError = 'invalid_request',\n cause?: unknown,\n ) {\n super(error, error_description, 400, cause)\n }\n\n static from(\n parameters: OAuthAuthorizationRequestParameters,\n cause: unknown,\n ): AuthorizationError {\n if (cause instanceof AuthorizationError) return cause\n const payload = buildErrorPayload(cause)\n return new AuthorizationError(\n parameters,\n payload.error_description,\n isAuthorizationResponseError(payload.error)\n ? payload.error // Propagate \"error\" derived from the cause\n : rootCause(cause) instanceof OAuthError\n ? 'invalid_request'\n : 'server_error',\n cause,\n )\n }\n}\n\nfunction rootCause(err: unknown): unknown {\n while (err instanceof Error && err.cause != null) {\n err = err.cause\n }\n return err\n}\n"]}
1
+ {"version":3,"file":"authorization-error.js","sourceRoot":"","sources":["../../src/errors/authorization-error.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,4BAA4B,GAC7B,MAAM,0CAA0C,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAI7C,MAAM,OAAO,kBAAmB,SAAQ,UAAU;IAChD,YACkB,UAA+C,EAC/D,iBAAyB,EACzB,KAAK,GAA+B,iBAAiB,EACrD,KAAe;QAEf,KAAK,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;0BAL3B,UAAU;IAM5B,CAAC;IAED,MAAM,CAAC,IAAI,CACT,UAA+C,EAC/C,KAAc;QAEd,IAAI,KAAK,YAAY,kBAAkB;YAAE,OAAO,KAAK,CAAA;QACrD,MAAM,OAAO,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAA;QACxC,OAAO,IAAI,kBAAkB,CAC3B,UAAU,EACV,OAAO,CAAC,iBAAiB,EACzB,4BAA4B,CAAC,OAAO,CAAC,KAAK,CAAC;YACzC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,2CAA2C;YAC3D,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,YAAY,UAAU;gBACtC,CAAC,CAAC,iBAAiB;gBACnB,CAAC,CAAC,cAAc,EACpB,KAAK,CACN,CAAA;IACH,CAAC;CACF;AAED,SAAS,SAAS,CAAC,GAAY;IAC7B,OAAO,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;QACjD,GAAG,GAAG,GAAG,CAAC,KAAK,CAAA;IACjB,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC","sourcesContent":["import type { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'\nimport {\n type AuthorizationResponseError,\n isAuthorizationResponseError,\n} from '../types/authorization-response-error.js'\nimport { buildErrorPayload } from './error-parser.js'\nimport { OAuthError } from './oauth-error.js'\n\nexport type { AuthorizationResponseError, OAuthAuthorizationRequestParameters }\n\nexport class AuthorizationError extends OAuthError {\n constructor(\n public readonly parameters: OAuthAuthorizationRequestParameters,\n error_description: string,\n error: AuthorizationResponseError = 'invalid_request',\n cause?: unknown,\n ) {\n super(error, error_description, 400, cause)\n }\n\n static from(\n parameters: OAuthAuthorizationRequestParameters,\n cause: unknown,\n ): AuthorizationError {\n if (cause instanceof AuthorizationError) return cause\n const payload = buildErrorPayload(cause)\n return new AuthorizationError(\n parameters,\n payload.error_description,\n isAuthorizationResponseError(payload.error)\n ? payload.error // Propagate \"error\" derived from the cause\n : rootCause(cause) instanceof OAuthError\n ? 'invalid_request'\n : 'server_error',\n cause,\n )\n }\n}\n\nfunction rootCause(err: unknown): unknown {\n while (err instanceof Error && err.cause != null) {\n err = err.cause\n }\n return err\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types';
1
+ import type { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types';
2
2
  import { AuthorizationError } from './authorization-error.js';
3
3
  export declare class ConsentRequiredError extends AuthorizationError {
4
4
  constructor(parameters: OAuthAuthorizationRequestParameters, error_description?: string, cause?: unknown);
@@ -1 +1 @@
1
- {"version":3,"file":"consent-required-error.d.ts","sourceRoot":"","sources":["../../src/errors/consent-required-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAA;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,qBAAa,oBAAqB,SAAQ,kBAAkB;IAC1D,YACE,UAAU,EAAE,mCAAmC,EAC/C,iBAAiB,SAA0B,EAC3C,KAAK,CAAC,EAAE,OAAO,EAGhB;CACF"}
1
+ {"version":3,"file":"consent-required-error.d.ts","sourceRoot":"","sources":["../../src/errors/consent-required-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,qBAAa,oBAAqB,SAAQ,kBAAkB;IAC1D,YACE,UAAU,EAAE,mCAAmC,EAC/C,iBAAiB,SAA0B,EAC3C,KAAK,CAAC,EAAE,OAAO,EAGhB;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"consent-required-error.js","sourceRoot":"","sources":["../../src/errors/consent-required-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,MAAM,OAAO,oBAAqB,SAAQ,kBAAkB;IAC1D,YACE,UAA+C,EAC/C,iBAAiB,GAAG,uBAAuB,EAC3C,KAAe;QAEf,KAAK,CAAC,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,KAAK,CAAC,CAAA;IACjE,CAAC;CACF","sourcesContent":["import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'\nimport { AuthorizationError } from './authorization-error.js'\n\nexport class ConsentRequiredError extends AuthorizationError {\n constructor(\n parameters: OAuthAuthorizationRequestParameters,\n error_description = 'User consent required',\n cause?: unknown,\n ) {\n super(parameters, error_description, 'consent_required', cause)\n }\n}\n"]}
1
+ {"version":3,"file":"consent-required-error.js","sourceRoot":"","sources":["../../src/errors/consent-required-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,MAAM,OAAO,oBAAqB,SAAQ,kBAAkB;IAC1D,YACE,UAA+C,EAC/C,iBAAiB,GAAG,uBAAuB,EAC3C,KAAe;QAEf,KAAK,CAAC,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,KAAK,CAAC,CAAA;IACjE,CAAC;CACF","sourcesContent":["import type { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'\nimport { AuthorizationError } from './authorization-error.js'\n\nexport class ConsentRequiredError extends AuthorizationError {\n constructor(\n parameters: OAuthAuthorizationRequestParameters,\n error_description = 'User consent required',\n cause?: unknown,\n ) {\n super(parameters, error_description, 'consent_required', cause)\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { HandleUnavailableReason } from '@atproto/oauth-provider-api';
1
+ import type { HandleUnavailableReason } from '@atproto/oauth-provider-api';
2
2
  import { OAuthError } from './oauth-error.js';
3
3
  export type { HandleUnavailableReason };
4
4
  export declare class HandleUnavailableError extends OAuthError {
@@ -1 +1 @@
1
- {"version":3,"file":"handle-unavailable-error.d.ts","sourceRoot":"","sources":["../../src/errors/handle-unavailable-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAA;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAI7C,YAAY,EAAE,uBAAuB,EAAE,CAAA;AAEvC,qBAAa,sBAAuB,SAAQ,UAAU;IAElD,QAAQ,CAAC,MAAM,EAAE,uBAAuB;IAD1C,YACW,MAAM,EAAE,uBAAuB,EACxC,OAAO,GAAE,MAAuC,EAChD,KAAK,CAAC,EAAE,OAAO,EAGhB;IAED,MAAM;;;;MAKL;CACF"}
1
+ {"version":3,"file":"handle-unavailable-error.d.ts","sourceRoot":"","sources":["../../src/errors/handle-unavailable-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAA;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAI7C,YAAY,EAAE,uBAAuB,EAAE,CAAA;AAEvC,qBAAa,sBAAuB,SAAQ,UAAU;IAElD,QAAQ,CAAC,MAAM,EAAE,uBAAuB;IAD1C,YACW,MAAM,EAAE,uBAAuB,EACxC,OAAO,GAAE,MAAuC,EAChD,KAAK,CAAC,EAAE,OAAO,EAGhB;IAED,MAAM;;;;MAKL;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"handle-unavailable-error.js","sourceRoot":"","sources":["../../src/errors/handle-unavailable-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAM7C,MAAM,OAAO,sBAAuB,SAAQ,UAAU;IACpD,YACW,MAA+B,EACxC,OAAO,GAAW,8BAA8B,EAChD,KAAe;QAEf,KAAK,CAAC,oBAAoB,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;sBAJvC,MAAM;IAKjB,CAAC;IAED,MAAM;QACJ,OAAO;YACL,GAAG,KAAK,CAAC,MAAM,EAAE;YACjB,MAAM,EAAE,IAAI,CAAC,MAAM;SACX,CAAA;IACZ,CAAC;CACF","sourcesContent":["import { HandleUnavailableReason } from '@atproto/oauth-provider-api'\nimport { OAuthError } from './oauth-error.js'\n\n// @TODO this is *not* and \"OAuthError\" error but rather an ApiError.\n\nexport type { HandleUnavailableReason }\n\nexport class HandleUnavailableError extends OAuthError {\n constructor(\n readonly reason: HandleUnavailableReason,\n details: string = 'That handle is not available',\n cause?: unknown,\n ) {\n super('handle_unavailable', details, 400, cause)\n }\n\n toJSON() {\n return {\n ...super.toJSON(),\n reason: this.reason,\n } as const\n }\n}\n"]}
1
+ {"version":3,"file":"handle-unavailable-error.js","sourceRoot":"","sources":["../../src/errors/handle-unavailable-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAM7C,MAAM,OAAO,sBAAuB,SAAQ,UAAU;IACpD,YACW,MAA+B,EACxC,OAAO,GAAW,8BAA8B,EAChD,KAAe;QAEf,KAAK,CAAC,oBAAoB,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;sBAJvC,MAAM;IAKjB,CAAC;IAED,MAAM;QACJ,OAAO;YACL,GAAG,KAAK,CAAC,MAAM,EAAE;YACjB,MAAM,EAAE,IAAI,CAAC,MAAM;SACX,CAAA;IACZ,CAAC;CACF","sourcesContent":["import type { HandleUnavailableReason } from '@atproto/oauth-provider-api'\nimport { OAuthError } from './oauth-error.js'\n\n// @TODO this is *not* and \"OAuthError\" error but rather an ApiError.\n\nexport type { HandleUnavailableReason }\n\nexport class HandleUnavailableError extends OAuthError {\n constructor(\n readonly reason: HandleUnavailableReason,\n details: string = 'That handle is not available',\n cause?: unknown,\n ) {\n super('handle_unavailable', details, 400, cause)\n }\n\n toJSON() {\n return {\n ...super.toJSON(),\n reason: this.reason,\n } as const\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types';
1
+ import type { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types';
2
2
  import { AuthorizationError } from './authorization-error.js';
3
3
  /**
4
4
  * @see
@@ -1 +1 @@
1
- {"version":3,"file":"invalid-authorization-details-error.d.ts","sourceRoot":"","sources":["../../src/errors/invalid-authorization-details-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAA;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gCAAiC,SAAQ,kBAAkB;IACtE,YACE,UAAU,EAAE,mCAAmC,EAC/C,iBAAiB,EAAE,MAAM,EACzB,KAAK,CAAC,EAAE,OAAO,EAGhB;CACF"}
1
+ {"version":3,"file":"invalid-authorization-details-error.d.ts","sourceRoot":"","sources":["../../src/errors/invalid-authorization-details-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gCAAiC,SAAQ,kBAAkB;IACtE,YACE,UAAU,EAAE,mCAAmC,EAC/C,iBAAiB,EAAE,MAAM,EACzB,KAAK,CAAC,EAAE,OAAO,EAGhB;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"invalid-authorization-details-error.js","sourceRoot":"","sources":["../../src/errors/invalid-authorization-details-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,gCAAiC,SAAQ,kBAAkB;IACtE,YACE,UAA+C,EAC/C,iBAAyB,EACzB,KAAe;QAEf,KAAK,CAAC,UAAU,EAAE,iBAAiB,EAAE,+BAA+B,EAAE,KAAK,CAAC,CAAA;IAC9E,CAAC;CACF","sourcesContent":["import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'\nimport { AuthorizationError } from './authorization-error.js'\n\n/**\n * @see\n * {@link https://datatracker.ietf.org/doc/html/rfc9396#section-14.6 | RFC 9396 - OAuth Dynamic Client Registration Metadata Registration Error}\n *\n * The AS MUST refuse to process any unknown authorization details type or\n * authorization details not conforming to the respective type definition. The\n * AS MUST abort processing and respond with an error\n * invalid_authorization_details to the client if any of the following are true\n * of the objects in the authorization_details structure:\n * - contains an unknown authorization details type value,\n * - is an object of known type but containing unknown fields,\n * - contains fields of the wrong type for the authorization details type,\n * - contains fields with invalid values for the authorization details type, or\n * - is missing required fields for the authorization details type.\n */\nexport class InvalidAuthorizationDetailsError extends AuthorizationError {\n constructor(\n parameters: OAuthAuthorizationRequestParameters,\n error_description: string,\n cause?: unknown,\n ) {\n super(parameters, error_description, 'invalid_authorization_details', cause)\n }\n}\n"]}
1
+ {"version":3,"file":"invalid-authorization-details-error.js","sourceRoot":"","sources":["../../src/errors/invalid-authorization-details-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,gCAAiC,SAAQ,kBAAkB;IACtE,YACE,UAA+C,EAC/C,iBAAyB,EACzB,KAAe;QAEf,KAAK,CAAC,UAAU,EAAE,iBAAiB,EAAE,+BAA+B,EAAE,KAAK,CAAC,CAAA;IAC9E,CAAC;CACF","sourcesContent":["import type { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'\nimport { AuthorizationError } from './authorization-error.js'\n\n/**\n * @see\n * {@link https://datatracker.ietf.org/doc/html/rfc9396#section-14.6 | RFC 9396 - OAuth Dynamic Client Registration Metadata Registration Error}\n *\n * The AS MUST refuse to process any unknown authorization details type or\n * authorization details not conforming to the respective type definition. The\n * AS MUST abort processing and respond with an error\n * invalid_authorization_details to the client if any of the following are true\n * of the objects in the authorization_details structure:\n * - contains an unknown authorization details type value,\n * - is an object of known type but containing unknown fields,\n * - contains fields of the wrong type for the authorization details type,\n * - contains fields with invalid values for the authorization details type, or\n * - is missing required fields for the authorization details type.\n */\nexport class InvalidAuthorizationDetailsError extends AuthorizationError {\n constructor(\n parameters: OAuthAuthorizationRequestParameters,\n error_description: string,\n cause?: unknown,\n ) {\n super(parameters, error_description, 'invalid_authorization_details', cause)\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { ZodError } from 'zod';
2
- import { FetchError } from '@atproto-labs/fetch';
2
+ import { FetchError } from '@atproto-labs/fetch-node';
3
3
  import { OAuthError } from './oauth-error.js';
4
4
  /**
5
5
  * @see {@link https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.2 | RFC7591 - Client Registration Error Response}
@@ -1 +1 @@
1
- {"version":3,"file":"invalid-client-metadata-error.js","sourceRoot":"","sources":["../../src/errors/invalid-client-metadata-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAA;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C;;;;;;GAMG;AACH,MAAM,OAAO,0BAA2B,SAAQ,UAAU;IACxD,YAAY,iBAAyB,EAAE,KAAe;QACpD,KAAK,CAAC,yBAAyB,EAAE,iBAAiB,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;IACjE,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,KAAc,EAAE,OAAO,GAAG,yBAAyB;QAC7D,IAAI,KAAK,YAAY,UAAU,EAAE,CAAC;YAChC,OAAO,KAAK,CAAA;QACd,CAAC;QAED,IAAI,KAAK,YAAY,UAAU,EAAE,CAAC;YAChC,MAAM,IAAI,0BAA0B,CAClC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,EACvD,KAAK,CACN,CAAA;QACH,CAAC;QAED,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;YAC9B,MAAM,YAAY,GAChB,KAAK,CAAC,MAAM;iBACT,GAAG,CACF,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CACpB,aAAa,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,uBAAuB,OAAO,EAAE,CAC5F;iBACA,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,OAAO,CAAA;YAE/B,MAAM,IAAI,0BAA0B,CAClC,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,YAAY,EAAE,CAAC,CAAC,CAAC,OAAO,EACtD,KAAK,CACN,CAAA;QACH,CAAC;QAED,IACE,KAAK,YAAY,KAAK;YACtB,MAAM,IAAI,KAAK;YACf,KAAK,CAAC,IAAI,KAAK,6BAA6B,EAC5C,CAAC;YACD,MAAM,IAAI,0BAA0B,CAClC,GAAG,OAAO,2BAA2B,EACrC,KAAK,CACN,CAAA;QACH,CAAC;QAED,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;YAC/B,yEAAyE;YACzE,oEAAoE;YACpE,yEAAyE;YACzE,oCAAoC;YACpC,OAAO,IAAI,0BAA0B,CACnC,GAAG,OAAO,KAAK,KAAK,CAAC,OAAO,EAAE,EAC9B,KAAK,CACN,CAAA;QACH,CAAC;QAED,OAAO,IAAI,0BAA0B,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IACvD,CAAC;CACF","sourcesContent":["import { ZodError } from 'zod'\nimport { FetchError } from '@atproto-labs/fetch'\nimport { OAuthError } from './oauth-error.js'\n\n/**\n * @see {@link https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.2 | RFC7591 - Client Registration Error Response}\n *\n * The value of one of the client metadata fields is invalid and the server has\n * rejected this request. Note that an authorization server MAY choose to\n * substitute a valid value for any requested parameter of a client's metadata.\n */\nexport class InvalidClientMetadataError extends OAuthError {\n constructor(error_description: string, cause?: unknown) {\n super('invalid_client_metadata', error_description, 400, cause)\n }\n\n static from(cause: unknown, message = 'Invalid client metadata'): OAuthError {\n if (cause instanceof OAuthError) {\n return cause\n }\n\n if (cause instanceof FetchError) {\n throw new InvalidClientMetadataError(\n cause.expose ? `${message}: ${cause.message}` : message,\n cause,\n )\n }\n\n if (cause instanceof ZodError) {\n const causeMessage =\n cause.issues\n .map(\n ({ path, message }) =>\n `Validation${path.length ? ` of \"${path.join('.')}\"` : ''} failed with error: ${message}`,\n )\n .join(' ') || cause.message\n\n throw new InvalidClientMetadataError(\n causeMessage ? `${message}: ${causeMessage}` : message,\n cause,\n )\n }\n\n if (\n cause instanceof Error &&\n 'code' in cause &&\n cause.code === 'DEPTH_ZERO_SELF_SIGNED_CERT'\n ) {\n throw new InvalidClientMetadataError(\n `${message}: Self-signed certificate`,\n cause,\n )\n }\n\n if (cause instanceof TypeError) {\n // This method is meant to be used in the context of parsing & validating\n // a client client metadata. In that context, a TypeError would more\n // likely represent a problem with the data (e.g. invalid URL constructor\n // arg) and not a programming error.\n return new InvalidClientMetadataError(\n `${message}: ${cause.message}`,\n cause,\n )\n }\n\n return new InvalidClientMetadataError(message, cause)\n }\n}\n"]}
1
+ {"version":3,"file":"invalid-client-metadata-error.js","sourceRoot":"","sources":["../../src/errors/invalid-client-metadata-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAA;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C;;;;;;GAMG;AACH,MAAM,OAAO,0BAA2B,SAAQ,UAAU;IACxD,YAAY,iBAAyB,EAAE,KAAe;QACpD,KAAK,CAAC,yBAAyB,EAAE,iBAAiB,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;IACjE,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,KAAc,EAAE,OAAO,GAAG,yBAAyB;QAC7D,IAAI,KAAK,YAAY,UAAU,EAAE,CAAC;YAChC,OAAO,KAAK,CAAA;QACd,CAAC;QAED,IAAI,KAAK,YAAY,UAAU,EAAE,CAAC;YAChC,MAAM,IAAI,0BAA0B,CAClC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,EACvD,KAAK,CACN,CAAA;QACH,CAAC;QAED,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;YAC9B,MAAM,YAAY,GAChB,KAAK,CAAC,MAAM;iBACT,GAAG,CACF,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CACpB,aAAa,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,uBAAuB,OAAO,EAAE,CAC5F;iBACA,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,OAAO,CAAA;YAE/B,MAAM,IAAI,0BAA0B,CAClC,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,YAAY,EAAE,CAAC,CAAC,CAAC,OAAO,EACtD,KAAK,CACN,CAAA;QACH,CAAC;QAED,IACE,KAAK,YAAY,KAAK;YACtB,MAAM,IAAI,KAAK;YACf,KAAK,CAAC,IAAI,KAAK,6BAA6B,EAC5C,CAAC;YACD,MAAM,IAAI,0BAA0B,CAClC,GAAG,OAAO,2BAA2B,EACrC,KAAK,CACN,CAAA;QACH,CAAC;QAED,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;YAC/B,yEAAyE;YACzE,oEAAoE;YACpE,yEAAyE;YACzE,oCAAoC;YACpC,OAAO,IAAI,0BAA0B,CACnC,GAAG,OAAO,KAAK,KAAK,CAAC,OAAO,EAAE,EAC9B,KAAK,CACN,CAAA;QACH,CAAC;QAED,OAAO,IAAI,0BAA0B,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IACvD,CAAC;CACF","sourcesContent":["import { ZodError } from 'zod'\nimport { FetchError } from '@atproto-labs/fetch-node'\nimport { OAuthError } from './oauth-error.js'\n\n/**\n * @see {@link https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.2 | RFC7591 - Client Registration Error Response}\n *\n * The value of one of the client metadata fields is invalid and the server has\n * rejected this request. Note that an authorization server MAY choose to\n * substitute a valid value for any requested parameter of a client's metadata.\n */\nexport class InvalidClientMetadataError extends OAuthError {\n constructor(error_description: string, cause?: unknown) {\n super('invalid_client_metadata', error_description, 400, cause)\n }\n\n static from(cause: unknown, message = 'Invalid client metadata'): OAuthError {\n if (cause instanceof OAuthError) {\n return cause\n }\n\n if (cause instanceof FetchError) {\n throw new InvalidClientMetadataError(\n cause.expose ? `${message}: ${cause.message}` : message,\n cause,\n )\n }\n\n if (cause instanceof ZodError) {\n const causeMessage =\n cause.issues\n .map(\n ({ path, message }) =>\n `Validation${path.length ? ` of \"${path.join('.')}\"` : ''} failed with error: ${message}`,\n )\n .join(' ') || cause.message\n\n throw new InvalidClientMetadataError(\n causeMessage ? `${message}: ${causeMessage}` : message,\n cause,\n )\n }\n\n if (\n cause instanceof Error &&\n 'code' in cause &&\n cause.code === 'DEPTH_ZERO_SELF_SIGNED_CERT'\n ) {\n throw new InvalidClientMetadataError(\n `${message}: Self-signed certificate`,\n cause,\n )\n }\n\n if (cause instanceof TypeError) {\n // This method is meant to be used in the context of parsing & validating\n // a client client metadata. In that context, a TypeError would more\n // likely represent a problem with the data (e.g. invalid URL constructor\n // arg) and not a programming error.\n return new InvalidClientMetadataError(\n `${message}: ${cause.message}`,\n cause,\n )\n }\n\n return new InvalidClientMetadataError(message, cause)\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { Did } from '@atproto/did';
1
+ import type { Did } from '@atproto/did';
2
2
  import { InvalidRequestError } from './invalid-request-error.js';
3
3
  /**
4
4
  * Thrown by {@link AccountStore.authenticateAccount} implementations to signal
@@ -1 +1 @@
1
- {"version":3,"file":"invalid-credentials-error.d.ts","sourceRoot":"","sources":["../../src/errors/invalid-credentials-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAClC,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAEhE;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,uBAAwB,SAAQ,mBAAmB;aAG5C,GAAG,CAAC,EAAE,GAAG;IAF3B,YACE,OAAO,SAAmC,EAC1B,GAAG,CAAC,EAAE,GAAG,YAAA,EACzB,KAAK,CAAC,EAAE,OAAO,EAGhB;CACF"}
1
+ {"version":3,"file":"invalid-credentials-error.d.ts","sourceRoot":"","sources":["../../src/errors/invalid-credentials-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAEhE;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,uBAAwB,SAAQ,mBAAmB;aAG5C,GAAG,CAAC,EAAE,GAAG;IAF3B,YACE,OAAO,SAAmC,EAC1B,GAAG,CAAC,EAAE,GAAG,YAAA,EACzB,KAAK,CAAC,EAAE,OAAO,EAGhB;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"invalid-credentials-error.js","sourceRoot":"","sources":["../../src/errors/invalid-credentials-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAEhE;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,uBAAwB,SAAQ,mBAAmB;IAC9D,YACE,OAAO,GAAG,gCAAgC,EAC1B,GAAS,EACzB,KAAe;QAEf,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;mBAHL,GAAG;IAIrB,CAAC;CACF","sourcesContent":["import { Did } from '@atproto/did'\nimport { InvalidRequestError } from './invalid-request-error.js'\n\n/**\n * Thrown by {@link AccountStore.authenticateAccount} implementations to signal\n * that a sign-in attempt was rejected because the provided credentials did not\n * match a known account.\n *\n * Stores should populate {@link did}. when the identifier resolved to an\n * existing account but e.g. the password or OTP was incorrect. The\n * identifier-unknown case should leave {@link did} unset. This information is\n * surfaced to the `onSignInFailed` hook and never sent back to the client, so\n * populating it does not affect the client-visible response.\n *\n * Only the subject identifier (DID) is carried — not a full `Account` — to\n * avoid embedding PII (email, name, etc.) in an error that may be serialized\n * by loggers or monitoring tools walking the `.cause` chain. Hook consumers\n * that need richer profile info can resolve it from their own account store\n * using {@link did}.\n */\nexport class InvalidCredentialsError extends InvalidRequestError {\n constructor(\n message = 'Invalid identifier or password',\n public readonly did?: Did,\n cause?: unknown,\n ) {\n super(message, cause)\n }\n}\n"]}
1
+ {"version":3,"file":"invalid-credentials-error.js","sourceRoot":"","sources":["../../src/errors/invalid-credentials-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAEhE;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,uBAAwB,SAAQ,mBAAmB;IAC9D,YACE,OAAO,GAAG,gCAAgC,EAC1B,GAAS,EACzB,KAAe;QAEf,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;mBAHL,GAAG;IAIrB,CAAC;CACF","sourcesContent":["import type { Did } from '@atproto/did'\nimport { InvalidRequestError } from './invalid-request-error.js'\n\n/**\n * Thrown by {@link AccountStore.authenticateAccount} implementations to signal\n * that a sign-in attempt was rejected because the provided credentials did not\n * match a known account.\n *\n * Stores should populate {@link did}. when the identifier resolved to an\n * existing account but e.g. the password or OTP was incorrect. The\n * identifier-unknown case should leave {@link did} unset. This information is\n * surfaced to the `onSignInFailed` hook and never sent back to the client, so\n * populating it does not affect the client-visible response.\n *\n * Only the subject identifier (DID) is carried — not a full `Account` — to\n * avoid embedding PII (email, name, etc.) in an error that may be serialized\n * by loggers or monitoring tools walking the `.cause` chain. Hook consumers\n * that need richer profile info can resolve it from their own account store\n * using {@link did}.\n */\nexport class InvalidCredentialsError extends InvalidRequestError {\n constructor(\n message = 'Invalid identifier or password',\n public readonly did?: Did,\n cause?: unknown,\n ) {\n super(message, cause)\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types';
1
+ import type { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types';
2
2
  import { AuthorizationError } from './authorization-error.js';
3
3
  /**
4
4
  * @see {@link https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11#section-4.1.2.1}
@@ -1 +1 @@
1
- {"version":3,"file":"invalid-scope-error.d.ts","sourceRoot":"","sources":["../../src/errors/invalid-scope-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAA;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,kBAAkB;IACvD,YACE,UAAU,EAAE,mCAAmC,EAC/C,iBAAiB,EAAE,MAAM,EACzB,KAAK,CAAC,EAAE,OAAO,EAGhB;CACF"}
1
+ {"version":3,"file":"invalid-scope-error.d.ts","sourceRoot":"","sources":["../../src/errors/invalid-scope-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,kBAAkB;IACvD,YACE,UAAU,EAAE,mCAAmC,EAC/C,iBAAiB,EAAE,MAAM,EACzB,KAAK,CAAC,EAAE,OAAO,EAGhB;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"invalid-scope-error.js","sourceRoot":"","sources":["../../src/errors/invalid-scope-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D;;GAEG;AACH,MAAM,OAAO,iBAAkB,SAAQ,kBAAkB;IACvD,YACE,UAA+C,EAC/C,iBAAyB,EACzB,KAAe;QAEf,KAAK,CAAC,UAAU,EAAE,iBAAiB,EAAE,eAAe,EAAE,KAAK,CAAC,CAAA;IAC9D,CAAC;CACF","sourcesContent":["import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'\nimport { AuthorizationError } from './authorization-error.js'\n\n/**\n * @see {@link https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11#section-4.1.2.1}\n */\nexport class InvalidScopeError extends AuthorizationError {\n constructor(\n parameters: OAuthAuthorizationRequestParameters,\n error_description: string,\n cause?: unknown,\n ) {\n super(parameters, error_description, 'invalid_scope', cause)\n }\n}\n"]}
1
+ {"version":3,"file":"invalid-scope-error.js","sourceRoot":"","sources":["../../src/errors/invalid-scope-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D;;GAEG;AACH,MAAM,OAAO,iBAAkB,SAAQ,kBAAkB;IACvD,YACE,UAA+C,EAC/C,iBAAyB,EACzB,KAAe;QAEf,KAAK,CAAC,UAAU,EAAE,iBAAiB,EAAE,eAAe,EAAE,KAAK,CAAC,CAAA;IAC9D,CAAC;CACF","sourcesContent":["import type { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'\nimport { AuthorizationError } from './authorization-error.js'\n\n/**\n * @see {@link https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11#section-4.1.2.1}\n */\nexport class InvalidScopeError extends AuthorizationError {\n constructor(\n parameters: OAuthAuthorizationRequestParameters,\n error_description: string,\n cause?: unknown,\n ) {\n super(parameters, error_description, 'invalid_scope', cause)\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types';
1
+ import type { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types';
2
2
  import { AuthorizationError } from './authorization-error.js';
3
3
  export declare class LoginRequiredError extends AuthorizationError {
4
4
  constructor(parameters: OAuthAuthorizationRequestParameters, error_description?: string, cause?: unknown);
@@ -1 +1 @@
1
- {"version":3,"file":"login-required-error.d.ts","sourceRoot":"","sources":["../../src/errors/login-required-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAA;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,qBAAa,kBAAmB,SAAQ,kBAAkB;IACxD,YACE,UAAU,EAAE,mCAAmC,EAC/C,iBAAiB,SAAsB,EACvC,KAAK,CAAC,EAAE,OAAO,EAGhB;CACF"}
1
+ {"version":3,"file":"login-required-error.d.ts","sourceRoot":"","sources":["../../src/errors/login-required-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,qBAAa,kBAAmB,SAAQ,kBAAkB;IACxD,YACE,UAAU,EAAE,mCAAmC,EAC/C,iBAAiB,SAAsB,EACvC,KAAK,CAAC,EAAE,OAAO,EAGhB;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"login-required-error.js","sourceRoot":"","sources":["../../src/errors/login-required-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,MAAM,OAAO,kBAAmB,SAAQ,kBAAkB;IACxD,YACE,UAA+C,EAC/C,iBAAiB,GAAG,mBAAmB,EACvC,KAAe;QAEf,KAAK,CAAC,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,KAAK,CAAC,CAAA;IAC/D,CAAC;CACF","sourcesContent":["import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'\nimport { AuthorizationError } from './authorization-error.js'\n\nexport class LoginRequiredError extends AuthorizationError {\n constructor(\n parameters: OAuthAuthorizationRequestParameters,\n error_description = 'Login is required',\n cause?: unknown,\n ) {\n super(parameters, error_description, 'login_required', cause)\n }\n}\n"]}
1
+ {"version":3,"file":"login-required-error.js","sourceRoot":"","sources":["../../src/errors/login-required-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE7D,MAAM,OAAO,kBAAmB,SAAQ,kBAAkB;IACxD,YACE,UAA+C,EAC/C,iBAAiB,GAAG,mBAAmB,EACvC,KAAe;QAEf,KAAK,CAAC,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,KAAK,CAAC,CAAA;IAC/D,CAAC;CACF","sourcesContent":["import type { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'\nimport { AuthorizationError } from './authorization-error.js'\n\nexport class LoginRequiredError extends AuthorizationError {\n constructor(\n parameters: OAuthAuthorizationRequestParameters,\n error_description = 'Login is required',\n cause?: unknown,\n ) {\n super(parameters, error_description, 'login_required', cause)\n }\n}\n"]}