@atproto/oauth-provider 0.16.5 → 0.17.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 (321) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/access-token/access-token-mode.js +2 -5
  3. package/dist/access-token/access-token-mode.js.map +1 -1
  4. package/dist/account/account-manager.js +25 -33
  5. package/dist/account/account-manager.js.map +1 -1
  6. package/dist/account/account-store.js +11 -32
  7. package/dist/account/account-store.js.map +1 -1
  8. package/dist/account/sign-in-data.js +9 -12
  9. package/dist/account/sign-in-data.js.map +1 -1
  10. package/dist/account/sign-up-input.js +14 -17
  11. package/dist/account/sign-up-input.js.map +1 -1
  12. package/dist/client/client-auth.js +1 -2
  13. package/dist/client/client-data.js +1 -2
  14. package/dist/client/client-id.js +2 -5
  15. package/dist/client/client-id.js.map +1 -1
  16. package/dist/client/client-info.js +1 -2
  17. package/dist/client/client-manager.js +86 -97
  18. package/dist/client/client-manager.js.map +1 -1
  19. package/dist/client/client-store.js +7 -26
  20. package/dist/client/client-store.js.map +1 -1
  21. package/dist/client/client-utils.js +10 -14
  22. package/dist/client/client-utils.js.map +1 -1
  23. package/dist/client/client.js +43 -53
  24. package/dist/client/client.js.map +1 -1
  25. package/dist/constants.js +28 -31
  26. package/dist/constants.js.map +1 -1
  27. package/dist/customization/branding.js +8 -11
  28. package/dist/customization/branding.js.map +1 -1
  29. package/dist/customization/build-customization-css.js +8 -11
  30. package/dist/customization/build-customization-css.js.map +1 -1
  31. package/dist/customization/build-customization-data.js +1 -4
  32. package/dist/customization/build-customization-data.js.map +1 -1
  33. package/dist/customization/colors.js +11 -14
  34. package/dist/customization/colors.js.map +1 -1
  35. package/dist/customization/customization.js +8 -11
  36. package/dist/customization/customization.js.map +1 -1
  37. package/dist/customization/links.js +7 -10
  38. package/dist/customization/links.js.map +1 -1
  39. package/dist/device/device-data.js +7 -10
  40. package/dist/device/device-data.js.map +1 -1
  41. package/dist/device/device-id.js +11 -16
  42. package/dist/device/device-id.js.map +1 -1
  43. package/dist/device/device-manager.js +32 -38
  44. package/dist/device/device-manager.js.map +1 -1
  45. package/dist/device/device-store.js +7 -25
  46. package/dist/device/device-store.js.map +1 -1
  47. package/dist/device/session-id.js +9 -13
  48. package/dist/device/session-id.js.map +1 -1
  49. package/dist/dpop/dpop-manager.d.ts +3 -3
  50. package/dist/dpop/dpop-manager.js +38 -43
  51. package/dist/dpop/dpop-manager.js.map +1 -1
  52. package/dist/dpop/dpop-nonce.d.ts +2 -2
  53. package/dist/dpop/dpop-nonce.d.ts.map +1 -1
  54. package/dist/dpop/dpop-nonce.js +14 -18
  55. package/dist/dpop/dpop-nonce.js.map +1 -1
  56. package/dist/dpop/dpop-proof.js +1 -2
  57. package/dist/errors/access-denied-error.js +2 -6
  58. package/dist/errors/access-denied-error.js.map +1 -1
  59. package/dist/errors/account-selection-required-error.js +2 -6
  60. package/dist/errors/account-selection-required-error.js.map +1 -1
  61. package/dist/errors/authorization-error.js +7 -12
  62. package/dist/errors/authorization-error.js.map +1 -1
  63. package/dist/errors/consent-required-error.js +2 -6
  64. package/dist/errors/consent-required-error.js.map +1 -1
  65. package/dist/errors/error-parser.js +14 -18
  66. package/dist/errors/error-parser.js.map +1 -1
  67. package/dist/errors/handle-unavailable-error.js +2 -7
  68. package/dist/errors/handle-unavailable-error.js.map +1 -1
  69. package/dist/errors/invalid-authorization-details-error.js +2 -6
  70. package/dist/errors/invalid-authorization-details-error.js.map +1 -1
  71. package/dist/errors/invalid-client-error.js +2 -6
  72. package/dist/errors/invalid-client-error.js.map +1 -1
  73. package/dist/errors/invalid-client-id-error.js +2 -6
  74. package/dist/errors/invalid-client-id-error.js.map +1 -1
  75. package/dist/errors/invalid-client-metadata-error.js +7 -11
  76. package/dist/errors/invalid-client-metadata-error.js.map +1 -1
  77. package/dist/errors/invalid-credentials-error.js +2 -7
  78. package/dist/errors/invalid-credentials-error.js.map +1 -1
  79. package/dist/errors/invalid-dpop-key-binding-error.js +2 -6
  80. package/dist/errors/invalid-dpop-key-binding-error.js.map +1 -1
  81. package/dist/errors/invalid-dpop-proof-error.js +2 -6
  82. package/dist/errors/invalid-dpop-proof-error.js.map +1 -1
  83. package/dist/errors/invalid-grant-error.js +2 -6
  84. package/dist/errors/invalid-grant-error.js.map +1 -1
  85. package/dist/errors/invalid-invite-code-error.d.ts +1 -1
  86. package/dist/errors/invalid-invite-code-error.d.ts.map +1 -1
  87. package/dist/errors/invalid-invite-code-error.js +2 -6
  88. package/dist/errors/invalid-invite-code-error.js.map +1 -1
  89. package/dist/errors/invalid-redirect-uri-error.js +2 -6
  90. package/dist/errors/invalid-redirect-uri-error.js.map +1 -1
  91. package/dist/errors/invalid-request-error.js +3 -7
  92. package/dist/errors/invalid-request-error.js.map +1 -1
  93. package/dist/errors/invalid-scope-error.js +2 -6
  94. package/dist/errors/invalid-scope-error.js.map +1 -1
  95. package/dist/errors/invalid-token-error.js +10 -15
  96. package/dist/errors/invalid-token-error.js.map +1 -1
  97. package/dist/errors/login-required-error.js +2 -6
  98. package/dist/errors/login-required-error.js.map +1 -1
  99. package/dist/errors/oauth-error.js +1 -9
  100. package/dist/errors/oauth-error.js.map +1 -1
  101. package/dist/errors/second-authentication-factor-required-error.js +2 -8
  102. package/dist/errors/second-authentication-factor-required-error.js.map +1 -1
  103. package/dist/errors/unauthorized-client-error.js +2 -6
  104. package/dist/errors/unauthorized-client-error.js.map +1 -1
  105. package/dist/errors/use-dpop-nonce-error.js +4 -8
  106. package/dist/errors/use-dpop-nonce-error.js.map +1 -1
  107. package/dist/errors/www-authenticate-error.js +4 -9
  108. package/dist/errors/www-authenticate-error.js.map +1 -1
  109. package/dist/index.js +14 -30
  110. package/dist/index.js.map +1 -1
  111. package/dist/lexicon/lexicon-data.js +1 -2
  112. package/dist/lexicon/lexicon-getter.js +6 -10
  113. package/dist/lexicon/lexicon-getter.js.map +1 -1
  114. package/dist/lexicon/lexicon-manager.js +10 -30
  115. package/dist/lexicon/lexicon-manager.js.map +1 -1
  116. package/dist/lexicon/lexicon-store.js +5 -10
  117. package/dist/lexicon/lexicon-store.js.map +1 -1
  118. package/dist/lib/csp/index.js +3 -8
  119. package/dist/lib/csp/index.js.map +1 -1
  120. package/dist/lib/hcaptcha.js +33 -43
  121. package/dist/lib/hcaptcha.js.map +1 -1
  122. package/dist/lib/html/build-document.js +19 -24
  123. package/dist/lib/html/build-document.js.map +1 -1
  124. package/dist/lib/html/escapers.js +10 -16
  125. package/dist/lib/html/escapers.js.map +1 -1
  126. package/dist/lib/html/html.js +1 -5
  127. package/dist/lib/html/html.js.map +1 -1
  128. package/dist/lib/html/hydration-data.js +6 -10
  129. package/dist/lib/html/hydration-data.js.map +1 -1
  130. package/dist/lib/html/index.js +3 -19
  131. package/dist/lib/html/index.js.map +1 -1
  132. package/dist/lib/html/tags.js +14 -23
  133. package/dist/lib/html/tags.js.map +1 -1
  134. package/dist/lib/html/util.js +1 -4
  135. package/dist/lib/html/util.js.map +1 -1
  136. package/dist/lib/http/accept.d.ts.map +1 -1
  137. package/dist/lib/http/accept.js +8 -8
  138. package/dist/lib/http/accept.js.map +1 -1
  139. package/dist/lib/http/context.js +1 -4
  140. package/dist/lib/http/context.js.map +1 -1
  141. package/dist/lib/http/headers.js +1 -4
  142. package/dist/lib/http/headers.js.map +1 -1
  143. package/dist/lib/http/index.js +10 -26
  144. package/dist/lib/http/index.js.map +1 -1
  145. package/dist/lib/http/method.js +1 -4
  146. package/dist/lib/http/method.js.map +1 -1
  147. package/dist/lib/http/middleware.js +11 -17
  148. package/dist/lib/http/middleware.js.map +1 -1
  149. package/dist/lib/http/parser.js +13 -20
  150. package/dist/lib/http/parser.js.map +1 -1
  151. package/dist/lib/http/path.js +1 -4
  152. package/dist/lib/http/path.js.map +1 -1
  153. package/dist/lib/http/request.d.ts.map +1 -1
  154. package/dist/lib/http/request.js +32 -47
  155. package/dist/lib/http/request.js.map +1 -1
  156. package/dist/lib/http/response.js +14 -27
  157. package/dist/lib/http/response.js.map +1 -1
  158. package/dist/lib/http/route.js +9 -12
  159. package/dist/lib/http/route.js.map +1 -1
  160. package/dist/lib/http/router.js +8 -13
  161. package/dist/lib/http/router.js.map +1 -1
  162. package/dist/lib/http/security-headers.js +10 -15
  163. package/dist/lib/http/security-headers.js.map +1 -1
  164. package/dist/lib/http/stream.js +12 -20
  165. package/dist/lib/http/stream.js.map +1 -1
  166. package/dist/lib/http/types.js +1 -2
  167. package/dist/lib/http/url.js +1 -4
  168. package/dist/lib/http/url.js.map +1 -1
  169. package/dist/lib/nsid.js +4 -8
  170. package/dist/lib/nsid.js.map +1 -1
  171. package/dist/lib/redis.js +4 -7
  172. package/dist/lib/redis.js.map +1 -1
  173. package/dist/lib/util/authorization-header.js +11 -15
  174. package/dist/lib/util/authorization-header.js.map +1 -1
  175. package/dist/lib/util/cast.js +3 -8
  176. package/dist/lib/util/cast.js.map +1 -1
  177. package/dist/lib/util/color.js +23 -32
  178. package/dist/lib/util/color.js.map +1 -1
  179. package/dist/lib/util/crypto.js +5 -10
  180. package/dist/lib/util/crypto.js.map +1 -1
  181. package/dist/lib/util/date.js +2 -6
  182. package/dist/lib/util/date.js.map +1 -1
  183. package/dist/lib/util/error.js +5 -8
  184. package/dist/lib/util/error.js.map +1 -1
  185. package/dist/lib/util/function.js +3 -8
  186. package/dist/lib/util/function.js.map +1 -1
  187. package/dist/lib/util/locale.js +3 -6
  188. package/dist/lib/util/locale.js.map +1 -1
  189. package/dist/lib/util/object.js +1 -4
  190. package/dist/lib/util/object.js.map +1 -1
  191. package/dist/lib/util/redirect-uri.js +3 -6
  192. package/dist/lib/util/redirect-uri.js.map +1 -1
  193. package/dist/lib/util/time.js +5 -9
  194. package/dist/lib/util/time.js.map +1 -1
  195. package/dist/lib/util/type.d.ts.map +1 -1
  196. package/dist/lib/util/type.js +1 -5
  197. package/dist/lib/util/type.js.map +1 -1
  198. package/dist/lib/util/ui8.js +3 -8
  199. package/dist/lib/util/ui8.js.map +1 -1
  200. package/dist/lib/util/well-known.js +1 -4
  201. package/dist/lib/util/well-known.js.map +1 -1
  202. package/dist/lib/util/zod-error.js +4 -8
  203. package/dist/lib/util/zod-error.js.map +1 -1
  204. package/dist/lib/write-form-redirect.js +9 -12
  205. package/dist/lib/write-form-redirect.js.map +1 -1
  206. package/dist/lib/write-html.js +12 -15
  207. package/dist/lib/write-html.js.map +1 -1
  208. package/dist/metadata/build-metadata.js +9 -12
  209. package/dist/metadata/build-metadata.js.map +1 -1
  210. package/dist/oauth-client.js +2 -18
  211. package/dist/oauth-client.js.map +1 -1
  212. package/dist/oauth-dpop.js +2 -18
  213. package/dist/oauth-dpop.js.map +1 -1
  214. package/dist/oauth-errors.js +24 -42
  215. package/dist/oauth-errors.js.map +1 -1
  216. package/dist/oauth-hooks.js +8 -15
  217. package/dist/oauth-hooks.js.map +1 -1
  218. package/dist/oauth-middleware.js +13 -16
  219. package/dist/oauth-middleware.js.map +1 -1
  220. package/dist/oauth-provider.js +108 -125
  221. package/dist/oauth-provider.js.map +1 -1
  222. package/dist/oauth-store.js +7 -23
  223. package/dist/oauth-store.js.map +1 -1
  224. package/dist/oauth-verifier.js +41 -53
  225. package/dist/oauth-verifier.js.map +1 -1
  226. package/dist/oidc/sub.js +2 -5
  227. package/dist/oidc/sub.js.map +1 -1
  228. package/dist/replay/replay-manager.js +6 -11
  229. package/dist/replay/replay-manager.js.map +1 -1
  230. package/dist/replay/replay-store-memory.js +5 -7
  231. package/dist/replay/replay-store-memory.js.map +1 -1
  232. package/dist/replay/replay-store-redis.js +3 -8
  233. package/dist/replay/replay-store-redis.js.map +1 -1
  234. package/dist/replay/replay-store.js +3 -8
  235. package/dist/replay/replay-store.js.map +1 -1
  236. package/dist/request/code.js +10 -15
  237. package/dist/request/code.js.map +1 -1
  238. package/dist/request/request-data.js +1 -5
  239. package/dist/request/request-data.js.map +1 -1
  240. package/dist/request/request-id.js +9 -13
  241. package/dist/request/request-id.js.map +1 -1
  242. package/dist/request/request-manager.js +61 -71
  243. package/dist/request/request-manager.js.map +1 -1
  244. package/dist/request/request-store.js +9 -27
  245. package/dist/request/request-store.js.map +1 -1
  246. package/dist/request/request-uri.js +17 -23
  247. package/dist/request/request-uri.js.map +1 -1
  248. package/dist/result/authorization-redirect-parameters.js +1 -2
  249. package/dist/result/authorization-result-authorize-page.js +1 -2
  250. package/dist/result/authorization-result-redirect.js +1 -2
  251. package/dist/router/assets/assets-manifest.d.ts.map +1 -1
  252. package/dist/router/assets/assets-manifest.js +14 -15
  253. package/dist/router/assets/assets-manifest.js.map +1 -1
  254. package/dist/router/assets/assets.d.ts.map +1 -1
  255. package/dist/router/assets/assets.js +25 -27
  256. package/dist/router/assets/assets.js.map +1 -1
  257. package/dist/router/assets/csrf.js +16 -25
  258. package/dist/router/assets/csrf.js.map +1 -1
  259. package/dist/router/assets/send-account-page.js +3 -6
  260. package/dist/router/assets/send-account-page.js.map +1 -1
  261. package/dist/router/assets/send-authorization-page.js +3 -6
  262. package/dist/router/assets/send-authorization-page.js.map +1 -1
  263. package/dist/router/assets/send-cookie-error-page.js +3 -6
  264. package/dist/router/assets/send-cookie-error-page.js.map +1 -1
  265. package/dist/router/assets/send-error-page.js +6 -9
  266. package/dist/router/assets/send-error-page.js.map +1 -1
  267. package/dist/router/assets/send-redirect.js +12 -20
  268. package/dist/router/assets/send-redirect.js.map +1 -1
  269. package/dist/router/create-account-page-middleware.js +11 -14
  270. package/dist/router/create-account-page-middleware.js.map +1 -1
  271. package/dist/router/create-api-middleware.js +83 -90
  272. package/dist/router/create-api-middleware.js.map +1 -1
  273. package/dist/router/create-authorization-page-middleware.js +43 -46
  274. package/dist/router/create-authorization-page-middleware.js.map +1 -1
  275. package/dist/router/create-oauth-middleware.js +31 -34
  276. package/dist/router/create-oauth-middleware.js.map +1 -1
  277. package/dist/router/error-handler.js +1 -2
  278. package/dist/router/middleware-options.js +1 -2
  279. package/dist/signer/access-token-payload.js +12 -15
  280. package/dist/signer/access-token-payload.js.map +1 -1
  281. package/dist/signer/api-token-payload.js +8 -11
  282. package/dist/signer/api-token-payload.js.map +1 -1
  283. package/dist/signer/signer.js +11 -17
  284. package/dist/signer/signer.js.map +1 -1
  285. package/dist/token/refresh-token.js +10 -15
  286. package/dist/token/refresh-token.js.map +1 -1
  287. package/dist/token/token-claims.js +1 -2
  288. package/dist/token/token-data.js +1 -2
  289. package/dist/token/token-id.js +10 -15
  290. package/dist/token/token-id.js.map +1 -1
  291. package/dist/token/token-manager.js +40 -51
  292. package/dist/token/token-manager.js.map +1 -1
  293. package/dist/token/token-store.js +7 -25
  294. package/dist/token/token-store.js.map +1 -1
  295. package/dist/types/authorization-response-error.js +8 -12
  296. package/dist/types/authorization-response-error.js.map +1 -1
  297. package/dist/types/color-hue.js +2 -5
  298. package/dist/types/color-hue.js.map +1 -1
  299. package/dist/types/email-otp.js +2 -5
  300. package/dist/types/email-otp.js.map +1 -1
  301. package/dist/types/email.js +6 -9
  302. package/dist/types/email.js.map +1 -1
  303. package/dist/types/handle.js +6 -9
  304. package/dist/types/handle.js.map +1 -1
  305. package/dist/types/invite-code.js +2 -5
  306. package/dist/types/invite-code.js.map +1 -1
  307. package/dist/types/par-response-error.js +5 -9
  308. package/dist/types/par-response-error.js.map +1 -1
  309. package/dist/types/password.js +3 -6
  310. package/dist/types/password.js.map +1 -1
  311. package/dist/types/rgb-color.js +7 -10
  312. package/dist/types/rgb-color.js.map +1 -1
  313. package/package.json +20 -22
  314. package/src/dpop/dpop-nonce.ts +1 -1
  315. package/src/errors/invalid-invite-code-error.ts +1 -1
  316. package/src/lib/http/accept.ts +4 -1
  317. package/src/lib/http/request.ts +4 -1
  318. package/src/lib/util/type.ts +0 -1
  319. package/src/router/assets/assets-manifest.ts +3 -1
  320. package/src/router/assets/assets.ts +2 -0
  321. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,28 +1,25 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createAuthorizationPageMiddleware = createAuthorizationPageMiddleware;
