@atproto/oauth-provider 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (631) hide show
  1. package/.postcssrc.yml +3 -0
  2. package/CHANGELOG.md +19 -0
  3. package/LICENSE.txt +7 -0
  4. package/dist/access-token/access-token-type.d.ts +6 -0
  5. package/dist/access-token/access-token-type.d.ts.map +1 -0
  6. package/dist/access-token/access-token-type.js +10 -0
  7. package/dist/access-token/access-token-type.js.map +1 -0
  8. package/dist/account/account-manager.d.ts +14 -0
  9. package/dist/account/account-manager.d.ts.map +1 -0
  10. package/dist/account/account-manager.js +39 -0
  11. package/dist/account/account-manager.js.map +1 -0
  12. package/dist/account/account-store.d.ts +39 -0
  13. package/dist/account/account-store.d.ts.map +1 -0
  14. package/dist/account/account-store.js +19 -0
  15. package/dist/account/account-store.js.map +1 -0
  16. package/dist/account/account.d.ts +8 -0
  17. package/dist/account/account.d.ts.map +1 -0
  18. package/dist/account/account.js +3 -0
  19. package/dist/account/account.js.map +1 -0
  20. package/dist/assets/app/bundle-manifest.json +22 -0
  21. package/dist/assets/app/main.css +3 -0
  22. package/dist/assets/app/main.js +20 -0
  23. package/dist/assets/app/main.js.map +1 -0
  24. package/dist/assets/asset.d.ts +9 -0
  25. package/dist/assets/asset.d.ts.map +1 -0
  26. package/dist/assets/asset.js +3 -0
  27. package/dist/assets/asset.js.map +1 -0
  28. package/dist/assets/assets-middleware.d.ts +2 -0
  29. package/dist/assets/assets-middleware.d.ts.map +1 -0
  30. package/dist/assets/assets-middleware.js +30 -0
  31. package/dist/assets/assets-middleware.js.map +1 -0
  32. package/dist/assets/index.d.ts +4 -0
  33. package/dist/assets/index.d.ts.map +1 -0
  34. package/dist/assets/index.js +65 -0
  35. package/dist/assets/index.js.map +1 -0
  36. package/dist/client/client-auth.d.ts +13 -0
  37. package/dist/client/client-auth.d.ts.map +1 -0
  38. package/dist/client/client-auth.js +35 -0
  39. package/dist/client/client-auth.js.map +1 -0
  40. package/dist/client/client-data.d.ts +8 -0
  41. package/dist/client/client-data.d.ts.map +1 -0
  42. package/dist/client/client-data.js +3 -0
  43. package/dist/client/client-data.js.map +1 -0
  44. package/dist/client/client-id.d.ts +4 -0
  45. package/dist/client/client-id.d.ts.map +1 -0
  46. package/dist/client/client-id.js +6 -0
  47. package/dist/client/client-id.js.map +1 -0
  48. package/dist/client/client-info.d.ts +13 -0
  49. package/dist/client/client-info.d.ts.map +1 -0
  50. package/dist/client/client-info.js +3 -0
  51. package/dist/client/client-info.js.map +1 -0
  52. package/dist/client/client-manager.d.ts +38 -0
  53. package/dist/client/client-manager.d.ts.map +1 -0
  54. package/dist/client/client-manager.js +534 -0
  55. package/dist/client/client-manager.js.map +1 -0
  56. package/dist/client/client-store.d.ts +13 -0
  57. package/dist/client/client-store.d.ts.map +1 -0
  58. package/dist/client/client-store.js +39 -0
  59. package/dist/client/client-store.js.map +1 -0
  60. package/dist/client/client-utils.d.ts +6 -0
  61. package/dist/client/client-utils.d.ts.map +1 -0
  62. package/dist/client/client-utils.js +40 -0
  63. package/dist/client/client-utils.js.map +1 -0
  64. package/dist/client/client.d.ts +41 -0
  65. package/dist/client/client.d.ts.map +1 -0
  66. package/dist/client/client.js +163 -0
  67. package/dist/client/client.js.map +1 -0
  68. package/dist/constants.d.ts +42 -0
  69. package/dist/constants.d.ts.map +1 -0
  70. package/dist/constants.js +53 -0
  71. package/dist/constants.js.map +1 -0
  72. package/dist/device/device-data.d.ts +20 -0
  73. package/dist/device/device-data.d.ts.map +1 -0
  74. package/dist/device/device-data.js +11 -0
  75. package/dist/device/device-data.js.map +1 -0
  76. package/dist/device/device-details.d.ts +17 -0
  77. package/dist/device/device-details.d.ts.map +1 -0
  78. package/dist/device/device-details.js +34 -0
  79. package/dist/device/device-details.js.map +1 -0
  80. package/dist/device/device-id.d.ts +6 -0
  81. package/dist/device/device-id.d.ts.map +1 -0
  82. package/dist/device/device-id.js +18 -0
  83. package/dist/device/device-id.js.map +1 -0
  84. package/dist/device/device-manager.d.ts +88 -0
  85. package/dist/device/device-manager.d.ts.map +1 -0
  86. package/dist/device/device-manager.js +206 -0
  87. package/dist/device/device-manager.js.map +1 -0
  88. package/dist/device/device-store.d.ts +15 -0
  89. package/dist/device/device-store.d.ts.map +1 -0
  90. package/dist/device/device-store.js +36 -0
  91. package/dist/device/device-store.js.map +1 -0
  92. package/dist/device/session-id.d.ts +6 -0
  93. package/dist/device/session-id.d.ts.map +1 -0
  94. package/dist/device/session-id.js +18 -0
  95. package/dist/device/session-id.js.map +1 -0
  96. package/dist/dpop/dpop-manager.d.ts +33 -0
  97. package/dist/dpop/dpop-manager.d.ts.map +1 -0
  98. package/dist/dpop/dpop-manager.js +115 -0
  99. package/dist/dpop/dpop-manager.js.map +1 -0
  100. package/dist/dpop/dpop-nonce.d.ts +13 -0
  101. package/dist/dpop/dpop-nonce.d.ts.map +1 -0
  102. package/dist/dpop/dpop-nonce.js +94 -0
  103. package/dist/dpop/dpop-nonce.js.map +1 -0
  104. package/dist/errors/access-denied-error.d.ts +8 -0
  105. package/dist/errors/access-denied-error.d.ts.map +1 -0
  106. package/dist/errors/access-denied-error.js +21 -0
  107. package/dist/errors/access-denied-error.js.map +1 -0
  108. package/dist/errors/account-selection-required-error.d.ts +6 -0
  109. package/dist/errors/account-selection-required-error.d.ts.map +1 -0
  110. package/dist/errors/account-selection-required-error.js +11 -0
  111. package/dist/errors/account-selection-required-error.js.map +1 -0
  112. package/dist/errors/consent-required-error.d.ts +6 -0
  113. package/dist/errors/consent-required-error.d.ts.map +1 -0
  114. package/dist/errors/consent-required-error.js +11 -0
  115. package/dist/errors/consent-required-error.js.map +1 -0
  116. package/dist/errors/invalid-authorization-details-error.d.ts +20 -0
  117. package/dist/errors/invalid-authorization-details-error.d.ts.map +1 -0
  118. package/dist/errors/invalid-authorization-details-error.js +26 -0
  119. package/dist/errors/invalid-authorization-details-error.js.map +1 -0
  120. package/dist/errors/invalid-client-error.d.ts +18 -0
  121. package/dist/errors/invalid-client-error.d.ts.map +1 -0
  122. package/dist/errors/invalid-client-error.js +24 -0
  123. package/dist/errors/invalid-client-error.js.map +1 -0
  124. package/dist/errors/invalid-client-id-error.d.ts +13 -0
  125. package/dist/errors/invalid-client-id-error.d.ts.map +1 -0
  126. package/dist/errors/invalid-client-id-error.js +25 -0
  127. package/dist/errors/invalid-client-id-error.js.map +1 -0
  128. package/dist/errors/invalid-client-metadata-error.d.ts +13 -0
  129. package/dist/errors/invalid-client-metadata-error.d.ts.map +1 -0
  130. package/dist/errors/invalid-client-metadata-error.js +23 -0
  131. package/dist/errors/invalid-client-metadata-error.js.map +1 -0
  132. package/dist/errors/invalid-dpop-key-binding-error.d.ts +12 -0
  133. package/dist/errors/invalid-dpop-key-binding-error.d.ts.map +1 -0
  134. package/dist/errors/invalid-dpop-key-binding-error.js +20 -0
  135. package/dist/errors/invalid-dpop-key-binding-error.js.map +1 -0
  136. package/dist/errors/invalid-dpop-proof-error.d.ts +5 -0
  137. package/dist/errors/invalid-dpop-proof-error.d.ts.map +1 -0
  138. package/dist/errors/invalid-dpop-proof-error.js +12 -0
  139. package/dist/errors/invalid-dpop-proof-error.js.map +1 -0
  140. package/dist/errors/invalid-grant-error.d.ts +14 -0
  141. package/dist/errors/invalid-grant-error.d.ts.map +1 -0
  142. package/dist/errors/invalid-grant-error.js +20 -0
  143. package/dist/errors/invalid-grant-error.js.map +1 -0
  144. package/dist/errors/invalid-parameters-error.d.ts +6 -0
  145. package/dist/errors/invalid-parameters-error.d.ts.map +1 -0
  146. package/dist/errors/invalid-parameters-error.js +11 -0
  147. package/dist/errors/invalid-parameters-error.js.map +1 -0
  148. package/dist/errors/invalid-redirect-uri-error.d.ts +11 -0
  149. package/dist/errors/invalid-redirect-uri-error.d.ts.map +1 -0
  150. package/dist/errors/invalid-redirect-uri-error.js +21 -0
  151. package/dist/errors/invalid-redirect-uri-error.js.map +1 -0
  152. package/dist/errors/invalid-request-error.d.ts +28 -0
  153. package/dist/errors/invalid-request-error.d.ts.map +1 -0
  154. package/dist/errors/invalid-request-error.js +34 -0
  155. package/dist/errors/invalid-request-error.js.map +1 -0
  156. package/dist/errors/invalid-token-error.d.ts +16 -0
  157. package/dist/errors/invalid-token-error.d.ts.map +1 -0
  158. package/dist/errors/invalid-token-error.js +45 -0
  159. package/dist/errors/invalid-token-error.js.map +1 -0
  160. package/dist/errors/login-required-error.d.ts +6 -0
  161. package/dist/errors/login-required-error.d.ts.map +1 -0
  162. package/dist/errors/login-required-error.js +11 -0
  163. package/dist/errors/login-required-error.js.map +1 -0
  164. package/dist/errors/oauth-error.d.ts +13 -0
  165. package/dist/errors/oauth-error.d.ts.map +1 -0
  166. package/dist/errors/oauth-error.js +29 -0
  167. package/dist/errors/oauth-error.js.map +1 -0
  168. package/dist/errors/unauthorized-client-error.d.ts +18 -0
  169. package/dist/errors/unauthorized-client-error.d.ts.map +1 -0
  170. package/dist/errors/unauthorized-client-error.js +24 -0
  171. package/dist/errors/unauthorized-client-error.js.map +1 -0
  172. package/dist/errors/use-dpop-nonce-error.d.ts +18 -0
  173. package/dist/errors/use-dpop-nonce-error.d.ts.map +1 -0
  174. package/dist/errors/use-dpop-nonce-error.js +27 -0
  175. package/dist/errors/use-dpop-nonce-error.js.map +1 -0
  176. package/dist/errors/www-authenticate-error.d.ts +9 -0
  177. package/dist/errors/www-authenticate-error.d.ts.map +1 -0
  178. package/dist/errors/www-authenticate-error.js +46 -0
  179. package/dist/errors/www-authenticate-error.js.map +1 -0
  180. package/dist/index.d.ts +14 -0
  181. package/dist/index.d.ts.map +1 -0
  182. package/dist/index.js +31 -0
  183. package/dist/index.js.map +1 -0
  184. package/dist/lib/html/build-document.d.ts +32 -0
  185. package/dist/lib/html/build-document.d.ts.map +1 -0
  186. package/dist/lib/html/build-document.js +61 -0
  187. package/dist/lib/html/build-document.js.map +1 -0
  188. package/dist/lib/html/escapers.d.ts +9 -0
  189. package/dist/lib/html/escapers.d.ts.map +1 -0
  190. package/dist/lib/html/escapers.js +66 -0
  191. package/dist/lib/html/escapers.js.map +1 -0
  192. package/dist/lib/html/html.d.ts +13 -0
  193. package/dist/lib/html/html.d.ts.map +1 -0
  194. package/dist/lib/html/html.js +53 -0
  195. package/dist/lib/html/html.js.map +1 -0
  196. package/dist/lib/html/index.d.ts +4 -0
  197. package/dist/lib/html/index.d.ts.map +1 -0
  198. package/dist/lib/html/index.js +21 -0
  199. package/dist/lib/html/index.js.map +1 -0
  200. package/dist/lib/html/tags.d.ts +34 -0
  201. package/dist/lib/html/tags.d.ts.map +1 -0
  202. package/dist/lib/html/tags.js +47 -0
  203. package/dist/lib/html/tags.js.map +1 -0
  204. package/dist/lib/html/util.d.ts +4 -0
  205. package/dist/lib/html/util.d.ts.map +1 -0
  206. package/dist/lib/html/util.js +20 -0
  207. package/dist/lib/html/util.js.map +1 -0
  208. package/dist/lib/http/accept.d.ts +29 -0
  209. package/dist/lib/http/accept.d.ts.map +1 -0
  210. package/dist/lib/http/accept.js +67 -0
  211. package/dist/lib/http/accept.js.map +1 -0
  212. package/dist/lib/http/context.d.ts +5 -0
  213. package/dist/lib/http/context.d.ts.map +1 -0
  214. package/dist/lib/http/context.js +10 -0
  215. package/dist/lib/http/context.js.map +1 -0
  216. package/dist/lib/http/index.d.ts +10 -0
  217. package/dist/lib/http/index.d.ts.map +1 -0
  218. package/dist/lib/http/index.js +26 -0
  219. package/dist/lib/http/index.js.map +1 -0
  220. package/dist/lib/http/method.d.ts +6 -0
  221. package/dist/lib/http/method.d.ts.map +1 -0
  222. package/dist/lib/http/method.js +19 -0
  223. package/dist/lib/http/method.js.map +1 -0
  224. package/dist/lib/http/middleware.d.ts +18 -0
  225. package/dist/lib/http/middleware.d.ts.map +1 -0
  226. package/dist/lib/http/middleware.js +118 -0
  227. package/dist/lib/http/middleware.js.map +1 -0
  228. package/dist/lib/http/parser.d.ts +33 -0
  229. package/dist/lib/http/parser.d.ts.map +1 -0
  230. package/dist/lib/http/parser.js +48 -0
  231. package/dist/lib/http/parser.js.map +1 -0
  232. package/dist/lib/http/path.d.ts +9 -0
  233. package/dist/lib/http/path.d.ts.map +1 -0
  234. package/dist/lib/http/path.js +54 -0
  235. package/dist/lib/http/path.js.map +1 -0
  236. package/dist/lib/http/request.d.ts +33 -0
  237. package/dist/lib/http/request.d.ts.map +1 -0
  238. package/dist/lib/http/request.js +86 -0
  239. package/dist/lib/http/request.js.map +1 -0
  240. package/dist/lib/http/response.d.ts +13 -0
  241. package/dist/lib/http/response.d.ts.map +1 -0
  242. package/dist/lib/http/response.js +98 -0
  243. package/dist/lib/http/response.js.map +1 -0
  244. package/dist/lib/http/route.d.ts +25 -0
  245. package/dist/lib/http/route.d.ts.map +1 -0
  246. package/dist/lib/http/route.js +39 -0
  247. package/dist/lib/http/route.js.map +1 -0
  248. package/dist/lib/http/router.d.ts +32 -0
  249. package/dist/lib/http/router.d.ts.map +1 -0
  250. package/dist/lib/http/router.js +74 -0
  251. package/dist/lib/http/router.js.map +1 -0
  252. package/dist/lib/http/stream.d.ts +13 -0
  253. package/dist/lib/http/stream.d.ts.map +1 -0
  254. package/dist/lib/http/stream.js +46 -0
  255. package/dist/lib/http/stream.js.map +1 -0
  256. package/dist/lib/http/types.d.ts +7 -0
  257. package/dist/lib/http/types.d.ts.map +1 -0
  258. package/dist/lib/http/types.js +3 -0
  259. package/dist/lib/http/types.js.map +1 -0
  260. package/dist/lib/http/url.d.ts +8 -0
  261. package/dist/lib/http/url.d.ts.map +1 -0
  262. package/dist/lib/http/url.js +22 -0
  263. package/dist/lib/http/url.js.map +1 -0
  264. package/dist/lib/redis.d.ts +5 -0
  265. package/dist/lib/redis.d.ts.map +1 -0
  266. package/dist/lib/redis.js +22 -0
  267. package/dist/lib/redis.js.map +1 -0
  268. package/dist/lib/util/authorization-header.d.ts +4 -0
  269. package/dist/lib/util/authorization-header.d.ts.map +1 -0
  270. package/dist/lib/util/authorization-header.js +23 -0
  271. package/dist/lib/util/authorization-header.js.map +1 -0
  272. package/dist/lib/util/cast.d.ts +2 -0
  273. package/dist/lib/util/cast.d.ts.map +1 -0
  274. package/dist/lib/util/cast.js +10 -0
  275. package/dist/lib/util/cast.js.map +1 -0
  276. package/dist/lib/util/crypto.d.ts +3 -0
  277. package/dist/lib/util/crypto.d.ts.map +1 -0
  278. package/dist/lib/util/crypto.js +29 -0
  279. package/dist/lib/util/crypto.js.map +1 -0
  280. package/dist/lib/util/date.d.ts +3 -0
  281. package/dist/lib/util/date.d.ts.map +1 -0
  282. package/dist/lib/util/date.js +12 -0
  283. package/dist/lib/util/date.js.map +1 -0
  284. package/dist/lib/util/hostname.d.ts +6 -0
  285. package/dist/lib/util/hostname.d.ts.map +1 -0
  286. package/dist/lib/util/hostname.js +24 -0
  287. package/dist/lib/util/hostname.js.map +1 -0
  288. package/dist/lib/util/redirect-uri.d.ts +7 -0
  289. package/dist/lib/util/redirect-uri.d.ts.map +1 -0
  290. package/dist/lib/util/redirect-uri.js +44 -0
  291. package/dist/lib/util/redirect-uri.js.map +1 -0
  292. package/dist/lib/util/time.d.ts +6 -0
  293. package/dist/lib/util/time.d.ts.map +1 -0
  294. package/dist/lib/util/time.js +28 -0
  295. package/dist/lib/util/time.js.map +1 -0
  296. package/dist/lib/util/type.d.ts +6 -0
  297. package/dist/lib/util/type.d.ts.map +1 -0
  298. package/dist/lib/util/type.js +3 -0
  299. package/dist/lib/util/type.js.map +1 -0
  300. package/dist/lib/util/well-known.d.ts +3 -0
  301. package/dist/lib/util/well-known.d.ts.map +1 -0
  302. package/dist/lib/util/well-known.js +11 -0
  303. package/dist/lib/util/well-known.js.map +1 -0
  304. package/dist/metadata/build-metadata.d.ts +14 -0
  305. package/dist/metadata/build-metadata.d.ts.map +1 -0
  306. package/dist/metadata/build-metadata.js +132 -0
  307. package/dist/metadata/build-metadata.js.map +1 -0
  308. package/dist/oauth-client.d.ts +4 -0
  309. package/dist/oauth-client.d.ts.map +1 -0
  310. package/dist/oauth-client.js +19 -0
  311. package/dist/oauth-client.js.map +1 -0
  312. package/dist/oauth-dpop.d.ts +3 -0
  313. package/dist/oauth-dpop.d.ts.map +1 -0
  314. package/dist/oauth-dpop.js +19 -0
  315. package/dist/oauth-dpop.js.map +1 -0
  316. package/dist/oauth-errors.d.ts +20 -0
  317. package/dist/oauth-errors.d.ts.map +1 -0
  318. package/dist/oauth-errors.js +43 -0
  319. package/dist/oauth-errors.js.map +1 -0
  320. package/dist/oauth-hooks.d.ts +42 -0
  321. package/dist/oauth-hooks.d.ts.map +1 -0
  322. package/dist/oauth-hooks.js +3 -0
  323. package/dist/oauth-hooks.js.map +1 -0
  324. package/dist/oauth-provider.d.ts +179 -0
  325. package/dist/oauth-provider.d.ts.map +1 -0
  326. package/dist/oauth-provider.js +748 -0
  327. package/dist/oauth-provider.js.map +1 -0
  328. package/dist/oauth-store.d.ts +11 -0
  329. package/dist/oauth-store.d.ts.map +1 -0
  330. package/dist/oauth-store.js +27 -0
  331. package/dist/oauth-store.js.map +1 -0
  332. package/dist/oauth-verifier.d.ts +66 -0
  333. package/dist/oauth-verifier.d.ts.map +1 -0
  334. package/dist/oauth-verifier.js +94 -0
  335. package/dist/oauth-verifier.js.map +1 -0
  336. package/dist/oidc/claims.d.ts +16 -0
  337. package/dist/oidc/claims.d.ts.map +1 -0
  338. package/dist/oidc/claims.js +29 -0
  339. package/dist/oidc/claims.js.map +1 -0
  340. package/dist/oidc/sub.d.ts +4 -0
  341. package/dist/oidc/sub.d.ts.map +1 -0
  342. package/dist/oidc/sub.js +6 -0
  343. package/dist/oidc/sub.js.map +1 -0
  344. package/dist/oidc/userinfo.d.ts +7 -0
  345. package/dist/oidc/userinfo.d.ts.map +1 -0
  346. package/dist/oidc/userinfo.js +3 -0
  347. package/dist/oidc/userinfo.js.map +1 -0
  348. package/dist/output/build-error-payload.d.ts +6 -0
  349. package/dist/output/build-error-payload.d.ts.map +1 -0
  350. package/dist/output/build-error-payload.js +108 -0
  351. package/dist/output/build-error-payload.js.map +1 -0
  352. package/dist/output/customization.d.ts +37 -0
  353. package/dist/output/customization.d.ts.map +1 -0
  354. package/dist/output/customization.js +62 -0
  355. package/dist/output/customization.js.map +1 -0
  356. package/dist/output/send-authorize-page.d.ts +43 -0
  357. package/dist/output/send-authorize-page.d.ts.map +1 -0
  358. package/dist/output/send-authorize-page.js +49 -0
  359. package/dist/output/send-authorize-page.js.map +1 -0
  360. package/dist/output/send-authorize-redirect.d.ts +25 -0
  361. package/dist/output/send-authorize-redirect.d.ts.map +1 -0
  362. package/dist/output/send-authorize-redirect.js +72 -0
  363. package/dist/output/send-authorize-redirect.js.map +1 -0
  364. package/dist/output/send-error-page.d.ts +5 -0
  365. package/dist/output/send-error-page.d.ts.map +1 -0
  366. package/dist/output/send-error-page.js +31 -0
  367. package/dist/output/send-error-page.js.map +1 -0
  368. package/dist/output/send-web-page.d.ts +8 -0
  369. package/dist/output/send-web-page.d.ts.map +1 -0
  370. package/dist/output/send-web-page.js +48 -0
  371. package/dist/output/send-web-page.js.map +1 -0
  372. package/dist/parameters/claims-requested.d.ts +3 -0
  373. package/dist/parameters/claims-requested.d.ts.map +1 -0
  374. package/dist/parameters/claims-requested.js +77 -0
  375. package/dist/parameters/claims-requested.js.map +1 -0
  376. package/dist/parameters/oidc-payload.d.ts +31 -0
  377. package/dist/parameters/oidc-payload.d.ts.map +1 -0
  378. package/dist/parameters/oidc-payload.js +25 -0
  379. package/dist/parameters/oidc-payload.js.map +1 -0
  380. package/dist/replay/replay-manager.d.ts +10 -0
  381. package/dist/replay/replay-manager.d.ts.map +1 -0
  382. package/dist/replay/replay-manager.js +23 -0
  383. package/dist/replay/replay-manager.js.map +1 -0
  384. package/dist/replay/replay-store-memory.d.ts +11 -0
  385. package/dist/replay/replay-store-memory.d.ts.map +1 -0
  386. package/dist/replay/replay-store-memory.js +30 -0
  387. package/dist/replay/replay-store-memory.js.map +1 -0
  388. package/dist/replay/replay-store-redis.d.ts +16 -0
  389. package/dist/replay/replay-store-redis.d.ts.map +1 -0
  390. package/dist/replay/replay-store-redis.js +20 -0
  391. package/dist/replay/replay-store-redis.js.map +1 -0
  392. package/dist/replay/replay-store.d.ts +16 -0
  393. package/dist/replay/replay-store.d.ts.map +1 -0
  394. package/dist/replay/replay-store.js +22 -0
  395. package/dist/replay/replay-store.js.map +1 -0
  396. package/dist/request/code.d.ts +7 -0
  397. package/dist/request/code.d.ts.map +1 -0
  398. package/dist/request/code.js +20 -0
  399. package/dist/request/code.js.map +1 -0
  400. package/dist/request/request-data.d.ts +21 -0
  401. package/dist/request/request-data.d.ts.map +1 -0
  402. package/dist/request/request-data.js +6 -0
  403. package/dist/request/request-data.js.map +1 -0
  404. package/dist/request/request-id.d.ts +6 -0
  405. package/dist/request/request-id.d.ts.map +1 -0
  406. package/dist/request/request-id.js +18 -0
  407. package/dist/request/request-id.js.map +1 -0
  408. package/dist/request/request-info.d.ts +12 -0
  409. package/dist/request/request-info.d.ts.map +1 -0
  410. package/dist/request/request-info.js +3 -0
  411. package/dist/request/request-info.js.map +1 -0
  412. package/dist/request/request-manager.d.ts +40 -0
  413. package/dist/request/request-manager.d.ts.map +1 -0
  414. package/dist/request/request-manager.js +310 -0
  415. package/dist/request/request-manager.js.map +1 -0
  416. package/dist/request/request-store-memory.d.ts +16 -0
  417. package/dist/request/request-store-memory.d.ts.map +1 -0
  418. package/dist/request/request-store-memory.js +31 -0
  419. package/dist/request/request-store-memory.js.map +1 -0
  420. package/dist/request/request-store-redis.d.ts +24 -0
  421. package/dist/request/request-store-redis.d.ts.map +1 -0
  422. package/dist/request/request-store-redis.js +58 -0
  423. package/dist/request/request-store-redis.js.map +1 -0
  424. package/dist/request/request-store.d.ts +27 -0
  425. package/dist/request/request-store.d.ts.map +1 -0
  426. package/dist/request/request-store.js +37 -0
  427. package/dist/request/request-store.js.map +1 -0
  428. package/dist/request/request-uri.d.ts +8 -0
  429. package/dist/request/request-uri.d.ts.map +1 -0
  430. package/dist/request/request-uri.js +24 -0
  431. package/dist/request/request-uri.js.map +1 -0
  432. package/dist/request/types.d.ts +328 -0
  433. package/dist/request/types.d.ts.map +1 -0
  434. package/dist/request/types.js +27 -0
  435. package/dist/request/types.js.map +1 -0
  436. package/dist/signer/signed-token-payload.d.ts +1694 -0
  437. package/dist/signer/signed-token-payload.d.ts.map +1 -0
  438. package/dist/signer/signed-token-payload.js +32 -0
  439. package/dist/signer/signed-token-payload.js.map +1 -0
  440. package/dist/signer/signer.d.ts +193 -0
  441. package/dist/signer/signer.d.ts.map +1 -0
  442. package/dist/signer/signer.js +101 -0
  443. package/dist/signer/signer.js.map +1 -0
  444. package/dist/token/refresh-token.d.ts +7 -0
  445. package/dist/token/refresh-token.d.ts.map +1 -0
  446. package/dist/token/refresh-token.js +20 -0
  447. package/dist/token/refresh-token.js.map +1 -0
  448. package/dist/token/token-claims.d.ts +1687 -0
  449. package/dist/token/token-claims.d.ts.map +1 -0
  450. package/dist/token/token-claims.js +30 -0
  451. package/dist/token/token-claims.js.map +1 -0
  452. package/dist/token/token-data.d.ts +20 -0
  453. package/dist/token/token-data.d.ts.map +1 -0
  454. package/dist/token/token-data.js +3 -0
  455. package/dist/token/token-data.js.map +1 -0
  456. package/dist/token/token-id.d.ts +7 -0
  457. package/dist/token/token-id.d.ts.map +1 -0
  458. package/dist/token/token-id.js +20 -0
  459. package/dist/token/token-id.js.map +1 -0
  460. package/dist/token/token-manager.d.ts +48 -0
  461. package/dist/token/token-manager.d.ts.map +1 -0
  462. package/dist/token/token-manager.js +421 -0
  463. package/dist/token/token-manager.js.map +1 -0
  464. package/dist/token/token-store.d.ts +35 -0
  465. package/dist/token/token-store.d.ts.map +1 -0
  466. package/dist/token/token-store.js +38 -0
  467. package/dist/token/token-store.js.map +1 -0
  468. package/dist/token/types.d.ts +250 -0
  469. package/dist/token/types.d.ts.map +1 -0
  470. package/dist/token/types.js +36 -0
  471. package/dist/token/types.js.map +1 -0
  472. package/dist/token/verify-token-claims.d.ts +17 -0
  473. package/dist/token/verify-token-claims.d.ts.map +1 -0
  474. package/dist/token/verify-token-claims.js +39 -0
  475. package/dist/token/verify-token-claims.js.map +1 -0
  476. package/package.json +83 -0
  477. package/rollup.config.js +55 -0
  478. package/src/access-token/access-token-type.ts +5 -0
  479. package/src/account/account-manager.ts +55 -0
  480. package/src/account/account-store.ts +74 -0
  481. package/src/account/account.ts +10 -0
  482. package/src/assets/app/app.tsx +28 -0
  483. package/src/assets/app/backend-data.ts +65 -0
  484. package/src/assets/app/components/accept-form.tsx +112 -0
  485. package/src/assets/app/components/account-identifier.tsx +18 -0
  486. package/src/assets/app/components/account-picker.tsx +108 -0
  487. package/src/assets/app/components/client-identifier.tsx +32 -0
  488. package/src/assets/app/components/client-name.tsx +30 -0
  489. package/src/assets/app/components/error-card.tsx +41 -0
  490. package/src/assets/app/components/help-card.tsx +42 -0
  491. package/src/assets/app/components/layout-title-page.tsx +43 -0
  492. package/src/assets/app/components/layout-welcome.tsx +58 -0
  493. package/src/assets/app/components/sign-in-form.tsx +290 -0
  494. package/src/assets/app/components/sign-up-account-form.tsx +210 -0
  495. package/src/assets/app/components/sign-up-disclaimer.tsx +44 -0
  496. package/src/assets/app/components/url-viewer.tsx +70 -0
  497. package/src/assets/app/cookies.ts +11 -0
  498. package/src/assets/app/hooks/use-api.ts +104 -0
  499. package/src/assets/app/hooks/use-bound-dispatch.ts +5 -0
  500. package/src/assets/app/hooks/use-csrf-token.ts +5 -0
  501. package/src/assets/app/lib/api.ts +64 -0
  502. package/src/assets/app/lib/clsx.ts +4 -0
  503. package/src/assets/app/lib/util.ts +10 -0
  504. package/src/assets/app/main.css +11 -0
  505. package/src/assets/app/main.tsx +28 -0
  506. package/src/assets/app/views/accept-view.tsx +51 -0
  507. package/src/assets/app/views/authorize-view.tsx +101 -0
  508. package/src/assets/app/views/error-view.tsx +27 -0
  509. package/src/assets/app/views/sign-in-view.tsx +121 -0
  510. package/src/assets/app/views/sign-up-view.tsx +93 -0
  511. package/src/assets/app/views/welcome-view.tsx +61 -0
  512. package/src/assets/asset.ts +8 -0
  513. package/src/assets/assets-middleware.ts +32 -0
  514. package/src/assets/index.ts +74 -0
  515. package/src/client/client-auth.ts +45 -0
  516. package/src/client/client-data.ts +9 -0
  517. package/src/client/client-id.ts +4 -0
  518. package/src/client/client-info.ts +13 -0
  519. package/src/client/client-manager.ts +818 -0
  520. package/src/client/client-store.ts +38 -0
  521. package/src/client/client-utils.ts +43 -0
  522. package/src/client/client.ts +231 -0
  523. package/src/constants.ts +69 -0
  524. package/src/device/device-data.ts +11 -0
  525. package/src/device/device-details.ts +43 -0
  526. package/src/device/device-id.ts +23 -0
  527. package/src/device/device-manager.ts +287 -0
  528. package/src/device/device-store.ts +35 -0
  529. package/src/device/session-id.ts +22 -0
  530. package/src/dpop/dpop-manager.ts +147 -0
  531. package/src/dpop/dpop-nonce.ts +104 -0
  532. package/src/errors/access-denied-error.ts +26 -0
  533. package/src/errors/account-selection-required-error.ts +12 -0
  534. package/src/errors/consent-required-error.ts +12 -0
  535. package/src/errors/invalid-authorization-details-error.ts +22 -0
  536. package/src/errors/invalid-client-error.ts +20 -0
  537. package/src/errors/invalid-client-id-error.ts +20 -0
  538. package/src/errors/invalid-client-metadata-error.ts +19 -0
  539. package/src/errors/invalid-dpop-key-binding-error.ts +21 -0
  540. package/src/errors/invalid-dpop-proof-error.ts +13 -0
  541. package/src/errors/invalid-grant-error.ts +16 -0
  542. package/src/errors/invalid-parameters-error.ts +12 -0
  543. package/src/errors/invalid-redirect-uri-error.ts +17 -0
  544. package/src/errors/invalid-request-error.ts +30 -0
  545. package/src/errors/invalid-token-error.ts +59 -0
  546. package/src/errors/login-required-error.ts +12 -0
  547. package/src/errors/oauth-error.ts +28 -0
  548. package/src/errors/unauthorized-client-error.ts +20 -0
  549. package/src/errors/use-dpop-nonce-error.ts +32 -0
  550. package/src/errors/www-authenticate-error.ts +65 -0
  551. package/src/index.ts +15 -0
  552. package/src/lib/html/README.md +9 -0
  553. package/src/lib/html/build-document.ts +98 -0
  554. package/src/lib/html/escapers.ts +66 -0
  555. package/src/lib/html/html.ts +61 -0
  556. package/src/lib/html/index.ts +5 -0
  557. package/src/lib/html/tags.ts +58 -0
  558. package/src/lib/html/util.ts +21 -0
  559. package/src/lib/http/README.md +11 -0
  560. package/src/lib/http/accept.ts +91 -0
  561. package/src/lib/http/context.ts +11 -0
  562. package/src/lib/http/index.ts +9 -0
  563. package/src/lib/http/method.ts +18 -0
  564. package/src/lib/http/middleware.ts +183 -0
  565. package/src/lib/http/parser.ts +64 -0
  566. package/src/lib/http/path.ts +82 -0
  567. package/src/lib/http/request.ts +141 -0
  568. package/src/lib/http/response.ts +133 -0
  569. package/src/lib/http/route.ts +56 -0
  570. package/src/lib/http/router.ts +118 -0
  571. package/src/lib/http/stream.ts +78 -0
  572. package/src/lib/http/types.ts +22 -0
  573. package/src/lib/http/url.ts +23 -0
  574. package/src/lib/redis.ts +23 -0
  575. package/src/lib/util/authorization-header.ts +26 -0
  576. package/src/lib/util/cast.ts +4 -0
  577. package/src/lib/util/crypto.ts +27 -0
  578. package/src/lib/util/date.ts +7 -0
  579. package/src/lib/util/hostname.ts +19 -0
  580. package/src/lib/util/redirect-uri.ts +46 -0
  581. package/src/lib/util/time.ts +33 -0
  582. package/src/lib/util/type.ts +4 -0
  583. package/src/lib/util/well-known.ts +8 -0
  584. package/src/metadata/build-metadata.ts +165 -0
  585. package/src/oauth-client.ts +3 -0
  586. package/src/oauth-dpop.ts +2 -0
  587. package/src/oauth-errors.ts +21 -0
  588. package/src/oauth-hooks.ts +66 -0
  589. package/src/oauth-provider.ts +1409 -0
  590. package/src/oauth-store.ts +11 -0
  591. package/src/oauth-verifier.ts +219 -0
  592. package/src/oidc/claims.ts +35 -0
  593. package/src/oidc/sub.ts +4 -0
  594. package/src/oidc/userinfo.ts +11 -0
  595. package/src/output/build-error-payload.ts +143 -0
  596. package/src/output/customization.ts +96 -0
  597. package/src/output/send-authorize-page.ts +111 -0
  598. package/src/output/send-authorize-redirect.ts +130 -0
  599. package/src/output/send-error-page.ts +41 -0
  600. package/src/output/send-web-page.ts +66 -0
  601. package/src/parameters/claims-requested.ts +106 -0
  602. package/src/parameters/oidc-payload.ts +28 -0
  603. package/src/replay/replay-manager.ts +38 -0
  604. package/src/replay/replay-store-memory.ts +36 -0
  605. package/src/replay/replay-store-redis.ts +31 -0
  606. package/src/replay/replay-store.ts +44 -0
  607. package/src/request/code.ts +24 -0
  608. package/src/request/request-data.ts +26 -0
  609. package/src/request/request-id.ts +23 -0
  610. package/src/request/request-info.ts +12 -0
  611. package/src/request/request-manager.ts +479 -0
  612. package/src/request/request-store-memory.ts +39 -0
  613. package/src/request/request-store-redis.ts +71 -0
  614. package/src/request/request-store.ts +54 -0
  615. package/src/request/request-uri.ts +29 -0
  616. package/src/request/types.ts +48 -0
  617. package/src/signer/signed-token-payload.ts +35 -0
  618. package/src/signer/signer.ts +165 -0
  619. package/src/token/refresh-token.ts +31 -0
  620. package/src/token/token-claims.ts +31 -0
  621. package/src/token/token-data.ts +33 -0
  622. package/src/token/token-id.ts +26 -0
  623. package/src/token/token-manager.ts +591 -0
  624. package/src/token/token-store.ts +78 -0
  625. package/src/token/types.ts +86 -0
  626. package/src/token/verify-token-claims.ts +65 -0
  627. package/tailwind.config.js +13 -0
  628. package/tsconfig.backend.json +9 -0
  629. package/tsconfig.frontend.json +11 -0
  630. package/tsconfig.json +8 -0
  631. package/tsconfig.tools.json +8 -0
@@ -0,0 +1,287 @@
1
+ import { IncomingMessage, ServerResponse } from 'node:http'
2
+
3
+ import { serialize as serializeCookie } from 'cookie'
4
+ import type Keygrip from 'keygrip'
5
+ import { z } from 'zod'
6
+
7
+ import { appendHeader, parseHttpCookies } from '../lib/http/index.js'
8
+
9
+ import { SESSION_FIXATION_MAX_AGE } from '../constants.js'
10
+ import { DeviceData } from './device-data.js'
11
+ import { extractDeviceDetails } from './device-details.js'
12
+ import { DeviceId, deviceIdSchema, generateDeviceId } from './device-id.js'
13
+ import { DeviceStore } from './device-store.js'
14
+ import { generateSessionId, sessionIdSchema } from './session-id.js'
15
+
16
+ export const DEFAULT_OPTIONS = {
17
+ /**
18
+ * Controls whether the IP address is read from the `X-Forwarded-For` header
19
+ * (if `true`), or from the `req.socket.remoteAddress` property (if `false`).
20
+ *
21
+ * @default true // (nowadays, most requests are proxied)
22
+ */
23
+ trustProxy: true,
24
+
25
+ /**
26
+ * Amount of time (in ms) after which session IDs will be rotated
27
+ *
28
+ * @default 300e3 // (5 minutes)
29
+ */
30
+ rotationRate: 5 * 60e3,
31
+
32
+ /**
33
+ * Cookie options
34
+ */
35
+ cookie: {
36
+ keys: undefined as undefined | Keygrip,
37
+
38
+ /**
39
+ * Name of the cookie used to identify the device
40
+ *
41
+ * @default 'session-id'
42
+ */
43
+ device: 'device-id',
44
+
45
+ /**
46
+ * Name of the cookie used to identify the session
47
+ *
48
+ * @default 'session-id'
49
+ */
50
+ session: 'session-id',
51
+
52
+ /**
53
+ * Url path for the cookie
54
+ *
55
+ * @default '/oauth/authorize'
56
+ */
57
+ path: '/oauth/authorize',
58
+
59
+ /**
60
+ * Amount of time (in ms) after which the session cookie will expire.
61
+ * If set to `null`, the cookie will be a session cookie (deleted when the
62
+ * browser is closed).
63
+ *
64
+ * @default 10 * 365.2 * 24 * 60 * 60e3 // 10 years (in ms)
65
+ */
66
+ age: <number | null>(10 * 365.2 * 24 * 60 * 60e3),
67
+
68
+ /**
69
+ * Controls whether the cookie is only sent over HTTPS (if `true`), or also
70
+ * over HTTP (if `false`). This should **NOT** be set to `false` in
71
+ * production.
72
+ */
73
+ secure: true,
74
+
75
+ /**
76
+ * Controls whether the cookie is sent along with cross-site requests.
77
+ *
78
+ * @default 'lax'
79
+ */
80
+ sameSite: 'lax' as 'lax' | 'strict',
81
+ },
82
+ }
83
+
84
+ export type DeviceDeviceManagerOptions = typeof DEFAULT_OPTIONS
85
+
86
+ const cookieValueSchema = z.tuple([deviceIdSchema, sessionIdSchema])
87
+ type CookieValue = z.infer<typeof cookieValueSchema>
88
+
89
+ /**
90
+ * This class provides an abstraction for keeping track of DEVICE sessions. It
91
+ * relies on a {@link DeviceStore} to persist session data and a cookie to
92
+ * identify the session.
93
+ */
94
+ export class DeviceManager {
95
+ constructor(
96
+ private readonly store: DeviceStore,
97
+ private readonly options: DeviceDeviceManagerOptions = DEFAULT_OPTIONS,
98
+ ) {}
99
+
100
+ public async load(
101
+ req: IncomingMessage,
102
+ res: ServerResponse,
103
+ ): Promise<{ deviceId: DeviceId }> {
104
+ const cookie = await this.getCookie(req)
105
+ if (cookie) {
106
+ return this.refresh(req, res, cookie.value, cookie.mustRotate)
107
+ } else {
108
+ return this.create(req, res)
109
+ }
110
+ }
111
+
112
+ private async create(
113
+ req: IncomingMessage,
114
+ res: ServerResponse,
115
+ ): Promise<{ deviceId: DeviceId }> {
116
+ const { userAgent, ipAddress } = this.getDeviceDetails(req)
117
+
118
+ const [deviceId, sessionId] = await Promise.all([
119
+ generateDeviceId(),
120
+ generateSessionId(),
121
+ ] as const)
122
+
123
+ await this.store.createDevice(deviceId, {
124
+ sessionId,
125
+ lastSeenAt: new Date(),
126
+ userAgent,
127
+ ipAddress,
128
+ })
129
+
130
+ this.setCookie(res, [deviceId, sessionId])
131
+
132
+ return { deviceId }
133
+ }
134
+
135
+ private async refresh(
136
+ req: IncomingMessage,
137
+ res: ServerResponse,
138
+ [deviceId, sessionId]: CookieValue,
139
+ forceRotate = false,
140
+ ): Promise<{ deviceId: DeviceId }> {
141
+ const data = await this.store.readDevice(deviceId)
142
+ if (!data) return this.create(req, res)
143
+
144
+ const lastSeenAt = new Date(data.lastSeenAt)
145
+ const age = Date.now() - lastSeenAt.getTime()
146
+
147
+ if (sessionId !== data.sessionId) {
148
+ if (age <= SESSION_FIXATION_MAX_AGE) {
149
+ // The cookie was probably rotated by a concurrent request. Let's
150
+ // update the cookie with the new sessionId.
151
+ forceRotate = true
152
+ } else {
153
+ // Something's wrong. Let's create a new session.
154
+ await this.store.deleteDevice(deviceId)
155
+ return this.create(req, res)
156
+ }
157
+ }
158
+
159
+ const details = this.getDeviceDetails(req)
160
+
161
+ if (
162
+ forceRotate ||
163
+ details.ipAddress !== data.ipAddress ||
164
+ details.userAgent !== data.userAgent ||
165
+ age > this.options.rotationRate
166
+ ) {
167
+ await this.rotate(req, res, deviceId, {
168
+ ipAddress: details.ipAddress,
169
+ userAgent: details.userAgent || data.userAgent,
170
+ })
171
+ }
172
+
173
+ return { deviceId }
174
+ }
175
+
176
+ public async rotate(
177
+ req: IncomingMessage,
178
+ res: ServerResponse,
179
+ deviceId: DeviceId,
180
+ data?: Partial<Omit<DeviceData, 'sessionId' | 'lastSeenAt'>>,
181
+ ): Promise<void> {
182
+ const sessionId = await generateSessionId()
183
+
184
+ await this.store.updateDevice(deviceId, {
185
+ ...data,
186
+ sessionId,
187
+ lastSeenAt: new Date(),
188
+ })
189
+
190
+ this.setCookie(res, [deviceId, sessionId])
191
+ }
192
+
193
+ private async getCookie(
194
+ req: IncomingMessage,
195
+ ): Promise<{ value: CookieValue; mustRotate: boolean } | null> {
196
+ const cookies = parseHttpCookies(req)
197
+ if (!cookies) return null
198
+
199
+ const device = this.parseCookie(
200
+ cookies,
201
+ this.options.cookie.device,
202
+ deviceIdSchema,
203
+ )
204
+ const session = this.parseCookie(
205
+ cookies,
206
+ this.options.cookie.session,
207
+ sessionIdSchema,
208
+ )
209
+
210
+ // Silently ignore invalid cookies
211
+ if (!device || !session) {
212
+ // If the device cookie is valid, let's cleanup the DB
213
+ if (device) await this.store.deleteDevice(device.value)
214
+
215
+ return null
216
+ }
217
+
218
+ return {
219
+ value: [device.value, session.value],
220
+ mustRotate: device.mustRotate || session.mustRotate,
221
+ }
222
+ }
223
+
224
+ private parseCookie<T>(
225
+ cookies: Record<string, string | undefined>,
226
+ name: string,
227
+ schema: z.ZodType<T> | z.ZodEffects<z.ZodTypeAny, T, string>,
228
+ ): null | { value: T; mustRotate: boolean } {
229
+ const result = schema.safeParse(cookies[name], { path: ['cookie', name] })
230
+ if (!result.success) return null
231
+
232
+ const value = result.data
233
+
234
+ if (this.options.cookie.keys) {
235
+ const hash = cookies[`${name}:hash`]
236
+ if (!hash) return null
237
+
238
+ const idx = this.options.cookie.keys.index(value, hash)
239
+ if (idx < 0) return null
240
+
241
+ return { value, mustRotate: idx !== 0 }
242
+ }
243
+
244
+ return { value, mustRotate: false }
245
+ }
246
+
247
+ private setCookie(res: ServerResponse, cookieValue: null | CookieValue) {
248
+ this.writeCookie(res, this.options.cookie.device, cookieValue?.[0])
249
+ this.writeCookie(res, this.options.cookie.session, cookieValue?.[1])
250
+ }
251
+
252
+ private writeCookie(res: ServerResponse, name: string, value?: string) {
253
+ const cookieOptions = {
254
+ maxAge: value
255
+ ? this.options.cookie.age == null
256
+ ? undefined
257
+ : this.options.cookie.age / 1000
258
+ : 0,
259
+ httpOnly: true,
260
+ path: this.options.cookie.path,
261
+ secure: this.options.cookie.secure !== false,
262
+ sameSite: this.options.cookie.sameSite === 'lax' ? 'lax' : 'strict',
263
+ } as const
264
+
265
+ appendHeader(
266
+ res,
267
+ 'Set-Cookie',
268
+ serializeCookie(name, value || '', cookieOptions),
269
+ )
270
+
271
+ if (this.options.cookie.keys) {
272
+ appendHeader(
273
+ res,
274
+ 'Set-Cookie',
275
+ serializeCookie(
276
+ `${name}:hash`,
277
+ value ? this.options.cookie.keys.sign(value) : '',
278
+ cookieOptions,
279
+ ),
280
+ )
281
+ }
282
+ }
283
+
284
+ private getDeviceDetails(req: IncomingMessage) {
285
+ return extractDeviceDetails(req, this.options.trustProxy)
286
+ }
287
+ }
@@ -0,0 +1,35 @@
1
+ import { Awaitable } from '../lib/util/type.js'
2
+ import { DeviceData } from './device-data.js'
3
+ import { DeviceId } from './device-id.js'
4
+
5
+ // Export all types needed to implement the DeviceStore interface
6
+ export * from './device-id.js'
7
+ export * from './device-data.js'
8
+ export * from './session-id.js'
9
+
10
+ export interface DeviceStore {
11
+ createDevice(deviceId: DeviceId, data: DeviceData): Awaitable<void>
12
+ readDevice(deviceId: DeviceId): Awaitable<DeviceData | null>
13
+ updateDevice(deviceId: DeviceId, data: Partial<DeviceData>): Awaitable<void>
14
+ deleteDevice(deviceId: DeviceId): Awaitable<void>
15
+ }
16
+
17
+ export function isDeviceStore(
18
+ implementation: Record<string, unknown> & Partial<DeviceStore>,
19
+ ): implementation is Record<string, unknown> & DeviceStore {
20
+ return (
21
+ typeof implementation.createDevice === 'function' &&
22
+ typeof implementation.readDevice === 'function' &&
23
+ typeof implementation.updateDevice === 'function' &&
24
+ typeof implementation.deleteDevice === 'function'
25
+ )
26
+ }
27
+
28
+ export function asDeviceStore(
29
+ implementation?: Record<string, unknown> & Partial<DeviceStore>,
30
+ ): DeviceStore {
31
+ if (!implementation || !isDeviceStore(implementation)) {
32
+ throw new Error('Invalid DeviceStore implementation')
33
+ }
34
+ return implementation
35
+ }
@@ -0,0 +1,22 @@
1
+ import { z } from 'zod'
2
+
3
+ import { SESSION_ID_BYTES_LENGTH, SESSION_ID_PREFIX } from '../constants.js'
4
+ import { randomHexId } from '../lib/util/crypto.js'
5
+
6
+ export const SESSION_ID_LENGTH =
7
+ SESSION_ID_PREFIX.length + SESSION_ID_BYTES_LENGTH * 2 // hex encoding
8
+
9
+ export const sessionIdSchema = z
10
+ .string()
11
+ .length(SESSION_ID_LENGTH)
12
+ .refine(
13
+ (v): v is `${typeof SESSION_ID_PREFIX}${string}` =>
14
+ v.startsWith(SESSION_ID_PREFIX),
15
+ {
16
+ message: `Invalid session ID format`,
17
+ },
18
+ )
19
+ export type SessionId = z.infer<typeof sessionIdSchema>
20
+ export const generateSessionId = async (): Promise<SessionId> => {
21
+ return `${SESSION_ID_PREFIX}${await randomHexId(SESSION_ID_BYTES_LENGTH)}`
22
+ }
@@ -0,0 +1,147 @@
1
+ import { createHash } from 'node:crypto'
2
+
3
+ import { EmbeddedJWK, calculateJwkThumbprint, jwtVerify } from 'jose'
4
+ import { JOSEError } from 'jose/errors'
5
+
6
+ import { DPOP_NONCE_MAX_AGE } from '../constants.js'
7
+ import { InvalidDpopProofError } from '../errors/invalid-dpop-proof-error.js'
8
+ import { UseDpopNonceError } from '../errors/use-dpop-nonce-error.js'
9
+ import { DpopNonce, DpopNonceInput } from './dpop-nonce.js'
10
+
11
+ export { DpopNonce, type DpopNonceInput }
12
+ export type DpopManagerOptions = {
13
+ /**
14
+ * Set this to `false` to disable the use of nonces in DPoP proofs. Set this
15
+ * to a secret Uint8Array or hex encoded string to use a predictable seed for
16
+ * all nonces (typically useful when multiple instances are running). Leave
17
+ * undefined to generate a random seed at startup.
18
+ */
19
+ dpopSecret?: false | DpopNonceInput
20
+ dpopStep?: number
21
+ }
22
+
23
+ export class DpopManager {
24
+ protected readonly dpopNonce?: DpopNonce
25
+
26
+ constructor({ dpopSecret, dpopStep }: DpopManagerOptions = {}) {
27
+ this.dpopNonce =
28
+ dpopSecret === false ? undefined : DpopNonce.from(dpopSecret, dpopStep)
29
+ }
30
+
31
+ nextNonce(): string | undefined {
32
+ return this.dpopNonce?.next()
33
+ }
34
+
35
+ /**
36
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc9449#section-4.3}
37
+ */
38
+ async checkProof(
39
+ proof: unknown,
40
+ htm: string, // HTTP Method
41
+ htu: string | URL, // HTTP URL
42
+ accessToken?: string, // Access Token
43
+ ) {
44
+ if (Array.isArray(proof) && proof.length === 1) {
45
+ proof = proof[0]
46
+ }
47
+
48
+ if (!proof || typeof proof !== 'string') {
49
+ throw new InvalidDpopProofError('DPoP proof required')
50
+ }
51
+
52
+ const { protectedHeader, payload } = await jwtVerify<{
53
+ iat: number
54
+ exp: number
55
+ jti: string
56
+ }>(proof, EmbeddedJWK, {
57
+ typ: 'dpop+jwt',
58
+ maxTokenAge: 10,
59
+ clockTolerance: DPOP_NONCE_MAX_AGE / 1e3,
60
+ requiredClaims: ['iat', 'exp', 'jti'],
61
+ }).catch((err) => {
62
+ const message =
63
+ err instanceof JOSEError
64
+ ? `Invalid DPoP proof (${err.message})`
65
+ : 'Invalid DPoP proof'
66
+ throw new InvalidDpopProofError(message, err)
67
+ })
68
+
69
+ if (!payload.jti || typeof payload.jti !== 'string') {
70
+ throw new InvalidDpopProofError('Invalid or missing jti property')
71
+ }
72
+
73
+ if (payload.exp - payload.iat > DPOP_NONCE_MAX_AGE / 3 / 1e3) {
74
+ throw new InvalidDpopProofError('DPoP proof validity too long')
75
+ }
76
+
77
+ // Note rfc9110#section-9.1 states that the method name is case-sensitive
78
+ if (!htm || htm !== payload['htm']) {
79
+ throw new InvalidDpopProofError('DPoP htm mismatch')
80
+ }
81
+
82
+ if (
83
+ payload['nonce'] !== undefined &&
84
+ typeof payload['nonce'] !== 'string'
85
+ ) {
86
+ throw new InvalidDpopProofError('DPoP nonce must be a string')
87
+ }
88
+
89
+ if (!payload['nonce'] && this.dpopNonce) {
90
+ throw new UseDpopNonceError()
91
+ }
92
+
93
+ if (payload['nonce'] && !this.dpopNonce?.check(payload['nonce'])) {
94
+ throw new UseDpopNonceError()
95
+ }
96
+
97
+ const htuNorm = normalizeHtu(htu)
98
+ if (!htuNorm || htuNorm !== normalizeHtu(payload['htu'])) {
99
+ throw new InvalidDpopProofError('DPoP htu mismatch')
100
+ }
101
+
102
+ if (accessToken) {
103
+ const athBuffer = createHash('sha256').update(accessToken).digest()
104
+ if (payload['ath'] !== athBuffer.toString('base64url')) {
105
+ throw new InvalidDpopProofError('DPoP ath mismatch')
106
+ }
107
+ } else if (payload['ath']) {
108
+ throw new InvalidDpopProofError('DPoP ath not allowed')
109
+ }
110
+
111
+ try {
112
+ return {
113
+ protectedHeader,
114
+ payload,
115
+ jkt: await calculateJwkThumbprint(protectedHeader['jwk']!, 'sha256'), // EmbeddedJWK
116
+ }
117
+ } catch (err) {
118
+ const message =
119
+ err instanceof JOSEError ? err.message : 'Failed to calculate jkt'
120
+ throw new InvalidDpopProofError(message, err)
121
+ }
122
+ }
123
+ }
124
+
125
+ /**
126
+ * @note
127
+ * > The htu claim matches the HTTP URI value for the HTTP request in which the
128
+ * > JWT was received, ignoring any query and fragment parts.
129
+ *
130
+ * > To reduce the likelihood of false negatives, servers SHOULD employ
131
+ * > syntax-based normalization (Section 6.2.2 of [RFC3986]) and scheme-based
132
+ * > normalization (Section 6.2.3 of [RFC3986]) before comparing the htu claim.
133
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc9449#section-4.3 | RFC9449 section 4.3. Checking DPoP Proofs}
134
+ */
135
+ function normalizeHtu(htu: unknown): string | null {
136
+ // Optimization
137
+ if (!htu) return null
138
+
139
+ try {
140
+ const url = new URL(String(htu))
141
+ url.hash = ''
142
+ url.search = ''
143
+ return url.href
144
+ } catch {
145
+ return null
146
+ }
147
+ }
@@ -0,0 +1,104 @@
1
+ import { createHmac, randomBytes } from 'node:crypto'
2
+
3
+ import { DPOP_NONCE_MAX_AGE } from '../constants.js'
4
+
5
+ function numTo64bits(num: number) {
6
+ const arr = new Uint8Array(8)
7
+ arr[7] = (num = num | 0) & 0xff
8
+ arr[6] = (num >>= 8) & 0xff
9
+ arr[5] = (num >>= 8) & 0xff
10
+ arr[4] = (num >>= 8) & 0xff
11
+ arr[3] = (num >>= 8) & 0xff
12
+ arr[2] = (num >>= 8) & 0xff
13
+ arr[1] = (num >>= 8) & 0xff
14
+ arr[0] = (num >>= 8) & 0xff
15
+ return arr
16
+ }
17
+
18
+ export type DpopNonceInput = string | Uint8Array | DpopNonce
19
+
20
+ export class DpopNonce {
21
+ #secret: Uint8Array
22
+ #counter: number
23
+
24
+ #prev: string
25
+ #now: string
26
+ #next: string
27
+
28
+ constructor(
29
+ protected readonly secret: Uint8Array,
30
+ protected readonly step: number,
31
+ ) {
32
+ if (secret.length !== 32) throw new TypeError('Expected 32 bytes')
33
+ if (this.step < 0 || this.step > DPOP_NONCE_MAX_AGE / 3) {
34
+ throw new TypeError('Invalid step')
35
+ }
36
+
37
+ this.#secret = Uint8Array.from(secret)
38
+ this.#counter = (Date.now() / step) | 0
39
+
40
+ this.#prev = this.compute(this.#counter - 1)
41
+ this.#now = this.compute(this.#counter)
42
+ this.#next = this.compute(this.#counter + 1)
43
+ }
44
+
45
+ protected rotate() {
46
+ const counter = (Date.now() / this.step) | 0
47
+ switch (counter - this.#counter) {
48
+ case 0:
49
+ // counter === this.#counter => nothing to do
50
+ return
51
+ case 1:
52
+ // Optimization: avoid recomputing #prev & #now
53
+ this.#prev = this.#now
54
+ this.#now = this.#next
55
+ this.#next = this.compute(counter + 1)
56
+ break
57
+ case 2:
58
+ // Optimization: avoid recomputing #prev
59
+ this.#prev = this.#next
60
+ this.#now = this.compute(counter)
61
+ this.#next = this.compute(counter + 1)
62
+ break
63
+ default:
64
+ // All nonces are outdated, so we recompute all of them
65
+ this.#prev = this.compute(counter - 1)
66
+ this.#now = this.compute(counter)
67
+ this.#next = this.compute(counter + 1)
68
+ break
69
+ }
70
+ this.#counter = counter
71
+ }
72
+
73
+ protected compute(counter: number) {
74
+ return createHmac('sha256', this.#secret)
75
+ .update(numTo64bits(counter))
76
+ .digest()
77
+ .toString('base64url')
78
+ }
79
+
80
+ public next() {
81
+ this.rotate()
82
+ return this.#next
83
+ }
84
+
85
+ public check(nonce: string) {
86
+ return this.#next === nonce || this.#now === nonce || this.#prev === nonce
87
+ }
88
+
89
+ static from(
90
+ input: DpopNonceInput = randomBytes(32),
91
+ step = DPOP_NONCE_MAX_AGE / 3,
92
+ ): DpopNonce {
93
+ if (input instanceof DpopNonce) {
94
+ return input
95
+ }
96
+ if (input instanceof Uint8Array) {
97
+ return new DpopNonce(input, step)
98
+ }
99
+ if (typeof input === 'string') {
100
+ return new DpopNonce(Buffer.from(input, 'hex'), step)
101
+ }
102
+ return new DpopNonce(input, step)
103
+ }
104
+ }
@@ -0,0 +1,26 @@
1
+ import { OAuthAuthenticationRequestParameters } from '@atproto/oauth-types'
2
+ import { buildErrorPayload } from '../output/build-error-payload.js'
3
+ import { OAuthError } from './oauth-error.js'
4
+
5
+ export class AccessDeniedError extends OAuthError {
6
+ constructor(
7
+ public readonly parameters: OAuthAuthenticationRequestParameters,
8
+ error_description: string,
9
+ error = 'access_denied',
10
+ cause?: unknown,
11
+ ) {
12
+ super(error, error_description, 400, cause)
13
+ }
14
+
15
+ static from(
16
+ parameters: OAuthAuthenticationRequestParameters,
17
+ cause?: unknown,
18
+ ) {
19
+ if (cause && cause instanceof AccessDeniedError) {
20
+ return cause
21
+ }
22
+
23
+ const { error, error_description } = buildErrorPayload(cause)
24
+ return new AccessDeniedError(parameters, error_description, error, cause)
25
+ }
26
+ }
@@ -0,0 +1,12 @@
1
+ import { OAuthAuthenticationRequestParameters } from '@atproto/oauth-types'
2
+ import { AccessDeniedError } from './access-denied-error.js'
3
+
4
+ export class AccountSelectionRequiredError extends AccessDeniedError {
5
+ constructor(
6
+ parameters: OAuthAuthenticationRequestParameters,
7
+ error_description = 'Account selection required',
8
+ cause?: unknown,
9
+ ) {
10
+ super(parameters, error_description, 'account_selection_required', cause)
11
+ }
12
+ }
@@ -0,0 +1,12 @@
1
+ import { OAuthAuthenticationRequestParameters } from '@atproto/oauth-types'
2
+ import { AccessDeniedError } from './access-denied-error.js'
3
+
4
+ export class ConsentRequiredError extends AccessDeniedError {
5
+ constructor(
6
+ parameters: OAuthAuthenticationRequestParameters,
7
+ error_description = 'User consent required',
8
+ cause?: unknown,
9
+ ) {
10
+ super(parameters, error_description, 'consent_required', cause)
11
+ }
12
+ }
@@ -0,0 +1,22 @@
1
+ import { OAuthError } from './oauth-error.js'
2
+
3
+ /**
4
+ * @see
5
+ * {@link https://datatracker.ietf.org/doc/html/rfc9396#section-14.6 | RFC 9396 - OAuth Dynamic Client Registration Metadata Registration Error}
6
+ *
7
+ * The AS MUST refuse to process any unknown authorization details type or
8
+ * authorization details not conforming to the respective type definition. The
9
+ * AS MUST abort processing and respond with an error
10
+ * invalid_authorization_details to the client if any of the following are true
11
+ * of the objects in the authorization_details structure:
12
+ * - contains an unknown authorization details type value,
13
+ * - is an object of known type but containing unknown fields,
14
+ * - contains fields of the wrong type for the authorization details type,
15
+ * - contains fields with invalid values for the authorization details type, or
16
+ * - is missing required fields for the authorization details type.
17
+ */
18
+ export class InvalidAuthorizationDetailsError extends OAuthError {
19
+ constructor(error_description: string, cause?: unknown) {
20
+ super('invalid_authorization_details', error_description, 400, cause)
21
+ }
22
+ }
@@ -0,0 +1,20 @@
1
+ import { OAuthError } from './oauth-error.js'
2
+
3
+ /**
4
+ * @see
5
+ * {@link https://datatracker.ietf.org/doc/html/rfc6749#section-5.2 | RFC6749 - Issuing an Access Token }
6
+ *
7
+ * Client authentication failed (e.g., unknown client, no client authentication
8
+ * included, or unsupported authentication method). The authorization server MAY
9
+ * return an HTTP 401 (Unauthorized) status code to indicate which HTTP
10
+ * authentication schemes are supported. If the client attempted to
11
+ * authenticate via the "Authorization" request header field, the authorization
12
+ * server MUST respond with an HTTP 401 (Unauthorized) status code and include
13
+ * the "WWW-Authenticate" response header field matching the authentication
14
+ * scheme used by the client.
15
+ */
16
+ export class InvalidClientError extends OAuthError {
17
+ constructor(error_description: string, cause?: unknown) {
18
+ super('invalid_client', error_description, 400, cause)
19
+ }
20
+ }