4
- const oauth_types_1 = require("@atproto/oauth-types");
5
- const authorization_error_js_1 = require("../errors/authorization-error.js");
6
- const invalid_request_error_js_1 = require("../errors/invalid-request-error.js");
7
- const index_js_1 = require("../lib/http/index.js");
8
- const error_js_1 = require("../lib/util/error.js");
9
- const write_form_redirect_js_1 = require("../lib/write-form-redirect.js");
10
- const request_uri_js_1 = require("../request/request-uri.js");
11
- const send_authorization_page_js_1 = require("./assets/send-authorization-page.js");
12
- const send_cookie_error_page_js_1 = require("./assets/send-cookie-error-page.js");
13
- const send_error_page_js_1 = require("./assets/send-error-page.js");
14
- const send_redirect_js_1 = require("./assets/send-redirect.js");
15
- const create_api_middleware_js_1 = require("./create-api-middleware.js");
16
- function createAuthorizationPageMiddleware(server, { onError }) {
1
+ import { oauthAuthorizationRequestQuerySchema, } from '@atproto/oauth-types';
2
+ import { AuthorizationError } from '../errors/authorization-error.js';
3
+ import { InvalidRequestError } from '../errors/invalid-request-error.js';
4
+ import { Router, getCookie, setCookie, validateFetchDest, validateFetchMode, validateFetchSite, validateOrigin, validateReferrer, } from '../lib/http/index.js';
5
+ import { formatError } from '../lib/util/error.js';
6
+ import { writeFormRedirect } from '../lib/write-form-redirect.js';
7
+ import { parseRequestUri, requestUriSchema } from '../request/request-uri.js';
8
+ import { sendAuthorizePageFactory } from './assets/send-authorization-page.js';
9
+ import { sendCookieErrorPageFactory } from './assets/send-cookie-error-page.js';
10
+ import { sendErrorPageFactory } from './assets/send-error-page.js';
11
+ import { sendAuthorizationResultRedirect, sendRedirect, } from './assets/send-redirect.js';
12
+ import { parseRedirectUrl } from './create-api-middleware.js';
13
+ export function createAuthorizationPageMiddleware(server, { onError }) {
17
14
  const issuerUrl = new URL(server.issuer);
18
15
  const issuerOrigin = issuerUrl.origin;
19
16
  const securityOptions = {
20
17
  hsts: issuerUrl.protocol === 'http:' ? false : undefined,
21
18
  };
22
- const sendAuthorizePage = (0, send_authorization_page_js_1.sendAuthorizePageFactory)(server.customization, securityOptions);
23
- const sendErrorPage = (0, send_error_page_js_1.sendErrorPageFactory)(server.customization, securityOptions);
24
- const sendCookieErrorPage = (0, send_cookie_error_page_js_1.sendCookieErrorPageFactory)(server.customization, securityOptions);
25
- const router = new index_js_1.Router(issuerUrl);
19
+ const sendAuthorizePage = sendAuthorizePageFactory(server.customization, securityOptions);
20
+ const sendErrorPage = sendErrorPageFactory(server.customization, securityOptions);
21
+ const sendCookieErrorPage = sendCookieErrorPageFactory(server.customization, securityOptions);
22
+ const router = new Router(issuerUrl);
26
23
  router.get('/oauth/authorize', withErrorHandler(async function (req, res) {
27
24
  res.setHeader('Cache-Control', 'no-store');
28
25
  res.setHeader('Pragma', 'no-cache');
@@ -30,10 +27,10 @@ function createAuthorizationPageMiddleware(server, { onError }) {
30
27
  // well as refreshing the authorization page).
31
28
  // @TODO Consider removing this altogether to allow hosting PDS and app on
32
29
  // the same site but different origins (different subdomains).
33
- (0, index_js_1.validateFetchSite)(req, ['same-origin', 'cross-site', 'none']);
34
- (0, index_js_1.validateFetchMode)(req, ['navigate']);
35
- (0, index_js_1.validateFetchDest)(req, ['document']);
36
- (0, index_js_1.validateOrigin)(req, issuerOrigin);
30
+ validateFetchSite(req, ['same-origin', 'cross-site', 'none']);
31
+ validateFetchMode(req, ['navigate']);
32
+ validateFetchDest(req, ['document']);
33
+ validateOrigin(req, issuerOrigin);
37
34
  // Do not perform any of the following logic if the request is invalid
38
35
  const query = parseOAuthAuthorizationRequestQuery(this.url);
39
36
  // @NOTE For some reason, even when loaded through a
@@ -48,7 +45,7 @@ function createAuthorizationPageMiddleware(server, { onError }) {
48
45
  // Only for iOS users
49
46
  req.headers['user-agent']?.includes('iPhone OS') &&
50
47
  // Disabled if the user already passed the test, which means their browser preserves cookies on redirect
51
- !((0, index_js_1.getCookie)(req, 'cookie-test') === 'succeeded') &&
48
+ !(getCookie(req, 'cookie-test') === 'succeeded') &&
52
49
  // Disabled if the user already has a session
53
50
  !(await server.deviceManager.hasSession(req))) {
54
51
  // @TODO Another possible solution would be to avoid relying on cookies if we
@@ -61,12 +58,12 @@ function createAuthorizationPageMiddleware(server, { onError }) {
61
58
  // preserves cookies by redirecting back to ourselves
62
59
  if (!this.url.searchParams.has('redirect-test')) {
63
60
  // 2) Set a testing cookie
64
- (0, index_js_1.setCookie)(res, 'cookie-test', 'testing', {
61
+ setCookie(res, 'cookie-test', 'testing', {
65
62
  sameSite: 'lax',
66
63
  httpOnly: true,
67
64
  });
68
65
  // 3) And send an auto-submit form redirecting back to ourselves
69
- return (0, write_form_redirect_js_1.writeFormRedirect)(res, 'get', this.url.href,
66
+ return writeFormRedirect(res, 'get', this.url.href,
70
67
  // 4) We add an extra query parameter to trigger the test logic after
71
68
  // the redirect occurred.
72
69
  [...this.url.searchParams, ['redirect-test', '1']], securityOptions);
@@ -74,11 +71,11 @@ function createAuthorizationPageMiddleware(server, { onError }) {
74
71
  else {
75
72
  // 5) We just got redirected back to ourselves. Verify that the
76
73
  // browser preserved cookies during the redirect
77
- if ((0, index_js_1.getCookie)(req, 'cookie-test')) {
74
+ if (getCookie(req, 'cookie-test')) {
78
75
  // 6) Success! The browser preserved cookies. Proceed with the
79
76
  // normal authorization flow.
80
77
  // 7) Set a long lasting cookie to skip the test next time
81
- (0, index_js_1.setCookie)(res, 'cookie-test', 'succeeded', {
78
+ setCookie(res, 'cookie-test', 'succeeded', {
82
79
  sameSite: 'lax',
83
80
  maxAge: 31 * 24 * 60 * 60,
84
81
  httpOnly: true,
@@ -91,7 +88,7 @@ function createAuthorizationPageMiddleware(server, { onError }) {
91
88
  // 8) Show an error page to the user explaining the situation
92
89
  // Give the browser another chance to save cookies after the use
93
90
  // pressed "Continue"
94
- (0, index_js_1.setCookie)(res, 'cookie-test', 'testing', {
91
+ setCookie(res, 'cookie-test', 'testing', {
95
92
  sameSite: 'lax',
96
93
  httpOnly: true,
97
94
  });
@@ -112,20 +109,20 @@ function createAuthorizationPageMiddleware(server, { onError }) {
112
109
  // back to the client with the error parameters.
113
110
  if ('request_uri' in query) {
114
111
  // Load and delete the authorization request
115
- const requestUri = (0, request_uri_js_1.parseRequestUri)(query.request_uri, {
112
+ const requestUri = parseRequestUri(query.request_uri, {
116
113
  path: ['query', 'request_uri'],
117
114
  });
118
115
  const data = await server.requestManager.get(requestUri, undefined, query.client_id);
119
116
  await server.requestManager.delete(requestUri);
120
- throw new authorization_error_js_1.AuthorizationError(data.parameters, message);
117
+ throw new AuthorizationError(data.parameters, message);
121
118
  }
122
119
  else if ('request' in query) {
123
120
  const client = await server.clientManager.getClient(query.client_id);
124
121
  const parameters = await server.decodeJAR(client, query);
125
- throw new authorization_error_js_1.AuthorizationError(parameters, message);
122
+ throw new AuthorizationError(parameters, message);
126
123
  }
127
124
  else {
128
- throw new authorization_error_js_1.AuthorizationError(query, message);
125
+ throw new AuthorizationError(query, message);
129
126
  }
130
127
  }
131
128
  }
@@ -135,7 +132,7 @@ function createAuthorizationPageMiddleware(server, { onError }) {
135
132
  const device = await server.deviceManager.load(req, res);
136
133
  const result = await server.authorize(query, device);
137
134
  if ('redirect' in result) {
138
- return (0, send_redirect_js_1.sendAuthorizationResultRedirect)(res, result, securityOptions);
135
+ return sendAuthorizationResultRedirect(res, result, securityOptions);
139
136
  }
140
137
  else {
141
138
  return sendAuthorizePage(req, res, result);
@@ -147,17 +144,17 @@ function createAuthorizationPageMiddleware(server, { onError }) {
147
144
  // implement it here to avoid duplicating the logic.
148
145
  router.get('/oauth/authorize/redirect', withErrorHandler(async function (req, res) {
149
146
  // Ensure we come from the authorization page
150
- (0, index_js_1.validateFetchSite)(req, ['same-origin']);
151
- (0, index_js_1.validateFetchMode)(req, ['navigate']);
152
- (0, index_js_1.validateFetchDest)(req, ['document']);
153
- (0, index_js_1.validateOrigin)(req, issuerOrigin);
154
- const referrer = (0, index_js_1.validateReferrer)(req, {
147
+ validateFetchSite(req, ['same-origin']);
148
+ validateFetchMode(req, ['navigate']);
149
+ validateFetchDest(req, ['document']);
150
+ validateOrigin(req, issuerOrigin);
151
+ const referrer = validateReferrer(req, {
155
152
  origin: issuerOrigin,
156
153
  pathname: '/oauth/authorize',
157
154
  });
158
155
  // Ensure we are coming from the authorization page
159
- request_uri_js_1.requestUriSchema.parse(referrer.searchParams.get('request_uri'));
160
- return (0, send_redirect_js_1.sendRedirect)(res, (0, create_api_middleware_js_1.parseRedirectUrl)(this.url), securityOptions);
156
+ requestUriSchema.parse(referrer.searchParams.get('request_uri'));
157
+ return sendRedirect(res, parseRedirectUrl(this.url), securityOptions);
161
158
  }));
162
159
  return router.buildMiddleware();
163
160
  function withErrorHandler(handler) {
@@ -168,8 +165,8 @@ function createAuthorizationPageMiddleware(server, { onError }) {
168
165
  catch (err) {
169
166
  onError?.(req, res, err, `Authorization Request Error`);
170
167
  if (!res.headersSent) {
171
- if (err instanceof authorization_error_js_1.AuthorizationError) {
172
- return (0, send_redirect_js_1.sendAuthorizationResultRedirect)(res, {
168
+ if (err instanceof AuthorizationError) {
169
+ return sendAuthorizationResultRedirect(res, {
173
170
  issuer: server.issuer,
174
171
  parameters: err.parameters,
175
172
  redirect: err.toJSON(),
@@ -188,13 +185,13 @@ function createAuthorizationPageMiddleware(server, { onError }) {
188
185
  }
189
186
  function parseOAuthAuthorizationRequestQuery(url) {
190
187
  const query = Object.fromEntries(url.searchParams);
191
- const result = oauth_types_1.oauthAuthorizationRequestQuerySchema.safeParse(query, {
188
+ const result = oauthAuthorizationRequestQuerySchema.safeParse(query, {
192
189
  path: ['query'],
193
190
  });
194
191
  if (!result.success) {
195
192
  const message = 'Invalid request parameters';
196
193
  const err = result.error;
197
- throw new invalid_request_error_js_1.InvalidRequestError((0, error_js_1.formatError)(err, message), err);
194
+ throw new InvalidRequestError(formatError(err, message), err);
198
195
  }
199
196
  return result.data;
200
197
  }
@@ -1 +1 @@
1
- {"version":3,"file":"create-authorization-page-middleware.js","sourceRoot":"","sources":["../../src/router/create-authorization-page-middleware.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport {\n OAuthAuthorizationRequestQuery,\n oauthAuthorizationRequestQuerySchema,\n} from '@atproto/oauth-types'\nimport { AuthorizationError } from '../errors/authorization-error.js'\nimport { InvalidRequestError } from '../errors/invalid-request-error.js'\nimport {\n Middleware,\n Router,\n RouterCtx,\n getCookie,\n setCookie,\n validateFetchDest,\n validateFetchMode,\n validateFetchSite,\n validateOrigin,\n validateReferrer,\n} from '../lib/http/index.js'\nimport { SecurityHeadersOptions } from '../lib/http/security-headers.js'\nimport { formatError } from '../lib/util/error.js'\nimport type { Awaitable } from '../lib/util/type.js'\nimport { writeFormRedirect } from '../lib/write-form-redirect.js'\nimport type { OAuthProvider } from '../oauth-provider.js'\nimport { parseRequestUri, requestUriSchema } from '../request/request-uri.js'\nimport { sendAuthorizePageFactory } from './assets/send-authorization-page.js'\nimport { sendCookieErrorPageFactory } from './assets/send-cookie-error-page.js'\nimport { sendErrorPageFactory } from './assets/send-error-page.js'\nimport {\n sendAuthorizationResultRedirect,\n sendRedirect,\n} from './assets/send-redirect.js'\nimport { parseRedirectUrl } from './create-api-middleware.js'\nimport type { MiddlewareOptions } from './middleware-options.js'\n\nexport function createAuthorizationPageMiddleware<\n Ctx extends object | void = void,\n Req extends IncomingMessage = IncomingMessage,\n Res extends ServerResponse = ServerResponse,\n>(\n server: OAuthProvider,\n { onError }: MiddlewareOptions<Req, Res>,\n): Middleware<Ctx, Req, Res> {\n const issuerUrl = new URL(server.issuer)\n const issuerOrigin = issuerUrl.origin\n\n const securityOptions: SecurityHeadersOptions = {\n hsts: issuerUrl.protocol === 'http:' ? false : undefined,\n }\n\n const sendAuthorizePage = sendAuthorizePageFactory(\n server.customization,\n securityOptions,\n )\n const sendErrorPage = sendErrorPageFactory(\n server.customization,\n securityOptions,\n )\n const sendCookieErrorPage = sendCookieErrorPageFactory(\n server.customization,\n securityOptions,\n )\n\n const router = new Router<Ctx, Req, Res>(issuerUrl)\n\n router.get(\n '/oauth/authorize',\n withErrorHandler(async function (req, res) {\n res.setHeader('Cache-Control', 'no-store')\n res.setHeader('Pragma', 'no-cache')\n\n // \"same-origin\" is required to support the redirect test logic below (as\n // well as refreshing the authorization page).\n\n // @TODO Consider removing this altogether to allow hosting PDS and app on\n // the same site but different origins (different subdomains).\n validateFetchSite(req, ['same-origin', 'cross-site', 'none'])\n validateFetchMode(req, ['navigate'])\n validateFetchDest(req, ['document'])\n validateOrigin(req, issuerOrigin)\n\n // Do not perform any of the following logic if the request is invalid\n const query = parseOAuthAuthorizationRequestQuery(this.url)\n\n // @NOTE For some reason, even when loaded through a\n // ASWebAuthenticationSession, iOS will sometimes fail to properly save\n // cookies set during the rendering of the page. When this happens, the\n // authorization page logic, which relies on cookies to maintain the session,\n // will fail. To work around this, we perform an initial redirect to ourselves\n // using a form GET submit, in an attempt to verify if the browser saves\n // cookies on redirect or not. If it does, we proceed as normal. If it\n // doesn't, we redirect the user back to the client with an error message.\n if (\n // Only for iOS users\n req.headers['user-agent']?.includes('iPhone OS') &&\n // Disabled if the user already passed the test, which means their browser preserves cookies on redirect\n !(getCookie(req, 'cookie-test') === 'succeeded') &&\n // Disabled if the user already has a session\n !(await server.deviceManager.hasSession(req))\n ) {\n // @TODO Another possible solution would be to avoid relying on cookies if we\n // detect that they are not being preserved. This would mean that preserving\n // sessions (SSO) would not be possible for browsers that don't preserve\n // cookies on redirect, but at least the authorization request could still be\n // completed. This was not implemented yet due to the extra complexity\n // involved in supporting this.\n\n // 1) When the user first comes here, we will test if their browser\n // preserves cookies by redirecting back to ourselves\n if (!this.url.searchParams.has('redirect-test')) {\n // 2) Set a testing cookie\n setCookie(res, 'cookie-test', 'testing', {\n sameSite: 'lax',\n httpOnly: true,\n })\n\n // 3) And send an auto-submit form redirecting back to ourselves\n return writeFormRedirect(\n res,\n 'get',\n this.url.href,\n // 4) We add an extra query parameter to trigger the test logic after\n // the redirect occurred.\n [...this.url.searchParams, ['redirect-test', '1']],\n securityOptions,\n )\n } else {\n // 5) We just got redirected back to ourselves. Verify that the\n // browser preserved cookies during the redirect\n if (getCookie(req, 'cookie-test')) {\n // 6) Success! The browser preserved cookies. Proceed with the\n // normal authorization flow.\n\n // 7) Set a long lasting cookie to skip the test next time\n setCookie(res, 'cookie-test', 'succeeded', {\n sameSite: 'lax',\n maxAge: 31 * 24 * 60 * 60,\n httpOnly: true,\n })\n } else {\n // The browser did NOT preserve cookies. We have to abort the\n // authorization request.\n\n if (this.url.searchParams.get('redirect-test') === '1') {\n // 8) Show an error page to the user explaining the situation\n\n // Give the browser another chance to save cookies after the use\n // pressed \"Continue\"\n setCookie(res, 'cookie-test', 'testing', {\n sameSite: 'lax',\n httpOnly: true,\n })\n\n // Make sure next time we reach the other branch and redirect back\n // to the client\n const continueUrl = new URL(this.url.href)\n continueUrl.searchParams.set('redirect-test', '2')\n return sendCookieErrorPage(req, res, { continueUrl })\n } else {\n // 9) Once the use acknowledges the error, redirect them back to\n // the client with an error message.\n\n // Allow the client to understand what happened (the `error`\n // response parameter value is constrained by the OAuth2 spec)\n const message = 'ERR_COOKIES_UNSUPPORTED'\n\n // @NOTE AuthorizationError thrown here will be caught by the\n // error handler middleware defined below, and cause a redirect\n // back to the client with the error parameters.\n if ('request_uri' in query) {\n // Load and delete the authorization request\n const requestUri = parseRequestUri(query.request_uri, {\n path: ['query', 'request_uri'],\n })\n const data = await server.requestManager.get(\n requestUri,\n undefined,\n query.client_id,\n )\n await server.requestManager.delete(requestUri)\n throw new AuthorizationError(data.parameters, message)\n } else if ('request' in query) {\n const client = await server.clientManager.getClient(\n query.client_id,\n )\n const parameters = await server.decodeJAR(client, query)\n throw new AuthorizationError(parameters, message)\n } else {\n throw new AuthorizationError(query, message)\n }\n }\n }\n }\n }\n\n // Normal authorization flow\n const device = await server.deviceManager.load(req, res)\n\n const result = await server.authorize(query, device)\n\n if ('redirect' in result) {\n return sendAuthorizationResultRedirect(res, result, securityOptions)\n } else {\n return sendAuthorizePage(req, res, result)\n }\n }),\n )\n\n // This is a private endpoint that will be called by the user after the\n // authorization request was either approved or denied. The logic performed\n // here **could** be performed directly in the frontend. We decided to\n // implement it here to avoid duplicating the logic.\n router.get(\n '/oauth/authorize/redirect',\n withErrorHandler(async function (req, res) {\n // Ensure we come from the authorization page\n validateFetchSite(req, ['same-origin'])\n validateFetchMode(req, ['navigate'])\n validateFetchDest(req, ['document'])\n validateOrigin(req, issuerOrigin)\n\n const referrer = validateReferrer(req, {\n origin: issuerOrigin,\n pathname: '/oauth/authorize',\n })\n\n // Ensure we are coming from the authorization page\n requestUriSchema.parse(referrer.searchParams.get('request_uri'))\n\n return sendRedirect(res, parseRedirectUrl(this.url), securityOptions)\n }),\n )\n\n return router.buildMiddleware()\n\n function withErrorHandler<T extends RouterCtx>(\n handler: (this: T, req: Req, res: Res) => Awaitable<void>,\n ): Middleware<T, Req, Res> {\n return async function (req, res) {\n try {\n await handler.call(this, req, res)\n } catch (err) {\n onError?.(req, res, err, `Authorization Request Error`)\n\n if (!res.headersSent) {\n if (err instanceof AuthorizationError) {\n return sendAuthorizationResultRedirect(\n res,\n {\n issuer: server.issuer,\n parameters: err.parameters,\n redirect: err.toJSON(),\n },\n securityOptions,\n )\n } else {\n return sendErrorPage(req, res, err)\n }\n } else if (!res.destroyed) {\n res.end()\n }\n }\n }\n }\n}\n\nfunction parseOAuthAuthorizationRequestQuery(\n url: URL,\n): OAuthAuthorizationRequestQuery {\n const query = Object.fromEntries(url.searchParams)\n const result = oauthAuthorizationRequestQuerySchema.safeParse(query, {\n path: ['query'],\n })\n\n if (!result.success) {\n const message = 'Invalid request parameters'\n const err = result.error\n throw new InvalidRequestError(formatError(err, message), err)\n }\n\n return result.data\n}\n"]}
1
+ {"version":3,"file":"create-authorization-page-middleware.js","sourceRoot":"","sources":["../../src/router/create-authorization-page-middleware.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport {\n OAuthAuthorizationRequestQuery,\n oauthAuthorizationRequestQuerySchema,\n} from '@atproto/oauth-types'\nimport { AuthorizationError } from '../errors/authorization-error.js'\nimport { InvalidRequestError } from '../errors/invalid-request-error.js'\nimport {\n Middleware,\n Router,\n RouterCtx,\n getCookie,\n setCookie,\n validateFetchDest,\n validateFetchMode,\n validateFetchSite,\n validateOrigin,\n validateReferrer,\n} from '../lib/http/index.js'\nimport { SecurityHeadersOptions } from '../lib/http/security-headers.js'\nimport { formatError } from '../lib/util/error.js'\nimport type { Awaitable } from '../lib/util/type.js'\nimport { writeFormRedirect } from '../lib/write-form-redirect.js'\nimport type { OAuthProvider } from '../oauth-provider.js'\nimport { parseRequestUri, requestUriSchema } from '../request/request-uri.js'\nimport { sendAuthorizePageFactory } from './assets/send-authorization-page.js'\nimport { sendCookieErrorPageFactory } from './assets/send-cookie-error-page.js'\nimport { sendErrorPageFactory } from './assets/send-error-page.js'\nimport {\n sendAuthorizationResultRedirect,\n sendRedirect,\n} from './assets/send-redirect.js'\nimport { parseRedirectUrl } from './create-api-middleware.js'\nimport type { MiddlewareOptions } from './middleware-options.js'\n\nexport function createAuthorizationPageMiddleware<\n Ctx extends object | void = void,\n Req extends IncomingMessage = IncomingMessage,\n Res extends ServerResponse = ServerResponse,\n>(\n server: OAuthProvider,\n { onError }: MiddlewareOptions<Req, Res>,\n): Middleware<Ctx, Req, Res> {\n const issuerUrl = new URL(server.issuer)\n const issuerOrigin = issuerUrl.origin\n\n const securityOptions: SecurityHeadersOptions = {\n hsts: issuerUrl.protocol === 'http:' ? false : undefined,\n }\n\n const sendAuthorizePage = sendAuthorizePageFactory(\n server.customization,\n securityOptions,\n )\n const sendErrorPage = sendErrorPageFactory(\n server.customization,\n securityOptions,\n )\n const sendCookieErrorPage = sendCookieErrorPageFactory(\n server.customization,\n securityOptions,\n )\n\n const router = new Router<Ctx, Req, Res>(issuerUrl)\n\n router.get(\n '/oauth/authorize',\n withErrorHandler(async function (req, res) {\n res.setHeader('Cache-Control', 'no-store')\n res.setHeader('Pragma', 'no-cache')\n\n // \"same-origin\" is required to support the redirect test logic below (as\n // well as refreshing the authorization page).\n\n // @TODO Consider removing this altogether to allow hosting PDS and app on\n // the same site but different origins (different subdomains).\n validateFetchSite(req, ['same-origin', 'cross-site', 'none'])\n validateFetchMode(req, ['navigate'])\n validateFetchDest(req, ['document'])\n validateOrigin(req, issuerOrigin)\n\n // Do not perform any of the following logic if the request is invalid\n const query = parseOAuthAuthorizationRequestQuery(this.url)\n\n // @NOTE For some reason, even when loaded through a\n // ASWebAuthenticationSession, iOS will sometimes fail to properly save\n // cookies set during the rendering of the page. When this happens, the\n // authorization page logic, which relies on cookies to maintain the session,\n // will fail. To work around this, we perform an initial redirect to ourselves\n // using a form GET submit, in an attempt to verify if the browser saves\n // cookies on redirect or not. If it does, we proceed as normal. If it\n // doesn't, we redirect the user back to the client with an error message.\n if (\n // Only for iOS users\n req.headers['user-agent']?.includes('iPhone OS') &&\n // Disabled if the user already passed the test, which means their browser preserves cookies on redirect\n !(getCookie(req, 'cookie-test') === 'succeeded') &&\n // Disabled if the user already has a session\n !(await server.deviceManager.hasSession(req))\n ) {\n // @TODO Another possible solution would be to avoid relying on cookies if we\n // detect that they are not being preserved. This would mean that preserving\n // sessions (SSO) would not be possible for browsers that don't preserve\n // cookies on redirect, but at least the authorization request could still be\n // completed. This was not implemented yet due to the extra complexity\n // involved in supporting this.\n\n // 1) When the user first comes here, we will test if their browser\n // preserves cookies by redirecting back to ourselves\n if (!this.url.searchParams.has('redirect-test')) {\n // 2) Set a testing cookie\n setCookie(res, 'cookie-test', 'testing', {\n sameSite: 'lax',\n httpOnly: true,\n })\n\n // 3) And send an auto-submit form redirecting back to ourselves\n return writeFormRedirect(\n res,\n 'get',\n this.url.href,\n // 4) We add an extra query parameter to trigger the test logic after\n // the redirect occurred.\n [...this.url.searchParams, ['redirect-test', '1']],\n securityOptions,\n )\n } else {\n // 5) We just got redirected back to ourselves. Verify that the\n // browser preserved cookies during the redirect\n if (getCookie(req, 'cookie-test')) {\n // 6) Success! The browser preserved cookies. Proceed with the\n // normal authorization flow.\n\n // 7) Set a long lasting cookie to skip the test next time\n setCookie(res, 'cookie-test', 'succeeded', {\n sameSite: 'lax',\n maxAge: 31 * 24 * 60 * 60,\n httpOnly: true,\n })\n } else {\n // The browser did NOT preserve cookies. We have to abort the\n // authorization request.\n\n if (this.url.searchParams.get('redirect-test') === '1') {\n // 8) Show an error page to the user explaining the situation\n\n // Give the browser another chance to save cookies after the use\n // pressed \"Continue\"\n setCookie(res, 'cookie-test', 'testing', {\n sameSite: 'lax',\n httpOnly: true,\n })\n\n // Make sure next time we reach the other branch and redirect back\n // to the client\n const continueUrl = new URL(this.url.href)\n continueUrl.searchParams.set('redirect-test', '2')\n return sendCookieErrorPage(req, res, { continueUrl })\n } else {\n // 9) Once the use acknowledges the error, redirect them back to\n // the client with an error message.\n\n // Allow the client to understand what happened (the `error`\n // response parameter value is constrained by the OAuth2 spec)\n const message = 'ERR_COOKIES_UNSUPPORTED'\n\n // @NOTE AuthorizationError thrown here will be caught by the\n // error handler middleware defined below, and cause a redirect\n // back to the client with the error parameters.\n if ('request_uri' in query) {\n // Load and delete the authorization request\n const requestUri = parseRequestUri(query.request_uri, {\n path: ['query', 'request_uri'],\n })\n const data = await server.requestManager.get(\n requestUri,\n undefined,\n query.client_id,\n )\n await server.requestManager.delete(requestUri)\n throw new AuthorizationError(data.parameters, message)\n } else if ('request' in query) {\n const client = await server.clientManager.getClient(\n query.client_id,\n )\n const parameters = await server.decodeJAR(client, query)\n throw new AuthorizationError(parameters, message)\n } else {\n throw new AuthorizationError(query, message)\n }\n }\n }\n }\n }\n\n // Normal authorization flow\n const device = await server.deviceManager.load(req, res)\n\n const result = await server.authorize(query, device)\n\n if ('redirect' in result) {\n return sendAuthorizationResultRedirect(res, result, securityOptions)\n } else {\n return sendAuthorizePage(req, res, result)\n }\n }),\n )\n\n // This is a private endpoint that will be called by the user after the\n // authorization request was either approved or denied. The logic performed\n // here **could** be performed directly in the frontend. We decided to\n // implement it here to avoid duplicating the logic.\n router.get(\n '/oauth/authorize/redirect',\n withErrorHandler(async function (req, res) {\n // Ensure we come from the authorization page\n validateFetchSite(req, ['same-origin'])\n validateFetchMode(req, ['navigate'])\n validateFetchDest(req, ['document'])\n validateOrigin(req, issuerOrigin)\n\n const referrer = validateReferrer(req, {\n origin: issuerOrigin,\n pathname: '/oauth/authorize',\n })\n\n // Ensure we are coming from the authorization page\n requestUriSchema.parse(referrer.searchParams.get('request_uri'))\n\n return sendRedirect(res, parseRedirectUrl(this.url), securityOptions)\n }),\n )\n\n return router.buildMiddleware()\n\n function withErrorHandler<T extends RouterCtx>(\n handler: (this: T, req: Req, res: Res) => Awaitable<void>,\n ): Middleware<T, Req, Res> {\n return async function (req, res) {\n try {\n await handler.call(this, req, res)\n } catch (err) {\n onError?.(req, res, err, `Authorization Request Error`)\n\n if (!res.headersSent) {\n if (err instanceof AuthorizationError) {\n return sendAuthorizationResultRedirect(\n res,\n {\n issuer: server.issuer,\n parameters: err.parameters,\n redirect: err.toJSON(),\n },\n securityOptions,\n )\n } else {\n return sendErrorPage(req, res, err)\n }\n } else if (!res.destroyed) {\n res.end()\n }\n }\n }\n }\n}\n\nfunction parseOAuthAuthorizationRequestQuery(\n url: URL,\n): OAuthAuthorizationRequestQuery {\n const query = Object.fromEntries(url.searchParams)\n const result = oauthAuthorizationRequestQuerySchema.safeParse(query, {\n path: ['query'],\n })\n\n if (!result.success) {\n const message = 'Invalid request parameters'\n const err = result.error\n throw new InvalidRequestError(formatError(err, message), err)\n }\n\n return result.data\n}\n"]}
@@ -1,15 +1,12 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createOAuthMiddleware = createOAuthMiddleware;
4
- const oauth_types_1 = require("@atproto/oauth-types");
5
- const error_parser_js_1 = require("../errors/error-parser.js");
6
- const invalid_client_error_js_1 = require("../errors/invalid-client-error.js");
7
- const invalid_grant_error_js_1 = require("../errors/invalid-grant-error.js");
8
- const invalid_request_error_js_1 = require("../errors/invalid-request-error.js");
9
- const www_authenticate_error_js_1 = require("../errors/www-authenticate-error.js");
10
- const index_js_1 = require("../lib/http/index.js");
11
- const error_js_1 = require("../lib/util/error.js");
12
- const oauth_errors_js_1 = require("../oauth-errors.js");
1
+ import { oauthAuthorizationRequestParSchema, oauthClientCredentialsSchema, oauthTokenIdentificationSchema, oauthTokenRequestSchema, } from '@atproto/oauth-types';
2
+ import { buildErrorPayload, buildErrorStatus } from '../errors/error-parser.js';
3
+ import { InvalidClientError } from '../errors/invalid-client-error.js';
4
+ import { InvalidGrantError } from '../errors/invalid-grant-error.js';
5
+ import { InvalidRequestError } from '../errors/invalid-request-error.js';
6
+ import { WWWAuthenticateError } from '../errors/www-authenticate-error.js';
7
+ import { Router, cacheControlMiddleware, combineMiddlewares, jsonHandler, parseHttpRequest, staticJsonMiddleware, } from '../lib/http/index.js';
8
+ import { formatError } from '../lib/util/error.js';
9
+ import { OAuthError } from '../oauth-errors.js';
13
10
  // CORS preflight
14
11
  const corsHeaders = function (req, res, next) {
15
12
  res.setHeader('Access-Control-Max-Age', '86400'); // 1 day
@@ -33,28 +30,28 @@ const corsHeaders = function (req, res, next) {
33
30
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type,DPoP');
34
31
  next();
35
32
  };
36
- const corsPreflight = (0, index_js_1.combineMiddlewares)([
33
+ const corsPreflight = combineMiddlewares([
37
34
  corsHeaders,
38
35
  (req, res) => {
39
36
  res.writeHead(200).end();
40
37
  },
41
38
  ]);
42
- function createOAuthMiddleware(server, { onError }) {
43
- const router = new index_js_1.Router(new URL(server.issuer));
39
+ export function createOAuthMiddleware(server, { onError }) {
40
+ const router = new Router(new URL(server.issuer));
44
41
  //- Public OAuth endpoints
45
42
  router.options('/.well-known/oauth-authorization-server', corsPreflight);
46
- router.get('/.well-known/oauth-authorization-server', corsHeaders, (0, index_js_1.cacheControlMiddleware)(300), (0, index_js_1.staticJsonMiddleware)(server.metadata));
43
+ router.get('/.well-known/oauth-authorization-server', corsHeaders, cacheControlMiddleware(300), staticJsonMiddleware(server.metadata));
47
44
  router.options('/oauth/jwks', corsPreflight);
48
- router.get('/oauth/jwks', corsHeaders, (0, index_js_1.cacheControlMiddleware)(300), (0, index_js_1.staticJsonMiddleware)(server.jwks));
45
+ router.get('/oauth/jwks', corsHeaders, cacheControlMiddleware(300), staticJsonMiddleware(server.jwks));
49
46
  router.options('/oauth/par', corsPreflight);
50
47
  router.post('/oauth/par', corsHeaders, oauthHandler(async function (req) {
51
- const payload = await (0, index_js_1.parseHttpRequest)(req, ['json', 'urlencoded']);
48
+ const payload = await parseHttpRequest(req, ['json', 'urlencoded']);
52
49
  // https://datatracker.ietf.org/doc/html/rfc9126#name-error-response
53
50
  // https://datatracker.ietf.org/doc/html/rfc6749#autoid-56
54
- const credentials = await oauth_types_1.oauthClientCredentialsSchema
51
+ const credentials = await oauthClientCredentialsSchema
55
52
  .parseAsync(payload, { path: ['body'] })
56
53
  .catch((err) => throwInvalidClient(err, 'Client credentials missing'));
57
- const authorizationRequest = await oauth_types_1.oauthAuthorizationRequestParSchema
54
+ const authorizationRequest = await oauthAuthorizationRequestParSchema
58
55
  .parseAsync(payload, { path: ['body'] })
59
56
  .catch((err) => throwInvalidRequest(err, 'Invalid authorization request'));
60
57
  const dpopProof = await server.checkDpopProof(req.method, this.url, req.headers);
@@ -68,12 +65,12 @@ function createOAuthMiddleware(server, { onError }) {
68
65
  });
69
66
  router.options('/oauth/token', corsPreflight);
70
67
  router.post('/oauth/token', corsHeaders, oauthHandler(async function (req) {
71
- const payload = await (0, index_js_1.parseHttpRequest)(req, ['json', 'urlencoded']);
68
+ const payload = await parseHttpRequest(req, ['json', 'urlencoded']);
72
69
  const clientMetadata = await server.deviceManager.getRequestMetadata(req);
73
- const clientCredentials = await oauth_types_1.oauthClientCredentialsSchema
70
+ const clientCredentials = await oauthClientCredentialsSchema
74
71
  .parseAsync(payload, { path: ['body'] })
75
72
  .catch((err) => throwInvalidGrant(err, 'Client credentials missing'));
76
- const tokenRequest = await oauth_types_1.oauthTokenRequestSchema
73
+ const tokenRequest = await oauthTokenRequestSchema
77
74
  .parseAsync(payload, { path: ['body'] })
78
75
  .catch((err) => throwInvalidGrant(err, 'Invalid request payload'));
79
76
  const dpopProof = await server.checkDpopProof(req.method, this.url, req.headers);
@@ -81,11 +78,11 @@ function createOAuthMiddleware(server, { onError }) {
81
78
  }));
82
79
  router.options('/oauth/revoke', corsPreflight);
83
80
  router.post('/oauth/revoke', corsHeaders, oauthHandler(async function (req, res) {
84
- const payload = await (0, index_js_1.parseHttpRequest)(req, ['json', 'urlencoded']);
85
- const credentials = await oauth_types_1.oauthClientCredentialsSchema
81
+ const payload = await parseHttpRequest(req, ['json', 'urlencoded']);
82
+ const credentials = await oauthClientCredentialsSchema
86
83
  .parseAsync(payload, { path: ['body'] })
87
84
  .catch((err) => throwInvalidRequest(err, 'Client credentials missing'));
88
- const tokenIdentification = await oauth_types_1.oauthTokenIdentificationSchema
85
+ const tokenIdentification = await oauthTokenIdentificationSchema
89
86
  .parseAsync(payload, { path: ['body'] })
90
87
  .catch((err) => throwInvalidRequest(err, 'Invalid request payload'));
91
88
  const dpopProof = await server.checkDpopProof(req.method, this.url, req.headers);
@@ -105,7 +102,7 @@ function createOAuthMiddleware(server, { onError }) {
105
102
  }));
106
103
  return router.buildMiddleware();
107
104
  function oauthHandler(buildOAuthResponse, status) {
108
- return (0, index_js_1.jsonHandler)(async function (req, res) {
105
+ return jsonHandler(async function (req, res) {
109
106
  try {
110
107
  // https://www.rfc-editor.org/rfc/rfc6749.html#section-5.1
111
108
  res.setHeader('Cache-Control', 'no-store');
@@ -121,28 +118,28 @@ function createOAuthMiddleware(server, { onError }) {
121
118
  return { json, status };
122
119
  }
123
120
  catch (err) {
124
- onError?.(req, res, err, err instanceof oauth_errors_js_1.OAuthError
121
+ onError?.(req, res, err, err instanceof OAuthError
125
122
  ? `OAuth "${err.error}" error`
126
123
  : 'Unexpected error');
127
- if (!res.headersSent && err instanceof www_authenticate_error_js_1.WWWAuthenticateError) {
124
+ if (!res.headersSent && err instanceof WWWAuthenticateError) {
128
125
  const name = 'WWW-Authenticate';
129
126
  res.setHeader(name, err.wwwAuthenticateHeader);
130
127
  res.appendHeader('Access-Control-Expose-Headers', name);
131
128
  }
132
- const status = (0, error_parser_js_1.buildErrorStatus)(err);
133
- const json = (0, error_parser_js_1.buildErrorPayload)(err);
129
+ const status = buildErrorStatus(err);
130
+ const json = buildErrorPayload(err);
134
131
  return { json, status };
135
132
  }
136
133
  });
137
134
  }
138
135
  }
139
136
  function throwInvalidGrant(err, prefix) {
140
- throw new invalid_grant_error_js_1.InvalidGrantError((0, error_js_1.formatError)(err, prefix), err);
137
+ throw new InvalidGrantError(formatError(err, prefix), err);
141
138
  }
142
139
  function throwInvalidClient(err, prefix) {
143
- throw new invalid_client_error_js_1.InvalidClientError((0, error_js_1.formatError)(err, prefix), err);
140
+ throw new InvalidClientError(formatError(err, prefix), err);
144
141
  }
145
142
  function throwInvalidRequest(err, prefix) {
146
- throw new invalid_request_error_js_1.InvalidRequestError((0, error_js_1.formatError)(err, prefix), err);
143
+ throw new InvalidRequestError(formatError(err, prefix), err);
147
144
  }
148
145
  //# sourceMappingURL=create-oauth-middleware.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-oauth-middleware.js","sourceRoot":"","sources":["../../src/router/create-oauth-middleware.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport {\n oauthAuthorizationRequestParSchema,\n oauthClientCredentialsSchema,\n oauthTokenIdentificationSchema,\n oauthTokenRequestSchema,\n} from '@atproto/oauth-types'\nimport { buildErrorPayload, buildErrorStatus } from '../errors/error-parser.js'\nimport { InvalidClientError } from '../errors/invalid-client-error.js'\nimport { InvalidGrantError } from '../errors/invalid-grant-error.js'\nimport { InvalidRequestError } from '../errors/invalid-request-error.js'\nimport { WWWAuthenticateError } from '../errors/www-authenticate-error.js'\nimport {\n Middleware,\n Router,\n cacheControlMiddleware,\n combineMiddlewares,\n jsonHandler,\n parseHttpRequest,\n staticJsonMiddleware,\n} from '../lib/http/index.js'\nimport { formatError } from '../lib/util/error.js'\nimport { OAuthError } from '../oauth-errors.js'\nimport type { OAuthProvider } from '../oauth-provider.js'\nimport type { MiddlewareOptions } from './middleware-options.js'\n\n// CORS preflight\nconst corsHeaders: Middleware = function (req, res, next) {\n res.setHeader('Access-Control-Max-Age', '86400') // 1 day\n\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin\n //\n // > For requests without credentials, the literal value \"*\" can be\n // > specified as a wildcard; the value tells browsers to allow\n // > requesting code from any origin to access the resource.\n // > Attempting to use the wildcard with credentials results in an\n // > error.\n //\n // A \"*\" is safer to use than reflecting the request origin.\n res.setHeader('Access-Control-Allow-Origin', '*')\n\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods\n // > The value \"*\" only counts as a special wildcard value for\n // > requests without credentials (requests without HTTP cookies or\n // > HTTP authentication information). In requests with credentials,\n // > it is treated as the literal method name \"*\" without special\n // > semantics.\n res.setHeader('Access-Control-Allow-Methods', '*')\n\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type,DPoP')\n\n next()\n}\n\nconst corsPreflight: Middleware = combineMiddlewares([\n corsHeaders,\n (req, res) => {\n res.writeHead(200).end()\n },\n])\n\nexport function createOAuthMiddleware<\n Ctx extends object | void = void,\n Req extends IncomingMessage = IncomingMessage,\n Res extends ServerResponse = ServerResponse,\n>(\n server: OAuthProvider,\n { onError }: MiddlewareOptions<Req, Res>,\n): Middleware<Ctx, Req, Res> {\n const router = new Router<Ctx, Req, Res>(new URL(server.issuer))\n\n //- Public OAuth endpoints\n\n router.options('/.well-known/oauth-authorization-server', corsPreflight)\n router.get(\n '/.well-known/oauth-authorization-server',\n corsHeaders,\n cacheControlMiddleware(300),\n staticJsonMiddleware(server.metadata),\n )\n\n router.options('/oauth/jwks', corsPreflight)\n router.get(\n '/oauth/jwks',\n corsHeaders,\n cacheControlMiddleware(300),\n staticJsonMiddleware(server.jwks),\n )\n\n router.options('/oauth/par', corsPreflight)\n router.post(\n '/oauth/par',\n corsHeaders,\n oauthHandler(async function (req) {\n const payload = await parseHttpRequest(req, ['json', 'urlencoded'])\n\n // https://datatracker.ietf.org/doc/html/rfc9126#name-error-response\n // https://datatracker.ietf.org/doc/html/rfc6749#autoid-56\n\n const credentials = await oauthClientCredentialsSchema\n .parseAsync(payload, { path: ['body'] })\n .catch((err) => throwInvalidClient(err, 'Client credentials missing'))\n\n const authorizationRequest = await oauthAuthorizationRequestParSchema\n .parseAsync(payload, { path: ['body'] })\n .catch((err) =>\n throwInvalidRequest(err, 'Invalid authorization request'),\n )\n\n const dpopProof = await server.checkDpopProof(\n req.method!,\n this.url,\n req.headers,\n )\n\n return server.pushedAuthorizationRequest(\n credentials,\n authorizationRequest,\n dpopProof,\n )\n }, 201),\n )\n // https://datatracker.ietf.org/doc/html/rfc9126#section-2.3\n // > If the request did not use the POST method, the authorization server\n // > responds with an HTTP 405 (Method Not Allowed) status code.\n router.all('/oauth/par', (req, res) => {\n res.writeHead(405).end()\n })\n\n router.options('/oauth/token', corsPreflight)\n router.post(\n '/oauth/token',\n corsHeaders,\n oauthHandler(async function (req) {\n const payload = await parseHttpRequest(req, ['json', 'urlencoded'])\n\n const clientMetadata = await server.deviceManager.getRequestMetadata(req)\n\n const clientCredentials = await oauthClientCredentialsSchema\n .parseAsync(payload, { path: ['body'] })\n .catch((err) => throwInvalidGrant(err, 'Client credentials missing'))\n\n const tokenRequest = await oauthTokenRequestSchema\n .parseAsync(payload, { path: ['body'] })\n .catch((err) => throwInvalidGrant(err, 'Invalid request payload'))\n\n const dpopProof = await server.checkDpopProof(\n req.method!,\n this.url,\n req.headers,\n )\n\n return server.token(\n clientCredentials,\n clientMetadata,\n tokenRequest,\n dpopProof,\n )\n }),\n )\n\n router.options('/oauth/revoke', corsPreflight)\n router.post(\n '/oauth/revoke',\n corsHeaders,\n oauthHandler(async function (req, res) {\n const payload = await parseHttpRequest(req, ['json', 'urlencoded'])\n\n const credentials = await oauthClientCredentialsSchema\n .parseAsync(payload, { path: ['body'] })\n .catch((err) => throwInvalidRequest(err, 'Client credentials missing'))\n\n const tokenIdentification = await oauthTokenIdentificationSchema\n .parseAsync(payload, { path: ['body'] })\n .catch((err) => throwInvalidRequest(err, 'Invalid request payload'))\n\n const dpopProof = await server.checkDpopProof(\n req.method!,\n this.url,\n req.headers,\n )\n\n try {\n await server.revoke(credentials, tokenIdentification, dpopProof)\n } catch (err) {\n // > Note: invalid tokens do not cause an error response since the\n // > client cannot handle such an error in a reasonable way. Moreover,\n // > the purpose of the revocation request, invalidating the particular\n // > token, is already achieved.\n //\n // https://datatracker.ietf.org/doc/html/rfc7009#section-2.2\n\n onError?.(req, res, err, 'Failed to revoke token')\n }\n\n return {}\n }),\n )\n\n return router.buildMiddleware()\n\n function oauthHandler<T>(\n buildOAuthResponse: (this: T, req: Req, res: Res) => unknown,\n status?: number,\n ): Middleware<T, Req, Res> {\n return jsonHandler<T, Req, Res>(async function (req, res) {\n try {\n // https://www.rfc-editor.org/rfc/rfc6749.html#section-5.1\n res.setHeader('Cache-Control', 'no-store')\n res.setHeader('Pragma', 'no-cache')\n\n // https://datatracker.ietf.org/doc/html/rfc9449#section-8.2\n const dpopNonce = server.nextDpopNonce()\n if (dpopNonce) {\n const name = 'DPoP-Nonce'\n res.setHeader(name, dpopNonce)\n res.appendHeader('Access-Control-Expose-Headers', name)\n }\n\n const json = await buildOAuthResponse.call(this, req, res)\n return { json, status }\n } catch (err) {\n onError?.(\n req,\n res,\n err,\n err instanceof OAuthError\n ? `OAuth \"${err.error}\" error`\n : 'Unexpected error',\n )\n\n if (!res.headersSent && err instanceof WWWAuthenticateError) {\n const name = 'WWW-Authenticate'\n res.setHeader(name, err.wwwAuthenticateHeader)\n res.appendHeader('Access-Control-Expose-Headers', name)\n }\n\n const status = buildErrorStatus(err)\n const json = buildErrorPayload(err)\n\n return { json, status }\n }\n })\n }\n}\n\nfunction throwInvalidGrant(err: unknown, prefix: string): never {\n throw new InvalidGrantError(formatError(err, prefix), err)\n}\n\nfunction throwInvalidClient(err: unknown, prefix: string): never {\n throw new InvalidClientError(formatError(err, prefix), err)\n}\n\nfunction throwInvalidRequest(err: unknown, prefix: string): never {\n throw new InvalidRequestError(formatError(err, prefix), err)\n}\n"]}
1
+ {"version":3,"file":"create-oauth-middleware.js","sourceRoot":"","sources":["../../src/router/create-oauth-middleware.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport {\n oauthAuthorizationRequestParSchema,\n oauthClientCredentialsSchema,\n oauthTokenIdentificationSchema,\n oauthTokenRequestSchema,\n} from '@atproto/oauth-types'\nimport { buildErrorPayload, buildErrorStatus } from '../errors/error-parser.js'\nimport { InvalidClientError } from '../errors/invalid-client-error.js'\nimport { InvalidGrantError } from '../errors/invalid-grant-error.js'\nimport { InvalidRequestError } from '../errors/invalid-request-error.js'\nimport { WWWAuthenticateError } from '../errors/www-authenticate-error.js'\nimport {\n Middleware,\n Router,\n cacheControlMiddleware,\n combineMiddlewares,\n jsonHandler,\n parseHttpRequest,\n staticJsonMiddleware,\n} from '../lib/http/index.js'\nimport { formatError } from '../lib/util/error.js'\nimport { OAuthError } from '../oauth-errors.js'\nimport type { OAuthProvider } from '../oauth-provider.js'\nimport type { MiddlewareOptions } from './middleware-options.js'\n\n// CORS preflight\nconst corsHeaders: Middleware = function (req, res, next) {\n res.setHeader('Access-Control-Max-Age', '86400') // 1 day\n\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin\n //\n // > For requests without credentials, the literal value \"*\" can be\n // > specified as a wildcard; the value tells browsers to allow\n // > requesting code from any origin to access the resource.\n // > Attempting to use the wildcard with credentials results in an\n // > error.\n //\n // A \"*\" is safer to use than reflecting the request origin.\n res.setHeader('Access-Control-Allow-Origin', '*')\n\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods\n // > The value \"*\" only counts as a special wildcard value for\n // > requests without credentials (requests without HTTP cookies or\n // > HTTP authentication information). In requests with credentials,\n // > it is treated as the literal method name \"*\" without special\n // > semantics.\n res.setHeader('Access-Control-Allow-Methods', '*')\n\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type,DPoP')\n\n next()\n}\n\nconst corsPreflight: Middleware = combineMiddlewares([\n corsHeaders,\n (req, res) => {\n res.writeHead(200).end()\n },\n])\n\nexport function createOAuthMiddleware<\n Ctx extends object | void = void,\n Req extends IncomingMessage = IncomingMessage,\n Res extends ServerResponse = ServerResponse,\n>(\n server: OAuthProvider,\n { onError }: MiddlewareOptions<Req, Res>,\n): Middleware<Ctx, Req, Res> {\n const router = new Router<Ctx, Req, Res>(new URL(server.issuer))\n\n //- Public OAuth endpoints\n\n router.options('/.well-known/oauth-authorization-server', corsPreflight)\n router.get(\n '/.well-known/oauth-authorization-server',\n corsHeaders,\n cacheControlMiddleware(300),\n staticJsonMiddleware(server.metadata),\n )\n\n router.options('/oauth/jwks', corsPreflight)\n router.get(\n '/oauth/jwks',\n corsHeaders,\n cacheControlMiddleware(300),\n staticJsonMiddleware(server.jwks),\n )\n\n router.options('/oauth/par', corsPreflight)\n router.post(\n '/oauth/par',\n corsHeaders,\n oauthHandler(async function (req) {\n const payload = await parseHttpRequest(req, ['json', 'urlencoded'])\n\n // https://datatracker.ietf.org/doc/html/rfc9126#name-error-response\n // https://datatracker.ietf.org/doc/html/rfc6749#autoid-56\n\n const credentials = await oauthClientCredentialsSchema\n .parseAsync(payload, { path: ['body'] })\n .catch((err) => throwInvalidClient(err, 'Client credentials missing'))\n\n const authorizationRequest = await oauthAuthorizationRequestParSchema\n .parseAsync(payload, { path: ['body'] })\n .catch((err) =>\n throwInvalidRequest(err, 'Invalid authorization request'),\n )\n\n const dpopProof = await server.checkDpopProof(\n req.method!,\n this.url,\n req.headers,\n )\n\n return server.pushedAuthorizationRequest(\n credentials,\n authorizationRequest,\n dpopProof,\n )\n }, 201),\n )\n // https://datatracker.ietf.org/doc/html/rfc9126#section-2.3\n // > If the request did not use the POST method, the authorization server\n // > responds with an HTTP 405 (Method Not Allowed) status code.\n router.all('/oauth/par', (req, res) => {\n res.writeHead(405).end()\n })\n\n router.options('/oauth/token', corsPreflight)\n router.post(\n '/oauth/token',\n corsHeaders,\n oauthHandler(async function (req) {\n const payload = await parseHttpRequest(req, ['json', 'urlencoded'])\n\n const clientMetadata = await server.deviceManager.getRequestMetadata(req)\n\n const clientCredentials = await oauthClientCredentialsSchema\n .parseAsync(payload, { path: ['body'] })\n .catch((err) => throwInvalidGrant(err, 'Client credentials missing'))\n\n const tokenRequest = await oauthTokenRequestSchema\n .parseAsync(payload, { path: ['body'] })\n .catch((err) => throwInvalidGrant(err, 'Invalid request payload'))\n\n const dpopProof = await server.checkDpopProof(\n req.method!,\n this.url,\n req.headers,\n )\n\n return server.token(\n clientCredentials,\n clientMetadata,\n tokenRequest,\n dpopProof,\n )\n }),\n )\n\n router.options('/oauth/revoke', corsPreflight)\n router.post(\n '/oauth/revoke',\n corsHeaders,\n oauthHandler(async function (req, res) {\n const payload = await parseHttpRequest(req, ['json', 'urlencoded'])\n\n const credentials = await oauthClientCredentialsSchema\n .parseAsync(payload, { path: ['body'] })\n .catch((err) => throwInvalidRequest(err, 'Client credentials missing'))\n\n const tokenIdentification = await oauthTokenIdentificationSchema\n .parseAsync(payload, { path: ['body'] })\n .catch((err) => throwInvalidRequest(err, 'Invalid request payload'))\n\n const dpopProof = await server.checkDpopProof(\n req.method!,\n this.url,\n req.headers,\n )\n\n try {\n await server.revoke(credentials, tokenIdentification, dpopProof)\n } catch (err) {\n // > Note: invalid tokens do not cause an error response since the\n // > client cannot handle such an error in a reasonable way. Moreover,\n // > the purpose of the revocation request, invalidating the particular\n // > token, is already achieved.\n //\n // https://datatracker.ietf.org/doc/html/rfc7009#section-2.2\n\n onError?.(req, res, err, 'Failed to revoke token')\n }\n\n return {}\n }),\n )\n\n return router.buildMiddleware()\n\n function oauthHandler<T>(\n buildOAuthResponse: (this: T, req: Req, res: Res) => unknown,\n status?: number,\n ): Middleware<T, Req, Res> {\n return jsonHandler<T, Req, Res>(async function (req, res) {\n try {\n // https://www.rfc-editor.org/rfc/rfc6749.html#section-5.1\n res.setHeader('Cache-Control', 'no-store')\n res.setHeader('Pragma', 'no-cache')\n\n // https://datatracker.ietf.org/doc/html/rfc9449#section-8.2\n const dpopNonce = server.nextDpopNonce()\n if (dpopNonce) {\n const name = 'DPoP-Nonce'\n res.setHeader(name, dpopNonce)\n res.appendHeader('Access-Control-Expose-Headers', name)\n }\n\n const json = await buildOAuthResponse.call(this, req, res)\n return { json, status }\n } catch (err) {\n onError?.(\n req,\n res,\n err,\n err instanceof OAuthError\n ? `OAuth \"${err.error}\" error`\n : 'Unexpected error',\n )\n\n if (!res.headersSent && err instanceof WWWAuthenticateError) {\n const name = 'WWW-Authenticate'\n res.setHeader(name, err.wwwAuthenticateHeader)\n res.appendHeader('Access-Control-Expose-Headers', name)\n }\n\n const status = buildErrorStatus(err)\n const json = buildErrorPayload(err)\n\n return { json, status }\n }\n })\n }\n}\n\nfunction throwInvalidGrant(err: unknown, prefix: string): never {\n throw new InvalidGrantError(formatError(err, prefix), err)\n}\n\nfunction throwInvalidClient(err: unknown, prefix: string): never {\n throw new InvalidClientError(formatError(err, prefix), err)\n}\n\nfunction throwInvalidRequest(err: unknown, prefix: string): never {\n throw new InvalidRequestError(formatError(err, prefix), err)\n}\n"]}
@@ -1,3 +1,2 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ export {};
3
2
  //# sourceMappingURL=error-handler.js.map
@@ -1,3 +1,2 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ export {};
3
2
  //# sourceMappingURL=middleware-options.js.map
@@ -1,24 +1,21 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.accessTokenPayloadSchema = void 0;
4
- const zod_1 = require("zod");
5
- const jwk_1 = require("@atproto/jwk");
6
- const client_id_js_1 = require("../client/client-id.js");
7
- const sub_js_1 = require("../oidc/sub.js");
8
- const token_id_js_1 = require("../token/token-id.js");
9
- exports.accessTokenPayloadSchema = jwk_1.jwtPayloadSchema
1
+ import { z } from 'zod';
2
+ import { jwtPayloadSchema } from '@atproto/jwk';
3
+ import { clientIdSchema } from '../client/client-id.js';
4
+ import { subSchema } from '../oidc/sub.js';
5
+ import { tokenIdSchema } from '../token/token-id.js';
6
+ export const accessTokenPayloadSchema = jwtPayloadSchema
10
7
  .partial()
11
8
  .extend({
12
9
  // Following are required
13
- jti: token_id_js_1.tokenIdSchema,
14
- sub: sub_js_1.subSchema,
15
- exp: zod_1.z.number().int(),
16
- iat: zod_1.z.number().int(),
17
- iss: zod_1.z.string().min(1),
10
+ jti: tokenIdSchema,
11
+ sub: subSchema,
12
+ exp: z.number().int(),
13
+ iat: z.number().int(),
14
+ iss: z.string().min(1),
18
15
  // @NOTE "aud", "scope", "client_id" are not required, as are stored in the
19
16
  // DB in 'light' access token mode.
20
17
  // Restrict type of following
21
- client_id: client_id_js_1.clientIdSchema.optional(),
18
+ client_id: clientIdSchema.optional(),
22
19
  })
23
20
  .passthrough();
24
21
  //# sourceMappingURL=access-token-payload.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"access-token-payload.js","sourceRoot":"","sources":["../../src/signer/access-token-payload.ts"],"names":[],"mappings":";;;AAAA,6BAAuB;AACvB,sCAA+C;AAC/C,yDAAuD;AACvD,2CAA0C;AAC1C,sDAAoD;AAEvC,QAAA,wBAAwB,GAAG,sBAAgB;KACrD,OAAO,EAAE;KACT,MAAM,CAAC;IACN,yBAAyB;IACzB,GAAG,EAAE,2BAAa;IAClB,GAAG,EAAE,kBAAS;IACd,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACrB,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACrB,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAEtB,2EAA2E;IAC3E,mCAAmC;IAEnC,6BAA6B;IAC7B,SAAS,EAAE,6BAAc,CAAC,QAAQ,EAAE;CACrC,CAAC;KACD,WAAW,EAAE,CAAA","sourcesContent":["import { z } from 'zod'\nimport { jwtPayloadSchema } from '@atproto/jwk'\nimport { clientIdSchema } from '../client/client-id.js'\nimport { subSchema } from '../oidc/sub.js'\nimport { tokenIdSchema } from '../token/token-id.js'\n\nexport const accessTokenPayloadSchema = jwtPayloadSchema\n .partial()\n .extend({\n // Following are required\n jti: tokenIdSchema,\n sub: subSchema,\n exp: z.number().int(),\n iat: z.number().int(),\n iss: z.string().min(1),\n\n // @NOTE \"aud\", \"scope\", \"client_id\" are not required, as are stored in the\n // DB in 'light' access token mode.\n\n // Restrict type of following\n client_id: clientIdSchema.optional(),\n })\n .passthrough()\n\nexport type AccessTokenPayload = z.infer<typeof accessTokenPayloadSchema>\n"]}
1
+ {"version":3,"file":"access-token-payload.js","sourceRoot":"","sources":["../../src/signer/access-token-payload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAEpD,MAAM,CAAC,MAAM,wBAAwB,GAAG,gBAAgB;KACrD,OAAO,EAAE;KACT,MAAM,CAAC;IACN,yBAAyB;IACzB,GAAG,EAAE,aAAa;IAClB,GAAG,EAAE,SAAS;IACd,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACrB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACrB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAEtB,2EAA2E;IAC3E,mCAAmC;IAEnC,6BAA6B;IAC7B,SAAS,EAAE,cAAc,CAAC,QAAQ,EAAE;CACrC,CAAC;KACD,WAAW,EAAE,CAAA","sourcesContent":["import { z } from 'zod'\nimport { jwtPayloadSchema } from '@atproto/jwk'\nimport { clientIdSchema } from '../client/client-id.js'\nimport { subSchema } from '../oidc/sub.js'\nimport { tokenIdSchema } from '../token/token-id.js'\n\nexport const accessTokenPayloadSchema = jwtPayloadSchema\n .partial()\n .extend({\n // Following are required\n jti: tokenIdSchema,\n sub: subSchema,\n exp: z.number().int(),\n iat: z.number().int(),\n iss: z.string().min(1),\n\n // @NOTE \"aud\", \"scope\", \"client_id\" are not required, as are stored in the\n // DB in 'light' access token mode.\n\n // Restrict type of following\n client_id: clientIdSchema.optional(),\n })\n .passthrough()\n\nexport type AccessTokenPayload = z.infer<typeof accessTokenPayloadSchema>\n"]}
@@ -1,17 +1,14 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.apiTokenPayloadSchema = void 0;
4
- const jwk_1 = require("@atproto/jwk");
5
- const oauth_store_js_1 = require("../oauth-store.js");
6
- const sub_js_1 = require("../oidc/sub.js");
7
- const request_uri_js_1 = require("../request/request-uri.js");
8
- exports.apiTokenPayloadSchema = jwk_1.jwtPayloadSchema
1
+ import { jwtPayloadSchema } from '@atproto/jwk';
2
+ import { deviceIdSchema } from '../oauth-store.js';
3
+ import { subSchema } from '../oidc/sub.js';
4
+ import { requestUriSchema } from '../request/request-uri.js';
5
+ export const apiTokenPayloadSchema = jwtPayloadSchema
9
6
  .extend({
10
- sub: sub_js_1.subSchema,
11
- deviceId: oauth_store_js_1.deviceIdSchema,
7
+ sub: subSchema,
8
+ deviceId: deviceIdSchema,
12
9
  // If the token is bound to a particular authorization request, it can only
13
10
  // be used in the context of that request.
14
- requestUri: request_uri_js_1.requestUriSchema.optional(),
11
+ requestUri: requestUriSchema.optional(),
15
12
  })
16
13
  .passthrough();
17
14
  //# sourceMappingURL=api-token-payload.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-token-payload.js","sourceRoot":"","sources":["../../src/signer/api-token-payload.ts"],"names":[],"mappings":";;;AACA,sCAA+C;AAC/C,sDAAkD;AAClD,2CAA0C;AAC1C,8DAA4D;AAE/C,QAAA,qBAAqB,GAAG,sBAAgB;KAClD,MAAM,CAAC;IACN,GAAG,EAAE,kBAAS;IAEd,QAAQ,EAAE,+BAAc;IACxB,2EAA2E;IAC3E,0CAA0C;IAC1C,UAAU,EAAE,iCAAgB,CAAC,QAAQ,EAAE;CACxC,CAAC;KACD,WAAW,EAAE,CAAA","sourcesContent":["import { z } from 'zod'\nimport { jwtPayloadSchema } from '@atproto/jwk'\nimport { deviceIdSchema } from '../oauth-store.js'\nimport { subSchema } from '../oidc/sub.js'\nimport { requestUriSchema } from '../request/request-uri.js'\n\nexport const apiTokenPayloadSchema = jwtPayloadSchema\n .extend({\n sub: subSchema,\n\n deviceId: deviceIdSchema,\n // If the token is bound to a particular authorization request, it can only\n // be used in the context of that request.\n requestUri: requestUriSchema.optional(),\n })\n .passthrough()\n\nexport type ApiTokenPayload = z.infer<typeof apiTokenPayloadSchema>\n"]}
1
+ {"version":3,"file":"api-token-payload.js","sourceRoot":"","sources":["../../src/signer/api-token-payload.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAE5D,MAAM,CAAC,MAAM,qBAAqB,GAAG,gBAAgB;KAClD,MAAM,CAAC;IACN,GAAG,EAAE,SAAS;IAEd,QAAQ,EAAE,cAAc;IACxB,2EAA2E;IAC3E,0CAA0C;IAC1C,UAAU,EAAE,gBAAgB,CAAC,QAAQ,EAAE;CACxC,CAAC;KACD,WAAW,EAAE,CAAA","sourcesContent":["import { z } from 'zod'\nimport { jwtPayloadSchema } from '@atproto/jwk'\nimport { deviceIdSchema } from '../oauth-store.js'\nimport { subSchema } from '../oidc/sub.js'\nimport { requestUriSchema } from '../request/request-uri.js'\n\nexport const apiTokenPayloadSchema = jwtPayloadSchema\n .extend({\n sub: subSchema,\n\n deviceId: deviceIdSchema,\n // If the token is bound to a particular authorization request, it can only\n // be used in the context of that request.\n requestUri: requestUriSchema.optional(),\n })\n .passthrough()\n\nexport type ApiTokenPayload = z.infer<typeof apiTokenPayloadSchema>\n"]}