@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.
- package/.postcssrc.yml +3 -0
- package/CHANGELOG.md +19 -0
- package/LICENSE.txt +7 -0
- package/dist/access-token/access-token-type.d.ts +6 -0
- package/dist/access-token/access-token-type.d.ts.map +1 -0
- package/dist/access-token/access-token-type.js +10 -0
- package/dist/access-token/access-token-type.js.map +1 -0
- package/dist/account/account-manager.d.ts +14 -0
- package/dist/account/account-manager.d.ts.map +1 -0
- package/dist/account/account-manager.js +39 -0
- package/dist/account/account-manager.js.map +1 -0
- package/dist/account/account-store.d.ts +39 -0
- package/dist/account/account-store.d.ts.map +1 -0
- package/dist/account/account-store.js +19 -0
- package/dist/account/account-store.js.map +1 -0
- package/dist/account/account.d.ts +8 -0
- package/dist/account/account.d.ts.map +1 -0
- package/dist/account/account.js +3 -0
- package/dist/account/account.js.map +1 -0
- package/dist/assets/app/bundle-manifest.json +22 -0
- package/dist/assets/app/main.css +3 -0
- package/dist/assets/app/main.js +20 -0
- package/dist/assets/app/main.js.map +1 -0
- package/dist/assets/asset.d.ts +9 -0
- package/dist/assets/asset.d.ts.map +1 -0
- package/dist/assets/asset.js +3 -0
- package/dist/assets/asset.js.map +1 -0
- package/dist/assets/assets-middleware.d.ts +2 -0
- package/dist/assets/assets-middleware.d.ts.map +1 -0
- package/dist/assets/assets-middleware.js +30 -0
- package/dist/assets/assets-middleware.js.map +1 -0
- package/dist/assets/index.d.ts +4 -0
- package/dist/assets/index.d.ts.map +1 -0
- package/dist/assets/index.js +65 -0
- package/dist/assets/index.js.map +1 -0
- package/dist/client/client-auth.d.ts +13 -0
- package/dist/client/client-auth.d.ts.map +1 -0
- package/dist/client/client-auth.js +35 -0
- package/dist/client/client-auth.js.map +1 -0
- package/dist/client/client-data.d.ts +8 -0
- package/dist/client/client-data.d.ts.map +1 -0
- package/dist/client/client-data.js +3 -0
- package/dist/client/client-data.js.map +1 -0
- package/dist/client/client-id.d.ts +4 -0
- package/dist/client/client-id.d.ts.map +1 -0
- package/dist/client/client-id.js +6 -0
- package/dist/client/client-id.js.map +1 -0
- package/dist/client/client-info.d.ts +13 -0
- package/dist/client/client-info.d.ts.map +1 -0
- package/dist/client/client-info.js +3 -0
- package/dist/client/client-info.js.map +1 -0
- package/dist/client/client-manager.d.ts +38 -0
- package/dist/client/client-manager.d.ts.map +1 -0
- package/dist/client/client-manager.js +534 -0
- package/dist/client/client-manager.js.map +1 -0
- package/dist/client/client-store.d.ts +13 -0
- package/dist/client/client-store.d.ts.map +1 -0
- package/dist/client/client-store.js +39 -0
- package/dist/client/client-store.js.map +1 -0
- package/dist/client/client-utils.d.ts +6 -0
- package/dist/client/client-utils.d.ts.map +1 -0
- package/dist/client/client-utils.js +40 -0
- package/dist/client/client-utils.js.map +1 -0
- package/dist/client/client.d.ts +41 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +163 -0
- package/dist/client/client.js.map +1 -0
- package/dist/constants.d.ts +42 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +53 -0
- package/dist/constants.js.map +1 -0
- package/dist/device/device-data.d.ts +20 -0
- package/dist/device/device-data.d.ts.map +1 -0
- package/dist/device/device-data.js +11 -0
- package/dist/device/device-data.js.map +1 -0
- package/dist/device/device-details.d.ts +17 -0
- package/dist/device/device-details.d.ts.map +1 -0
- package/dist/device/device-details.js +34 -0
- package/dist/device/device-details.js.map +1 -0
- package/dist/device/device-id.d.ts +6 -0
- package/dist/device/device-id.d.ts.map +1 -0
- package/dist/device/device-id.js +18 -0
- package/dist/device/device-id.js.map +1 -0
- package/dist/device/device-manager.d.ts +88 -0
- package/dist/device/device-manager.d.ts.map +1 -0
- package/dist/device/device-manager.js +206 -0
- package/dist/device/device-manager.js.map +1 -0
- package/dist/device/device-store.d.ts +15 -0
- package/dist/device/device-store.d.ts.map +1 -0
- package/dist/device/device-store.js +36 -0
- package/dist/device/device-store.js.map +1 -0
- package/dist/device/session-id.d.ts +6 -0
- package/dist/device/session-id.d.ts.map +1 -0
- package/dist/device/session-id.js +18 -0
- package/dist/device/session-id.js.map +1 -0
- package/dist/dpop/dpop-manager.d.ts +33 -0
- package/dist/dpop/dpop-manager.d.ts.map +1 -0
- package/dist/dpop/dpop-manager.js +115 -0
- package/dist/dpop/dpop-manager.js.map +1 -0
- package/dist/dpop/dpop-nonce.d.ts +13 -0
- package/dist/dpop/dpop-nonce.d.ts.map +1 -0
- package/dist/dpop/dpop-nonce.js +94 -0
- package/dist/dpop/dpop-nonce.js.map +1 -0
- package/dist/errors/access-denied-error.d.ts +8 -0
- package/dist/errors/access-denied-error.d.ts.map +1 -0
- package/dist/errors/access-denied-error.js +21 -0
- package/dist/errors/access-denied-error.js.map +1 -0
- package/dist/errors/account-selection-required-error.d.ts +6 -0
- package/dist/errors/account-selection-required-error.d.ts.map +1 -0
- package/dist/errors/account-selection-required-error.js +11 -0
- package/dist/errors/account-selection-required-error.js.map +1 -0
- package/dist/errors/consent-required-error.d.ts +6 -0
- package/dist/errors/consent-required-error.d.ts.map +1 -0
- package/dist/errors/consent-required-error.js +11 -0
- package/dist/errors/consent-required-error.js.map +1 -0
- package/dist/errors/invalid-authorization-details-error.d.ts +20 -0
- package/dist/errors/invalid-authorization-details-error.d.ts.map +1 -0
- package/dist/errors/invalid-authorization-details-error.js +26 -0
- package/dist/errors/invalid-authorization-details-error.js.map +1 -0
- package/dist/errors/invalid-client-error.d.ts +18 -0
- package/dist/errors/invalid-client-error.d.ts.map +1 -0
- package/dist/errors/invalid-client-error.js +24 -0
- package/dist/errors/invalid-client-error.js.map +1 -0
- package/dist/errors/invalid-client-id-error.d.ts +13 -0
- package/dist/errors/invalid-client-id-error.d.ts.map +1 -0
- package/dist/errors/invalid-client-id-error.js +25 -0
- package/dist/errors/invalid-client-id-error.js.map +1 -0
- package/dist/errors/invalid-client-metadata-error.d.ts +13 -0
- package/dist/errors/invalid-client-metadata-error.d.ts.map +1 -0
- package/dist/errors/invalid-client-metadata-error.js +23 -0
- package/dist/errors/invalid-client-metadata-error.js.map +1 -0
- package/dist/errors/invalid-dpop-key-binding-error.d.ts +12 -0
- package/dist/errors/invalid-dpop-key-binding-error.d.ts.map +1 -0
- package/dist/errors/invalid-dpop-key-binding-error.js +20 -0
- package/dist/errors/invalid-dpop-key-binding-error.js.map +1 -0
- package/dist/errors/invalid-dpop-proof-error.d.ts +5 -0
- package/dist/errors/invalid-dpop-proof-error.d.ts.map +1 -0
- package/dist/errors/invalid-dpop-proof-error.js +12 -0
- package/dist/errors/invalid-dpop-proof-error.js.map +1 -0
- package/dist/errors/invalid-grant-error.d.ts +14 -0
- package/dist/errors/invalid-grant-error.d.ts.map +1 -0
- package/dist/errors/invalid-grant-error.js +20 -0
- package/dist/errors/invalid-grant-error.js.map +1 -0
- package/dist/errors/invalid-parameters-error.d.ts +6 -0
- package/dist/errors/invalid-parameters-error.d.ts.map +1 -0
- package/dist/errors/invalid-parameters-error.js +11 -0
- package/dist/errors/invalid-parameters-error.js.map +1 -0
- package/dist/errors/invalid-redirect-uri-error.d.ts +11 -0
- package/dist/errors/invalid-redirect-uri-error.d.ts.map +1 -0
- package/dist/errors/invalid-redirect-uri-error.js +21 -0
- package/dist/errors/invalid-redirect-uri-error.js.map +1 -0
- package/dist/errors/invalid-request-error.d.ts +28 -0
- package/dist/errors/invalid-request-error.d.ts.map +1 -0
- package/dist/errors/invalid-request-error.js +34 -0
- package/dist/errors/invalid-request-error.js.map +1 -0
- package/dist/errors/invalid-token-error.d.ts +16 -0
- package/dist/errors/invalid-token-error.d.ts.map +1 -0
- package/dist/errors/invalid-token-error.js +45 -0
- package/dist/errors/invalid-token-error.js.map +1 -0
- package/dist/errors/login-required-error.d.ts +6 -0
- package/dist/errors/login-required-error.d.ts.map +1 -0
- package/dist/errors/login-required-error.js +11 -0
- package/dist/errors/login-required-error.js.map +1 -0
- package/dist/errors/oauth-error.d.ts +13 -0
- package/dist/errors/oauth-error.d.ts.map +1 -0
- package/dist/errors/oauth-error.js +29 -0
- package/dist/errors/oauth-error.js.map +1 -0
- package/dist/errors/unauthorized-client-error.d.ts +18 -0
- package/dist/errors/unauthorized-client-error.d.ts.map +1 -0
- package/dist/errors/unauthorized-client-error.js +24 -0
- package/dist/errors/unauthorized-client-error.js.map +1 -0
- package/dist/errors/use-dpop-nonce-error.d.ts +18 -0
- package/dist/errors/use-dpop-nonce-error.d.ts.map +1 -0
- package/dist/errors/use-dpop-nonce-error.js +27 -0
- package/dist/errors/use-dpop-nonce-error.js.map +1 -0
- package/dist/errors/www-authenticate-error.d.ts +9 -0
- package/dist/errors/www-authenticate-error.d.ts.map +1 -0
- package/dist/errors/www-authenticate-error.js +46 -0
- package/dist/errors/www-authenticate-error.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/html/build-document.d.ts +32 -0
- package/dist/lib/html/build-document.d.ts.map +1 -0
- package/dist/lib/html/build-document.js +61 -0
- package/dist/lib/html/build-document.js.map +1 -0
- package/dist/lib/html/escapers.d.ts +9 -0
- package/dist/lib/html/escapers.d.ts.map +1 -0
- package/dist/lib/html/escapers.js +66 -0
- package/dist/lib/html/escapers.js.map +1 -0
- package/dist/lib/html/html.d.ts +13 -0
- package/dist/lib/html/html.d.ts.map +1 -0
- package/dist/lib/html/html.js +53 -0
- package/dist/lib/html/html.js.map +1 -0
- package/dist/lib/html/index.d.ts +4 -0
- package/dist/lib/html/index.d.ts.map +1 -0
- package/dist/lib/html/index.js +21 -0
- package/dist/lib/html/index.js.map +1 -0
- package/dist/lib/html/tags.d.ts +34 -0
- package/dist/lib/html/tags.d.ts.map +1 -0
- package/dist/lib/html/tags.js +47 -0
- package/dist/lib/html/tags.js.map +1 -0
- package/dist/lib/html/util.d.ts +4 -0
- package/dist/lib/html/util.d.ts.map +1 -0
- package/dist/lib/html/util.js +20 -0
- package/dist/lib/html/util.js.map +1 -0
- package/dist/lib/http/accept.d.ts +29 -0
- package/dist/lib/http/accept.d.ts.map +1 -0
- package/dist/lib/http/accept.js +67 -0
- package/dist/lib/http/accept.js.map +1 -0
- package/dist/lib/http/context.d.ts +5 -0
- package/dist/lib/http/context.d.ts.map +1 -0
- package/dist/lib/http/context.js +10 -0
- package/dist/lib/http/context.js.map +1 -0
- package/dist/lib/http/index.d.ts +10 -0
- package/dist/lib/http/index.d.ts.map +1 -0
- package/dist/lib/http/index.js +26 -0
- package/dist/lib/http/index.js.map +1 -0
- package/dist/lib/http/method.d.ts +6 -0
- package/dist/lib/http/method.d.ts.map +1 -0
- package/dist/lib/http/method.js +19 -0
- package/dist/lib/http/method.js.map +1 -0
- package/dist/lib/http/middleware.d.ts +18 -0
- package/dist/lib/http/middleware.d.ts.map +1 -0
- package/dist/lib/http/middleware.js +118 -0
- package/dist/lib/http/middleware.js.map +1 -0
- package/dist/lib/http/parser.d.ts +33 -0
- package/dist/lib/http/parser.d.ts.map +1 -0
- package/dist/lib/http/parser.js +48 -0
- package/dist/lib/http/parser.js.map +1 -0
- package/dist/lib/http/path.d.ts +9 -0
- package/dist/lib/http/path.d.ts.map +1 -0
- package/dist/lib/http/path.js +54 -0
- package/dist/lib/http/path.js.map +1 -0
- package/dist/lib/http/request.d.ts +33 -0
- package/dist/lib/http/request.d.ts.map +1 -0
- package/dist/lib/http/request.js +86 -0
- package/dist/lib/http/request.js.map +1 -0
- package/dist/lib/http/response.d.ts +13 -0
- package/dist/lib/http/response.d.ts.map +1 -0
- package/dist/lib/http/response.js +98 -0
- package/dist/lib/http/response.js.map +1 -0
- package/dist/lib/http/route.d.ts +25 -0
- package/dist/lib/http/route.d.ts.map +1 -0
- package/dist/lib/http/route.js +39 -0
- package/dist/lib/http/route.js.map +1 -0
- package/dist/lib/http/router.d.ts +32 -0
- package/dist/lib/http/router.d.ts.map +1 -0
- package/dist/lib/http/router.js +74 -0
- package/dist/lib/http/router.js.map +1 -0
- package/dist/lib/http/stream.d.ts +13 -0
- package/dist/lib/http/stream.d.ts.map +1 -0
- package/dist/lib/http/stream.js +46 -0
- package/dist/lib/http/stream.js.map +1 -0
- package/dist/lib/http/types.d.ts +7 -0
- package/dist/lib/http/types.d.ts.map +1 -0
- package/dist/lib/http/types.js +3 -0
- package/dist/lib/http/types.js.map +1 -0
- package/dist/lib/http/url.d.ts +8 -0
- package/dist/lib/http/url.d.ts.map +1 -0
- package/dist/lib/http/url.js +22 -0
- package/dist/lib/http/url.js.map +1 -0
- package/dist/lib/redis.d.ts +5 -0
- package/dist/lib/redis.d.ts.map +1 -0
- package/dist/lib/redis.js +22 -0
- package/dist/lib/redis.js.map +1 -0
- package/dist/lib/util/authorization-header.d.ts +4 -0
- package/dist/lib/util/authorization-header.d.ts.map +1 -0
- package/dist/lib/util/authorization-header.js +23 -0
- package/dist/lib/util/authorization-header.js.map +1 -0
- package/dist/lib/util/cast.d.ts +2 -0
- package/dist/lib/util/cast.d.ts.map +1 -0
- package/dist/lib/util/cast.js +10 -0
- package/dist/lib/util/cast.js.map +1 -0
- package/dist/lib/util/crypto.d.ts +3 -0
- package/dist/lib/util/crypto.d.ts.map +1 -0
- package/dist/lib/util/crypto.js +29 -0
- package/dist/lib/util/crypto.js.map +1 -0
- package/dist/lib/util/date.d.ts +3 -0
- package/dist/lib/util/date.d.ts.map +1 -0
- package/dist/lib/util/date.js +12 -0
- package/dist/lib/util/date.js.map +1 -0
- package/dist/lib/util/hostname.d.ts +6 -0
- package/dist/lib/util/hostname.d.ts.map +1 -0
- package/dist/lib/util/hostname.js +24 -0
- package/dist/lib/util/hostname.js.map +1 -0
- package/dist/lib/util/redirect-uri.d.ts +7 -0
- package/dist/lib/util/redirect-uri.d.ts.map +1 -0
- package/dist/lib/util/redirect-uri.js +44 -0
- package/dist/lib/util/redirect-uri.js.map +1 -0
- package/dist/lib/util/time.d.ts +6 -0
- package/dist/lib/util/time.d.ts.map +1 -0
- package/dist/lib/util/time.js +28 -0
- package/dist/lib/util/time.js.map +1 -0
- package/dist/lib/util/type.d.ts +6 -0
- package/dist/lib/util/type.d.ts.map +1 -0
- package/dist/lib/util/type.js +3 -0
- package/dist/lib/util/type.js.map +1 -0
- package/dist/lib/util/well-known.d.ts +3 -0
- package/dist/lib/util/well-known.d.ts.map +1 -0
- package/dist/lib/util/well-known.js +11 -0
- package/dist/lib/util/well-known.js.map +1 -0
- package/dist/metadata/build-metadata.d.ts +14 -0
- package/dist/metadata/build-metadata.d.ts.map +1 -0
- package/dist/metadata/build-metadata.js +132 -0
- package/dist/metadata/build-metadata.js.map +1 -0
- package/dist/oauth-client.d.ts +4 -0
- package/dist/oauth-client.d.ts.map +1 -0
- package/dist/oauth-client.js +19 -0
- package/dist/oauth-client.js.map +1 -0
- package/dist/oauth-dpop.d.ts +3 -0
- package/dist/oauth-dpop.d.ts.map +1 -0
- package/dist/oauth-dpop.js +19 -0
- package/dist/oauth-dpop.js.map +1 -0
- package/dist/oauth-errors.d.ts +20 -0
- package/dist/oauth-errors.d.ts.map +1 -0
- package/dist/oauth-errors.js +43 -0
- package/dist/oauth-errors.js.map +1 -0
- package/dist/oauth-hooks.d.ts +42 -0
- package/dist/oauth-hooks.d.ts.map +1 -0
- package/dist/oauth-hooks.js +3 -0
- package/dist/oauth-hooks.js.map +1 -0
- package/dist/oauth-provider.d.ts +179 -0
- package/dist/oauth-provider.d.ts.map +1 -0
- package/dist/oauth-provider.js +748 -0
- package/dist/oauth-provider.js.map +1 -0
- package/dist/oauth-store.d.ts +11 -0
- package/dist/oauth-store.d.ts.map +1 -0
- package/dist/oauth-store.js +27 -0
- package/dist/oauth-store.js.map +1 -0
- package/dist/oauth-verifier.d.ts +66 -0
- package/dist/oauth-verifier.d.ts.map +1 -0
- package/dist/oauth-verifier.js +94 -0
- package/dist/oauth-verifier.js.map +1 -0
- package/dist/oidc/claims.d.ts +16 -0
- package/dist/oidc/claims.d.ts.map +1 -0
- package/dist/oidc/claims.js +29 -0
- package/dist/oidc/claims.js.map +1 -0
- package/dist/oidc/sub.d.ts +4 -0
- package/dist/oidc/sub.d.ts.map +1 -0
- package/dist/oidc/sub.js +6 -0
- package/dist/oidc/sub.js.map +1 -0
- package/dist/oidc/userinfo.d.ts +7 -0
- package/dist/oidc/userinfo.d.ts.map +1 -0
- package/dist/oidc/userinfo.js +3 -0
- package/dist/oidc/userinfo.js.map +1 -0
- package/dist/output/build-error-payload.d.ts +6 -0
- package/dist/output/build-error-payload.d.ts.map +1 -0
- package/dist/output/build-error-payload.js +108 -0
- package/dist/output/build-error-payload.js.map +1 -0
- package/dist/output/customization.d.ts +37 -0
- package/dist/output/customization.d.ts.map +1 -0
- package/dist/output/customization.js +62 -0
- package/dist/output/customization.js.map +1 -0
- package/dist/output/send-authorize-page.d.ts +43 -0
- package/dist/output/send-authorize-page.d.ts.map +1 -0
- package/dist/output/send-authorize-page.js +49 -0
- package/dist/output/send-authorize-page.js.map +1 -0
- package/dist/output/send-authorize-redirect.d.ts +25 -0
- package/dist/output/send-authorize-redirect.d.ts.map +1 -0
- package/dist/output/send-authorize-redirect.js +72 -0
- package/dist/output/send-authorize-redirect.js.map +1 -0
- package/dist/output/send-error-page.d.ts +5 -0
- package/dist/output/send-error-page.d.ts.map +1 -0
- package/dist/output/send-error-page.js +31 -0
- package/dist/output/send-error-page.js.map +1 -0
- package/dist/output/send-web-page.d.ts +8 -0
- package/dist/output/send-web-page.d.ts.map +1 -0
- package/dist/output/send-web-page.js +48 -0
- package/dist/output/send-web-page.js.map +1 -0
- package/dist/parameters/claims-requested.d.ts +3 -0
- package/dist/parameters/claims-requested.d.ts.map +1 -0
- package/dist/parameters/claims-requested.js +77 -0
- package/dist/parameters/claims-requested.js.map +1 -0
- package/dist/parameters/oidc-payload.d.ts +31 -0
- package/dist/parameters/oidc-payload.d.ts.map +1 -0
- package/dist/parameters/oidc-payload.js +25 -0
- package/dist/parameters/oidc-payload.js.map +1 -0
- package/dist/replay/replay-manager.d.ts +10 -0
- package/dist/replay/replay-manager.d.ts.map +1 -0
- package/dist/replay/replay-manager.js +23 -0
- package/dist/replay/replay-manager.js.map +1 -0
- package/dist/replay/replay-store-memory.d.ts +11 -0
- package/dist/replay/replay-store-memory.d.ts.map +1 -0
- package/dist/replay/replay-store-memory.js +30 -0
- package/dist/replay/replay-store-memory.js.map +1 -0
- package/dist/replay/replay-store-redis.d.ts +16 -0
- package/dist/replay/replay-store-redis.d.ts.map +1 -0
- package/dist/replay/replay-store-redis.js +20 -0
- package/dist/replay/replay-store-redis.js.map +1 -0
- package/dist/replay/replay-store.d.ts +16 -0
- package/dist/replay/replay-store.d.ts.map +1 -0
- package/dist/replay/replay-store.js +22 -0
- package/dist/replay/replay-store.js.map +1 -0
- package/dist/request/code.d.ts +7 -0
- package/dist/request/code.d.ts.map +1 -0
- package/dist/request/code.js +20 -0
- package/dist/request/code.js.map +1 -0
- package/dist/request/request-data.d.ts +21 -0
- package/dist/request/request-data.d.ts.map +1 -0
- package/dist/request/request-data.js +6 -0
- package/dist/request/request-data.js.map +1 -0
- package/dist/request/request-id.d.ts +6 -0
- package/dist/request/request-id.d.ts.map +1 -0
- package/dist/request/request-id.js +18 -0
- package/dist/request/request-id.js.map +1 -0
- package/dist/request/request-info.d.ts +12 -0
- package/dist/request/request-info.d.ts.map +1 -0
- package/dist/request/request-info.js +3 -0
- package/dist/request/request-info.js.map +1 -0
- package/dist/request/request-manager.d.ts +40 -0
- package/dist/request/request-manager.d.ts.map +1 -0
- package/dist/request/request-manager.js +310 -0
- package/dist/request/request-manager.js.map +1 -0
- package/dist/request/request-store-memory.d.ts +16 -0
- package/dist/request/request-store-memory.d.ts.map +1 -0
- package/dist/request/request-store-memory.js +31 -0
- package/dist/request/request-store-memory.js.map +1 -0
- package/dist/request/request-store-redis.d.ts +24 -0
- package/dist/request/request-store-redis.d.ts.map +1 -0
- package/dist/request/request-store-redis.js +58 -0
- package/dist/request/request-store-redis.js.map +1 -0
- package/dist/request/request-store.d.ts +27 -0
- package/dist/request/request-store.d.ts.map +1 -0
- package/dist/request/request-store.js +37 -0
- package/dist/request/request-store.js.map +1 -0
- package/dist/request/request-uri.d.ts +8 -0
- package/dist/request/request-uri.d.ts.map +1 -0
- package/dist/request/request-uri.js +24 -0
- package/dist/request/request-uri.js.map +1 -0
- package/dist/request/types.d.ts +328 -0
- package/dist/request/types.d.ts.map +1 -0
- package/dist/request/types.js +27 -0
- package/dist/request/types.js.map +1 -0
- package/dist/signer/signed-token-payload.d.ts +1694 -0
- package/dist/signer/signed-token-payload.d.ts.map +1 -0
- package/dist/signer/signed-token-payload.js +32 -0
- package/dist/signer/signed-token-payload.js.map +1 -0
- package/dist/signer/signer.d.ts +193 -0
- package/dist/signer/signer.d.ts.map +1 -0
- package/dist/signer/signer.js +101 -0
- package/dist/signer/signer.js.map +1 -0
- package/dist/token/refresh-token.d.ts +7 -0
- package/dist/token/refresh-token.d.ts.map +1 -0
- package/dist/token/refresh-token.js +20 -0
- package/dist/token/refresh-token.js.map +1 -0
- package/dist/token/token-claims.d.ts +1687 -0
- package/dist/token/token-claims.d.ts.map +1 -0
- package/dist/token/token-claims.js +30 -0
- package/dist/token/token-claims.js.map +1 -0
- package/dist/token/token-data.d.ts +20 -0
- package/dist/token/token-data.d.ts.map +1 -0
- package/dist/token/token-data.js +3 -0
- package/dist/token/token-data.js.map +1 -0
- package/dist/token/token-id.d.ts +7 -0
- package/dist/token/token-id.d.ts.map +1 -0
- package/dist/token/token-id.js +20 -0
- package/dist/token/token-id.js.map +1 -0
- package/dist/token/token-manager.d.ts +48 -0
- package/dist/token/token-manager.d.ts.map +1 -0
- package/dist/token/token-manager.js +421 -0
- package/dist/token/token-manager.js.map +1 -0
- package/dist/token/token-store.d.ts +35 -0
- package/dist/token/token-store.d.ts.map +1 -0
- package/dist/token/token-store.js +38 -0
- package/dist/token/token-store.js.map +1 -0
- package/dist/token/types.d.ts +250 -0
- package/dist/token/types.d.ts.map +1 -0
- package/dist/token/types.js +36 -0
- package/dist/token/types.js.map +1 -0
- package/dist/token/verify-token-claims.d.ts +17 -0
- package/dist/token/verify-token-claims.d.ts.map +1 -0
- package/dist/token/verify-token-claims.js +39 -0
- package/dist/token/verify-token-claims.js.map +1 -0
- package/package.json +83 -0
- package/rollup.config.js +55 -0
- package/src/access-token/access-token-type.ts +5 -0
- package/src/account/account-manager.ts +55 -0
- package/src/account/account-store.ts +74 -0
- package/src/account/account.ts +10 -0
- package/src/assets/app/app.tsx +28 -0
- package/src/assets/app/backend-data.ts +65 -0
- package/src/assets/app/components/accept-form.tsx +112 -0
- package/src/assets/app/components/account-identifier.tsx +18 -0
- package/src/assets/app/components/account-picker.tsx +108 -0
- package/src/assets/app/components/client-identifier.tsx +32 -0
- package/src/assets/app/components/client-name.tsx +30 -0
- package/src/assets/app/components/error-card.tsx +41 -0
- package/src/assets/app/components/help-card.tsx +42 -0
- package/src/assets/app/components/layout-title-page.tsx +43 -0
- package/src/assets/app/components/layout-welcome.tsx +58 -0
- package/src/assets/app/components/sign-in-form.tsx +290 -0
- package/src/assets/app/components/sign-up-account-form.tsx +210 -0
- package/src/assets/app/components/sign-up-disclaimer.tsx +44 -0
- package/src/assets/app/components/url-viewer.tsx +70 -0
- package/src/assets/app/cookies.ts +11 -0
- package/src/assets/app/hooks/use-api.ts +104 -0
- package/src/assets/app/hooks/use-bound-dispatch.ts +5 -0
- package/src/assets/app/hooks/use-csrf-token.ts +5 -0
- package/src/assets/app/lib/api.ts +64 -0
- package/src/assets/app/lib/clsx.ts +4 -0
- package/src/assets/app/lib/util.ts +10 -0
- package/src/assets/app/main.css +11 -0
- package/src/assets/app/main.tsx +28 -0
- package/src/assets/app/views/accept-view.tsx +51 -0
- package/src/assets/app/views/authorize-view.tsx +101 -0
- package/src/assets/app/views/error-view.tsx +27 -0
- package/src/assets/app/views/sign-in-view.tsx +121 -0
- package/src/assets/app/views/sign-up-view.tsx +93 -0
- package/src/assets/app/views/welcome-view.tsx +61 -0
- package/src/assets/asset.ts +8 -0
- package/src/assets/assets-middleware.ts +32 -0
- package/src/assets/index.ts +74 -0
- package/src/client/client-auth.ts +45 -0
- package/src/client/client-data.ts +9 -0
- package/src/client/client-id.ts +4 -0
- package/src/client/client-info.ts +13 -0
- package/src/client/client-manager.ts +818 -0
- package/src/client/client-store.ts +38 -0
- package/src/client/client-utils.ts +43 -0
- package/src/client/client.ts +231 -0
- package/src/constants.ts +69 -0
- package/src/device/device-data.ts +11 -0
- package/src/device/device-details.ts +43 -0
- package/src/device/device-id.ts +23 -0
- package/src/device/device-manager.ts +287 -0
- package/src/device/device-store.ts +35 -0
- package/src/device/session-id.ts +22 -0
- package/src/dpop/dpop-manager.ts +147 -0
- package/src/dpop/dpop-nonce.ts +104 -0
- package/src/errors/access-denied-error.ts +26 -0
- package/src/errors/account-selection-required-error.ts +12 -0
- package/src/errors/consent-required-error.ts +12 -0
- package/src/errors/invalid-authorization-details-error.ts +22 -0
- package/src/errors/invalid-client-error.ts +20 -0
- package/src/errors/invalid-client-id-error.ts +20 -0
- package/src/errors/invalid-client-metadata-error.ts +19 -0
- package/src/errors/invalid-dpop-key-binding-error.ts +21 -0
- package/src/errors/invalid-dpop-proof-error.ts +13 -0
- package/src/errors/invalid-grant-error.ts +16 -0
- package/src/errors/invalid-parameters-error.ts +12 -0
- package/src/errors/invalid-redirect-uri-error.ts +17 -0
- package/src/errors/invalid-request-error.ts +30 -0
- package/src/errors/invalid-token-error.ts +59 -0
- package/src/errors/login-required-error.ts +12 -0
- package/src/errors/oauth-error.ts +28 -0
- package/src/errors/unauthorized-client-error.ts +20 -0
- package/src/errors/use-dpop-nonce-error.ts +32 -0
- package/src/errors/www-authenticate-error.ts +65 -0
- package/src/index.ts +15 -0
- package/src/lib/html/README.md +9 -0
- package/src/lib/html/build-document.ts +98 -0
- package/src/lib/html/escapers.ts +66 -0
- package/src/lib/html/html.ts +61 -0
- package/src/lib/html/index.ts +5 -0
- package/src/lib/html/tags.ts +58 -0
- package/src/lib/html/util.ts +21 -0
- package/src/lib/http/README.md +11 -0
- package/src/lib/http/accept.ts +91 -0
- package/src/lib/http/context.ts +11 -0
- package/src/lib/http/index.ts +9 -0
- package/src/lib/http/method.ts +18 -0
- package/src/lib/http/middleware.ts +183 -0
- package/src/lib/http/parser.ts +64 -0
- package/src/lib/http/path.ts +82 -0
- package/src/lib/http/request.ts +141 -0
- package/src/lib/http/response.ts +133 -0
- package/src/lib/http/route.ts +56 -0
- package/src/lib/http/router.ts +118 -0
- package/src/lib/http/stream.ts +78 -0
- package/src/lib/http/types.ts +22 -0
- package/src/lib/http/url.ts +23 -0
- package/src/lib/redis.ts +23 -0
- package/src/lib/util/authorization-header.ts +26 -0
- package/src/lib/util/cast.ts +4 -0
- package/src/lib/util/crypto.ts +27 -0
- package/src/lib/util/date.ts +7 -0
- package/src/lib/util/hostname.ts +19 -0
- package/src/lib/util/redirect-uri.ts +46 -0
- package/src/lib/util/time.ts +33 -0
- package/src/lib/util/type.ts +4 -0
- package/src/lib/util/well-known.ts +8 -0
- package/src/metadata/build-metadata.ts +165 -0
- package/src/oauth-client.ts +3 -0
- package/src/oauth-dpop.ts +2 -0
- package/src/oauth-errors.ts +21 -0
- package/src/oauth-hooks.ts +66 -0
- package/src/oauth-provider.ts +1409 -0
- package/src/oauth-store.ts +11 -0
- package/src/oauth-verifier.ts +219 -0
- package/src/oidc/claims.ts +35 -0
- package/src/oidc/sub.ts +4 -0
- package/src/oidc/userinfo.ts +11 -0
- package/src/output/build-error-payload.ts +143 -0
- package/src/output/customization.ts +96 -0
- package/src/output/send-authorize-page.ts +111 -0
- package/src/output/send-authorize-redirect.ts +130 -0
- package/src/output/send-error-page.ts +41 -0
- package/src/output/send-web-page.ts +66 -0
- package/src/parameters/claims-requested.ts +106 -0
- package/src/parameters/oidc-payload.ts +28 -0
- package/src/replay/replay-manager.ts +38 -0
- package/src/replay/replay-store-memory.ts +36 -0
- package/src/replay/replay-store-redis.ts +31 -0
- package/src/replay/replay-store.ts +44 -0
- package/src/request/code.ts +24 -0
- package/src/request/request-data.ts +26 -0
- package/src/request/request-id.ts +23 -0
- package/src/request/request-info.ts +12 -0
- package/src/request/request-manager.ts +479 -0
- package/src/request/request-store-memory.ts +39 -0
- package/src/request/request-store-redis.ts +71 -0
- package/src/request/request-store.ts +54 -0
- package/src/request/request-uri.ts +29 -0
- package/src/request/types.ts +48 -0
- package/src/signer/signed-token-payload.ts +35 -0
- package/src/signer/signer.ts +165 -0
- package/src/token/refresh-token.ts +31 -0
- package/src/token/token-claims.ts +31 -0
- package/src/token/token-data.ts +33 -0
- package/src/token/token-id.ts +26 -0
- package/src/token/token-manager.ts +591 -0
- package/src/token/token-store.ts +78 -0
- package/src/token/types.ts +86 -0
- package/src/token/verify-token-claims.ts +65 -0
- package/tailwind.config.js +13 -0
- package/tsconfig.backend.json +9 -0
- package/tsconfig.frontend.json +11 -0
- package/tsconfig.json +8 -0
- package/tsconfig.tools.json +8 -0
@@ -0,0 +1,818 @@
|
|
1
|
+
import {
|
2
|
+
bindFetch,
|
3
|
+
Fetch,
|
4
|
+
fetchJsonProcessor,
|
5
|
+
fetchJsonZodProcessor,
|
6
|
+
fetchOkProcessor,
|
7
|
+
} from '@atproto-labs/fetch'
|
8
|
+
import { pipe } from '@atproto-labs/pipe'
|
9
|
+
import {
|
10
|
+
CachedGetter,
|
11
|
+
GetCachedOptions,
|
12
|
+
SimpleStore,
|
13
|
+
} from '@atproto-labs/simple-store'
|
14
|
+
import { Jwks, jwksSchema, Keyset } from '@atproto/jwk'
|
15
|
+
import {
|
16
|
+
isLoopbackHost,
|
17
|
+
isLoopbackUrl,
|
18
|
+
isOAuthClientIdDiscoverable,
|
19
|
+
isOAuthClientIdLoopback,
|
20
|
+
OAUTH_AUTHENTICATED_ENDPOINT_NAMES,
|
21
|
+
OAuthClientIdDiscoverable,
|
22
|
+
OAuthClientIdLoopback,
|
23
|
+
OAuthClientMetadata,
|
24
|
+
OAuthClientMetadataInput,
|
25
|
+
oauthClientMetadataSchema,
|
26
|
+
} from '@atproto/oauth-types'
|
27
|
+
|
28
|
+
import { ALLOW_LOOPBACK_CLIENT_REFRESH_TOKEN } from '../constants.js'
|
29
|
+
import { InvalidClientMetadataError } from '../errors/invalid-client-metadata-error.js'
|
30
|
+
import { InvalidRedirectUriError } from '../errors/invalid-redirect-uri-error.js'
|
31
|
+
import { OAuthError } from '../errors/oauth-error.js'
|
32
|
+
import { parseDomain, parseUrlDomain } from '../lib/util/hostname.js'
|
33
|
+
import { Awaitable } from '../lib/util/type.js'
|
34
|
+
import { OAuthHooks } from '../oauth-hooks.js'
|
35
|
+
import { ClientId } from './client-id.js'
|
36
|
+
import { ClientStore } from './client-store.js'
|
37
|
+
import { parseDiscoverableClientId, parseRedirectUri } from './client-utils.js'
|
38
|
+
import { Client } from './client.js'
|
39
|
+
|
40
|
+
const fetchMetadataHandler = pipe(
|
41
|
+
fetchOkProcessor(),
|
42
|
+
fetchJsonProcessor('application/json', false),
|
43
|
+
fetchJsonZodProcessor(oauthClientMetadataSchema),
|
44
|
+
)
|
45
|
+
|
46
|
+
const fetchJwksHandler = pipe(
|
47
|
+
fetchOkProcessor(),
|
48
|
+
fetchJsonProcessor('application/json', false),
|
49
|
+
fetchJsonZodProcessor(jwksSchema),
|
50
|
+
)
|
51
|
+
|
52
|
+
export type LoopbackMetadataGetter = (
|
53
|
+
url: string,
|
54
|
+
) => Awaitable<OAuthClientMetadataInput>
|
55
|
+
|
56
|
+
export class ClientManager {
|
57
|
+
protected readonly jwks: CachedGetter<string, Jwks>
|
58
|
+
protected readonly metadata: CachedGetter<string, OAuthClientMetadata>
|
59
|
+
|
60
|
+
constructor(
|
61
|
+
protected readonly keyset: Keyset,
|
62
|
+
protected readonly hooks: OAuthHooks,
|
63
|
+
protected readonly store: ClientStore | null,
|
64
|
+
protected readonly loopbackMetadata: LoopbackMetadataGetter | null = null,
|
65
|
+
safeFetch: Fetch,
|
66
|
+
clientJwksCache: SimpleStore<string, Jwks>,
|
67
|
+
clientMetadataCache: SimpleStore<string, OAuthClientMetadata>,
|
68
|
+
) {
|
69
|
+
const fetch = bindFetch(safeFetch)
|
70
|
+
|
71
|
+
this.jwks = new CachedGetter(async (uri, options) => {
|
72
|
+
const jwks = await fetch(buildJsonGetRequest(uri, options)).then(
|
73
|
+
fetchJwksHandler,
|
74
|
+
)
|
75
|
+
|
76
|
+
return jwks
|
77
|
+
}, clientJwksCache)
|
78
|
+
|
79
|
+
this.metadata = new CachedGetter(async (uri, options) => {
|
80
|
+
const metadata = await fetch(buildJsonGetRequest(uri, options)).then(
|
81
|
+
fetchMetadataHandler,
|
82
|
+
)
|
83
|
+
|
84
|
+
// Validate within the getter to avoid caching invalid metadata
|
85
|
+
return this.validateClientMetadata(uri, metadata)
|
86
|
+
}, clientMetadataCache)
|
87
|
+
}
|
88
|
+
|
89
|
+
/**
|
90
|
+
*
|
91
|
+
* @see {@link https://openid.net/specs/openid-connect-registration-1_0.html#rfc.section.2 OIDC Client Registration}
|
92
|
+
*/
|
93
|
+
public async getClient(clientId: string) {
|
94
|
+
try {
|
95
|
+
const metadata = await this.getClientMetadata(clientId)
|
96
|
+
|
97
|
+
const jwks = metadata.jwks_uri
|
98
|
+
? await this.jwks.get(metadata.jwks_uri)
|
99
|
+
: undefined
|
100
|
+
|
101
|
+
const partialInfo = await this.hooks.onClientInfo?.(clientId, {
|
102
|
+
metadata,
|
103
|
+
jwks,
|
104
|
+
})
|
105
|
+
|
106
|
+
const isFirstParty = partialInfo?.isFirstParty ?? false
|
107
|
+
const isTrusted =
|
108
|
+
partialInfo?.isTrusted ??
|
109
|
+
(isFirstParty ||
|
110
|
+
// If the client was loaded from the store, we consider it trusted:
|
111
|
+
(!isOAuthClientIdLoopback(clientId) &&
|
112
|
+
!isOAuthClientIdDiscoverable(clientId)))
|
113
|
+
|
114
|
+
return new Client(clientId, metadata, jwks, { isFirstParty, isTrusted })
|
115
|
+
} catch (err) {
|
116
|
+
if (err instanceof OAuthError) throw err
|
117
|
+
if (err?.['code'] === 'DEPTH_ZERO_SELF_SIGNED_CERT') {
|
118
|
+
throw new InvalidClientMetadataError('Self-signed certificate', err)
|
119
|
+
}
|
120
|
+
throw InvalidClientMetadataError.from(err)
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
protected async getClientMetadata(
|
125
|
+
clientId: ClientId,
|
126
|
+
): Promise<OAuthClientMetadata> {
|
127
|
+
if (isOAuthClientIdLoopback(clientId)) {
|
128
|
+
return this.getLoopbackClientMetadata(clientId)
|
129
|
+
} else if (isOAuthClientIdDiscoverable(clientId)) {
|
130
|
+
return this.getDiscoverableClientMetadata(clientId)
|
131
|
+
} else if (this.store) {
|
132
|
+
return this.getStoredClientMetadata(clientId)
|
133
|
+
}
|
134
|
+
|
135
|
+
throw new InvalidClientMetadataError(`Invalid client ID "${clientId}"`)
|
136
|
+
}
|
137
|
+
|
138
|
+
protected async getLoopbackClientMetadata(
|
139
|
+
clientId: OAuthClientIdLoopback,
|
140
|
+
): Promise<OAuthClientMetadata> {
|
141
|
+
const { loopbackMetadata } = this
|
142
|
+
if (!loopbackMetadata) {
|
143
|
+
throw new InvalidClientMetadataError('Loopback clients are not allowed')
|
144
|
+
}
|
145
|
+
|
146
|
+
const result = oauthClientMetadataSchema.safeParse(
|
147
|
+
await loopbackMetadata(clientId),
|
148
|
+
)
|
149
|
+
|
150
|
+
if (!result.success) {
|
151
|
+
throw InvalidClientMetadataError.from(result.error)
|
152
|
+
}
|
153
|
+
|
154
|
+
return this.validateClientMetadata(clientId, result.data)
|
155
|
+
}
|
156
|
+
|
157
|
+
protected async getDiscoverableClientMetadata(
|
158
|
+
clientId: OAuthClientIdDiscoverable,
|
159
|
+
): Promise<OAuthClientMetadata> {
|
160
|
+
const metadataUrl = parseDiscoverableClientId(clientId)
|
161
|
+
|
162
|
+
const metadata = await this.metadata.get(metadataUrl.href)
|
163
|
+
|
164
|
+
// Note: we do *not* re-validate the metadata here, as the metadata is
|
165
|
+
// validated within the getter. This is to avoid double validation.
|
166
|
+
//
|
167
|
+
// return this.validateClientMetadata(metadataUrl.href, metadata)
|
168
|
+
return metadata
|
169
|
+
}
|
170
|
+
|
171
|
+
protected async getStoredClientMetadata(
|
172
|
+
clientId: ClientId,
|
173
|
+
): Promise<OAuthClientMetadata> {
|
174
|
+
if (this.store) {
|
175
|
+
const metadata = await this.store.findClient(clientId)
|
176
|
+
return this.validateClientMetadata(clientId, metadata)
|
177
|
+
}
|
178
|
+
|
179
|
+
throw new InvalidClientMetadataError(`Invalid client ID "${clientId}"`)
|
180
|
+
}
|
181
|
+
|
182
|
+
/**
|
183
|
+
* This method will ensure that the client metadata is valid w.r.t. the OAuth
|
184
|
+
* and OIDC specifications. It will also ensure that the metadata is
|
185
|
+
* compatible with the implementation of this library, and ATPROTO's
|
186
|
+
* requirements.
|
187
|
+
*/
|
188
|
+
protected validateClientMetadata(
|
189
|
+
clientId: ClientId,
|
190
|
+
metadata: OAuthClientMetadata,
|
191
|
+
): OAuthClientMetadata {
|
192
|
+
if (metadata.jwks && metadata.jwks_uri) {
|
193
|
+
throw new InvalidClientMetadataError(
|
194
|
+
'jwks_uri and jwks are mutually exclusive',
|
195
|
+
)
|
196
|
+
}
|
197
|
+
|
198
|
+
const clientUriUrl = metadata.client_uri
|
199
|
+
? new URL(metadata.client_uri)
|
200
|
+
: null
|
201
|
+
const clientUriParsed = clientUriUrl ? parseUrlDomain(clientUriUrl) : null
|
202
|
+
|
203
|
+
if (clientUriUrl && !clientUriParsed) {
|
204
|
+
throw new InvalidClientMetadataError('client_uri must be a valid URL')
|
205
|
+
}
|
206
|
+
|
207
|
+
const scopes = metadata.scope?.split(' ')
|
208
|
+
if (
|
209
|
+
metadata.grant_types.includes('refresh_token') !==
|
210
|
+
(scopes?.includes('offline_access') ?? false)
|
211
|
+
) {
|
212
|
+
throw new InvalidClientMetadataError(
|
213
|
+
'Grant type "refresh_token" requires scope "offline_access" (and vice versa)',
|
214
|
+
)
|
215
|
+
}
|
216
|
+
|
217
|
+
for (const grantType of metadata.grant_types) {
|
218
|
+
switch (grantType) {
|
219
|
+
case 'authorization_code':
|
220
|
+
case 'refresh_token':
|
221
|
+
case 'implicit': // Required by OIDC (for id_token)
|
222
|
+
continue
|
223
|
+
case 'password':
|
224
|
+
throw new InvalidClientMetadataError(
|
225
|
+
`Grant type "${grantType}" is not allowed`,
|
226
|
+
)
|
227
|
+
default:
|
228
|
+
throw new InvalidClientMetadataError(
|
229
|
+
`Grant type "${grantType}" is not supported`,
|
230
|
+
)
|
231
|
+
}
|
232
|
+
}
|
233
|
+
|
234
|
+
if (metadata.client_id && metadata.client_id !== clientId) {
|
235
|
+
throw new InvalidClientMetadataError('client_id does not match')
|
236
|
+
}
|
237
|
+
|
238
|
+
if (metadata.subject_type && metadata.subject_type !== 'public') {
|
239
|
+
throw new InvalidClientMetadataError(
|
240
|
+
'Only "public" subject_type is supported',
|
241
|
+
)
|
242
|
+
}
|
243
|
+
|
244
|
+
if (
|
245
|
+
metadata.userinfo_signed_response_alg &&
|
246
|
+
!this.keyset.signAlgorithms.includes(
|
247
|
+
metadata.userinfo_signed_response_alg,
|
248
|
+
)
|
249
|
+
) {
|
250
|
+
throw new InvalidClientMetadataError(
|
251
|
+
`Unsupported "userinfo_signed_response_alg" ${metadata.userinfo_signed_response_alg}`,
|
252
|
+
)
|
253
|
+
}
|
254
|
+
|
255
|
+
if (
|
256
|
+
metadata.id_token_signed_response_alg &&
|
257
|
+
!this.keyset.signAlgorithms.includes(
|
258
|
+
metadata.id_token_signed_response_alg,
|
259
|
+
)
|
260
|
+
) {
|
261
|
+
throw new InvalidClientMetadataError(
|
262
|
+
`Unsupported "id_token_signed_response_alg" ${metadata.id_token_signed_response_alg}`,
|
263
|
+
)
|
264
|
+
}
|
265
|
+
|
266
|
+
if (metadata.userinfo_encrypted_response_alg) {
|
267
|
+
// We only support signature for now.
|
268
|
+
throw new InvalidClientMetadataError(
|
269
|
+
'Encrypted userinfo response is not supported',
|
270
|
+
)
|
271
|
+
}
|
272
|
+
|
273
|
+
if (!metadata[`token_endpoint_auth_method`]) {
|
274
|
+
throw new InvalidClientMetadataError(
|
275
|
+
'Missing token_endpoint_auth_method client metadata',
|
276
|
+
)
|
277
|
+
}
|
278
|
+
|
279
|
+
for (const endpoint of OAUTH_AUTHENTICATED_ENDPOINT_NAMES) {
|
280
|
+
const method =
|
281
|
+
metadata[`${endpoint}_endpoint_auth_method`] ||
|
282
|
+
metadata[`token_endpoint_auth_method`]
|
283
|
+
|
284
|
+
switch (method) {
|
285
|
+
case 'none':
|
286
|
+
if (metadata.token_endpoint_auth_signing_alg) {
|
287
|
+
throw new InvalidClientMetadataError(
|
288
|
+
`${endpoint}_endpoint_auth_method "none" must not have ${endpoint}_endpoint_auth_signing_alg`,
|
289
|
+
)
|
290
|
+
}
|
291
|
+
break
|
292
|
+
|
293
|
+
case 'private_key_jwt':
|
294
|
+
if (!metadata.jwks && !metadata.jwks_uri) {
|
295
|
+
throw new InvalidClientMetadataError(
|
296
|
+
`private_key_jwt auth method requires jwks or jwks_uri`,
|
297
|
+
)
|
298
|
+
}
|
299
|
+
if (metadata.jwks?.keys.length === 0) {
|
300
|
+
throw new InvalidClientMetadataError(
|
301
|
+
`private_key_jwt auth method requires at least one key in jwks`,
|
302
|
+
)
|
303
|
+
}
|
304
|
+
if (!metadata.token_endpoint_auth_signing_alg) {
|
305
|
+
throw new InvalidClientMetadataError(
|
306
|
+
`Missing token_endpoint_auth_signing_alg client metadata`,
|
307
|
+
)
|
308
|
+
}
|
309
|
+
break
|
310
|
+
|
311
|
+
default:
|
312
|
+
throw new InvalidClientMetadataError(
|
313
|
+
`${method} is not a supported "${endpoint}_endpoint_auth_method". Use "private_key_jwt" or "none".`,
|
314
|
+
)
|
315
|
+
}
|
316
|
+
}
|
317
|
+
|
318
|
+
if (metadata.authorization_encrypted_response_enc) {
|
319
|
+
throw new InvalidClientMetadataError(
|
320
|
+
'Encrypted authorization response is not supported',
|
321
|
+
)
|
322
|
+
}
|
323
|
+
|
324
|
+
if (metadata.tls_client_certificate_bound_access_tokens) {
|
325
|
+
throw new InvalidClientMetadataError(
|
326
|
+
'Mutual-TLS bound access tokens are not supported',
|
327
|
+
)
|
328
|
+
}
|
329
|
+
|
330
|
+
if (
|
331
|
+
metadata.authorization_encrypted_response_enc &&
|
332
|
+
!metadata.authorization_encrypted_response_alg
|
333
|
+
) {
|
334
|
+
throw new InvalidClientMetadataError(
|
335
|
+
'authorization_encrypted_response_enc requires authorization_encrypted_response_alg',
|
336
|
+
)
|
337
|
+
}
|
338
|
+
|
339
|
+
// ATPROTO spec requires the use of DPoP (OAuth spec defaults to false)
|
340
|
+
if (metadata.dpop_bound_access_tokens !== true) {
|
341
|
+
throw new InvalidClientMetadataError(
|
342
|
+
'"dpop_bound_access_tokens" must be true',
|
343
|
+
)
|
344
|
+
}
|
345
|
+
|
346
|
+
for (const responseType of metadata.response_types) {
|
347
|
+
const rt = responseType.split(' ')
|
348
|
+
|
349
|
+
// ATPROTO spec requires the use of PKCE
|
350
|
+
if (rt.includes('token')) {
|
351
|
+
throw new InvalidClientMetadataError(
|
352
|
+
'"token" response type is not compatible with PKCE (use "code" instead)',
|
353
|
+
)
|
354
|
+
}
|
355
|
+
|
356
|
+
// Consistency check
|
357
|
+
if (
|
358
|
+
rt.includes('code') &&
|
359
|
+
!metadata.grant_types.includes('authorization_code')
|
360
|
+
) {
|
361
|
+
throw new InvalidClientMetadataError(
|
362
|
+
`Response type "${responseType}" requires the "authorization_code" grant type`,
|
363
|
+
)
|
364
|
+
}
|
365
|
+
|
366
|
+
// Asking for "code token" or "code id_token" is fine (as long as the
|
367
|
+
// grant_types includes "authorization_code" and the scope includes
|
368
|
+
// "openid"). Asking for "token" or "id_token" (without "code") requires
|
369
|
+
// the "implicit" grant type.
|
370
|
+
if (
|
371
|
+
(rt.includes('token') || rt.includes('id_token')) &&
|
372
|
+
!metadata.grant_types.includes('implicit')
|
373
|
+
) {
|
374
|
+
throw new InvalidClientMetadataError(
|
375
|
+
`Response type "${responseType}" requires the "implicit" grant type`,
|
376
|
+
)
|
377
|
+
}
|
378
|
+
}
|
379
|
+
|
380
|
+
if (metadata.application_type === 'native') {
|
381
|
+
// https://datatracker.ietf.org/doc/html/rfc8252#section-8.4
|
382
|
+
//
|
383
|
+
// > Except when using a mechanism like Dynamic Client Registration
|
384
|
+
// > [RFC7591] to provision per-instance secrets, native apps are
|
385
|
+
// > classified as public clients, as defined by Section 2.1 of OAuth 2.0
|
386
|
+
// > [RFC6749]; they MUST be registered with the authorization server as
|
387
|
+
// > such. Authorization servers MUST record the client type in the
|
388
|
+
// > client registration details in order to identify and process requests
|
389
|
+
// > accordingly.
|
390
|
+
}
|
391
|
+
|
392
|
+
if (!metadata.redirect_uris?.length) {
|
393
|
+
// https://openid.net/specs/openid-connect-registration-1_0.html#rfc.section.2
|
394
|
+
//
|
395
|
+
// > OPs can require that request_uri values used be pre-registered with
|
396
|
+
// > the require_request_uri_registration discovery parameter.
|
397
|
+
|
398
|
+
throw new InvalidClientMetadataError(
|
399
|
+
'At least one redirect_uri is required',
|
400
|
+
)
|
401
|
+
}
|
402
|
+
|
403
|
+
if (
|
404
|
+
metadata.application_type === 'web' &&
|
405
|
+
metadata.grant_types.includes('implicit')
|
406
|
+
) {
|
407
|
+
// https://openid.net/specs/openid-connect-registration-1_0.html#rfc.section.2
|
408
|
+
//
|
409
|
+
// > Web Clients [as defined by "application_type"] using the OAuth
|
410
|
+
// > Implicit Grant Type MUST only register URLs using the https
|
411
|
+
// > scheme as redirect_uris; they MUST NOT use localhost as the
|
412
|
+
// > hostname.
|
413
|
+
|
414
|
+
for (const redirectUri of metadata.redirect_uris) {
|
415
|
+
const url = parseRedirectUri(redirectUri)
|
416
|
+
if (url.protocol !== 'https:') {
|
417
|
+
throw new InvalidRedirectUriError(
|
418
|
+
`Web clients must use HTTPS redirect URIs`,
|
419
|
+
)
|
420
|
+
}
|
421
|
+
|
422
|
+
if (url.hostname === 'localhost') {
|
423
|
+
throw new InvalidRedirectUriError(
|
424
|
+
`Web clients must not use localhost as the hostname`,
|
425
|
+
)
|
426
|
+
}
|
427
|
+
}
|
428
|
+
}
|
429
|
+
|
430
|
+
if (metadata.application_type === 'native') {
|
431
|
+
// https://openid.net/specs/openid-connect-registration-1_0.html#rfc.section.2
|
432
|
+
//
|
433
|
+
// > Native Clients [as defined by "application_type"] MUST only
|
434
|
+
// > register redirect_uris using custom URI schemes or loopback URLs
|
435
|
+
// > using the http scheme; loopback URLs use localhost or the IP
|
436
|
+
// > loopback literals 127.0.0.1 or [::1] as the hostname.
|
437
|
+
|
438
|
+
for (const redirectUri of metadata.redirect_uris) {
|
439
|
+
const url = parseRedirectUri(redirectUri)
|
440
|
+
if (url.protocol !== 'http:') {
|
441
|
+
throw new InvalidRedirectUriError(
|
442
|
+
`Native clients must use HTTP redirect URIs (got ${url})`,
|
443
|
+
)
|
444
|
+
}
|
445
|
+
|
446
|
+
if (!isLoopbackHost(url.hostname) && !isPrivateUseUriScheme(url)) {
|
447
|
+
throw new InvalidRedirectUriError(
|
448
|
+
'Loopback redirect URIs are only allowed for native apps',
|
449
|
+
)
|
450
|
+
}
|
451
|
+
}
|
452
|
+
}
|
453
|
+
|
454
|
+
if (metadata.application_type === 'native') {
|
455
|
+
// https://openid.net/specs/openid-connect-registration-1_0.html#rfc.section.2
|
456
|
+
//
|
457
|
+
// > Authorization Servers MAY reject Redirection URI values using
|
458
|
+
// > the http scheme, other than the loopback case for Native
|
459
|
+
// > Clients.
|
460
|
+
|
461
|
+
for (const redirectUri of metadata.redirect_uris) {
|
462
|
+
const url = parseRedirectUri(redirectUri)
|
463
|
+
if (url.protocol === 'http:' && !isLoopbackUrl(url)) {
|
464
|
+
throw new InvalidRedirectUriError(
|
465
|
+
`Native clients must not use HTTP redirect URIs (got ${url})`,
|
466
|
+
)
|
467
|
+
}
|
468
|
+
}
|
469
|
+
}
|
470
|
+
|
471
|
+
for (const redirectUri of metadata.redirect_uris) {
|
472
|
+
const url = parseRedirectUri(redirectUri)
|
473
|
+
|
474
|
+
if (url.username || url.password) {
|
475
|
+
// Is this a valid concern? Should we allow credentials in the URI?
|
476
|
+
throw new InvalidRedirectUriError(
|
477
|
+
`Redirect URI ${url} must not contain credentials`,
|
478
|
+
)
|
479
|
+
}
|
480
|
+
|
481
|
+
switch (true) {
|
482
|
+
// FIRST: Loopback redirect URI exception (only for native apps)
|
483
|
+
|
484
|
+
case url.hostname === 'localhost': {
|
485
|
+
// https://datatracker.ietf.org/doc/html/rfc8252#section-8.3
|
486
|
+
//
|
487
|
+
// > While redirect URIs using localhost (i.e.,
|
488
|
+
// > "http://localhost:{port}/{path}") function similarly to loopback IP
|
489
|
+
// > redirects described in Section 7.3, the use of localhost is NOT
|
490
|
+
// > RECOMMENDED. Specifying a redirect URI with the loopback IP literal
|
491
|
+
// > rather than localhost avoids inadvertently listening on network
|
492
|
+
// > interfaces other than the loopback interface. It is also less
|
493
|
+
// > susceptible to client-side firewalls and misconfigured host name
|
494
|
+
// > resolution on the user's device.
|
495
|
+
throw new InvalidRedirectUriError(
|
496
|
+
`Loopback redirect URI ${url} is not allowed (use explicit IPs instead)`,
|
497
|
+
)
|
498
|
+
}
|
499
|
+
// falls through
|
500
|
+
case url.hostname === '127.0.0.1':
|
501
|
+
case url.hostname === '[::1]': {
|
502
|
+
// https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
|
503
|
+
//
|
504
|
+
// > Loopback redirect URIs use the "http" scheme and are constructed
|
505
|
+
// > with the loopback IP literal and whatever port the client is
|
506
|
+
// > listening on. That is, "http://127.0.0.1:{port}/{path}" for IPv4,
|
507
|
+
// > and "http://[::1]:{port}/{path}" for IPv6.
|
508
|
+
|
509
|
+
if (metadata.application_type !== 'native') {
|
510
|
+
throw new InvalidRedirectUriError(
|
511
|
+
`Loopback redirect URIs are only allowed for native apps`,
|
512
|
+
)
|
513
|
+
}
|
514
|
+
|
515
|
+
if (url.port) {
|
516
|
+
// https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
|
517
|
+
//
|
518
|
+
// > The authorization server MUST allow any port to be specified at
|
519
|
+
// > the time of the request for loopback IP redirect URIs, to
|
520
|
+
// > accommodate clients that obtain an available ephemeral port
|
521
|
+
// > from the operating system at the time of the request.
|
522
|
+
//
|
523
|
+
// Note: although validation of the redirect_uri will ignore the
|
524
|
+
// port we still allow it to be specified, as the spec does not
|
525
|
+
// forbid it. If a port number is specified, ports will need to
|
526
|
+
// match when validating authorization requests. See
|
527
|
+
// "compareRedirectUri()".
|
528
|
+
}
|
529
|
+
|
530
|
+
if (url.protocol !== 'http:') {
|
531
|
+
throw new InvalidRedirectUriError(
|
532
|
+
`Loopback redirect URI ${url} must use HTTP`,
|
533
|
+
)
|
534
|
+
}
|
535
|
+
|
536
|
+
break
|
537
|
+
}
|
538
|
+
|
539
|
+
// SECOND: Protocol-based URI Redirection
|
540
|
+
|
541
|
+
case url.protocol === 'http:': {
|
542
|
+
// https://openid.net/specs/openid-connect-registration-1_0.html#rfc.section.2
|
543
|
+
//
|
544
|
+
// > request_uri [...] URLs MUST use the https scheme unless the
|
545
|
+
// > target Request Object is signed in a way that is verifiable by
|
546
|
+
// > the OP.
|
547
|
+
//
|
548
|
+
// TODO: Should we allow this (and check for signed request objects)?
|
549
|
+
throw new InvalidRedirectUriError(
|
550
|
+
`Non loopback redirect URI ${url} must use HTTPS`,
|
551
|
+
)
|
552
|
+
}
|
553
|
+
|
554
|
+
case url.protocol === 'https:': {
|
555
|
+
const redirectUriDomain = parseUrlDomain(url)
|
556
|
+
if (!redirectUriDomain) {
|
557
|
+
throw new InvalidRedirectUriError(
|
558
|
+
`Redirect URI ${url} must be a valid URL`,
|
559
|
+
)
|
560
|
+
}
|
561
|
+
|
562
|
+
// https://datatracker.ietf.org/doc/html/rfc8252#section-8.4
|
563
|
+
//
|
564
|
+
// > In addition to the collision-resistant properties, requiring a
|
565
|
+
// > URI scheme based on a domain name that is under the control of
|
566
|
+
// > the app can help to prove ownership in the event of a dispute
|
567
|
+
// > where two apps claim the same private-use URI scheme (where one
|
568
|
+
// > app is acting maliciously).
|
569
|
+
//
|
570
|
+
// Although this only applies to "native" clients (extract being from
|
571
|
+
// rfc8252), we apply this rule to "web" clients as well.
|
572
|
+
if (!clientUriParsed) {
|
573
|
+
throw new InvalidClientMetadataError(
|
574
|
+
'client_uri is required for HTTPS redirect URIs',
|
575
|
+
)
|
576
|
+
} else {
|
577
|
+
if (redirectUriDomain.domain !== clientUriParsed.domain) {
|
578
|
+
throw new InvalidRedirectUriError(
|
579
|
+
`Redirect URI ${url} must be under the same domain as client_uri ${metadata.client_uri}`,
|
580
|
+
)
|
581
|
+
}
|
582
|
+
}
|
583
|
+
|
584
|
+
break
|
585
|
+
}
|
586
|
+
|
587
|
+
case isPrivateUseUriScheme(url): {
|
588
|
+
// https://datatracker.ietf.org/doc/html/rfc8252#section-7.1
|
589
|
+
//
|
590
|
+
// > When choosing a URI scheme to associate with the app, apps MUST
|
591
|
+
// > use a URI scheme based on a domain name under their control,
|
592
|
+
// > expressed in reverse order, as recommended by Section 3.8 of
|
593
|
+
// > [RFC7595] for private-use URI schemes.
|
594
|
+
|
595
|
+
if (metadata.application_type !== 'native') {
|
596
|
+
throw new InvalidRedirectUriError(
|
597
|
+
`Private-Use URI Scheme redirect URI are only allowed for native apps`,
|
598
|
+
)
|
599
|
+
}
|
600
|
+
|
601
|
+
const redirectUriDomain = parseDomain(
|
602
|
+
reverseDomain(url.protocol.slice(0, -1)),
|
603
|
+
)
|
604
|
+
|
605
|
+
if (!redirectUriDomain) {
|
606
|
+
throw new InvalidRedirectUriError(
|
607
|
+
`Private-use URI Scheme redirect URI must be based on a valid domain name`,
|
608
|
+
)
|
609
|
+
}
|
610
|
+
|
611
|
+
// https://datatracker.ietf.org/doc/html/rfc8252#section-8.4
|
612
|
+
//
|
613
|
+
// > In addition to the collision-resistant properties, requiring a
|
614
|
+
// > URI scheme based on a domain name that is under the control of
|
615
|
+
// > the app can help to prove ownership in the event of a dispute
|
616
|
+
// > where two apps claim the same private-use URI scheme (where one
|
617
|
+
// > app is acting maliciously).
|
618
|
+
if (!clientUriParsed) {
|
619
|
+
throw new InvalidClientMetadataError(
|
620
|
+
'client_uri is required for native apps using private-use URI Scheme redirect URIs',
|
621
|
+
)
|
622
|
+
} else {
|
623
|
+
if (redirectUriDomain.domain !== clientUriParsed.domain) {
|
624
|
+
throw new InvalidRedirectUriError(
|
625
|
+
`Private-Use URI Scheme redirect URI ${url} must be under the same domain as client_uri ${metadata.client_uri}`,
|
626
|
+
)
|
627
|
+
}
|
628
|
+
}
|
629
|
+
|
630
|
+
// https://datatracker.ietf.org/doc/html/rfc8252#section-7.1
|
631
|
+
//
|
632
|
+
// > Following the requirements of Section 3.2 of [RFC3986], as there
|
633
|
+
// > is no naming authority for private-use URI scheme redirects, only
|
634
|
+
// > a single slash ("/") appears after the scheme component.
|
635
|
+
if (
|
636
|
+
url.href.startsWith(`${url.protocol}//`) ||
|
637
|
+
url.username ||
|
638
|
+
url.password ||
|
639
|
+
url.hostname ||
|
640
|
+
url.port
|
641
|
+
) {
|
642
|
+
throw new InvalidRedirectUriError(
|
643
|
+
`Private-Use URI Scheme must be in the form ${url.protocol}/<path>`,
|
644
|
+
)
|
645
|
+
}
|
646
|
+
|
647
|
+
break
|
648
|
+
}
|
649
|
+
|
650
|
+
default:
|
651
|
+
// https://datatracker.ietf.org/doc/html/rfc8252#section-8.4
|
652
|
+
//
|
653
|
+
// > At a minimum, any private-use URI scheme that doesn't contain a
|
654
|
+
// > period character (".") SHOULD be rejected.
|
655
|
+
throw new InvalidRedirectUriError(
|
656
|
+
`Invalid redirect URI scheme "${url.protocol}"`,
|
657
|
+
)
|
658
|
+
}
|
659
|
+
}
|
660
|
+
|
661
|
+
if (isOAuthClientIdLoopback(clientId)) {
|
662
|
+
return this.validateLoopbackClientMetadata(clientId, metadata)
|
663
|
+
} else if (isOAuthClientIdDiscoverable(clientId)) {
|
664
|
+
return this.validateDiscoverableClientMetadata(clientId, metadata)
|
665
|
+
} else {
|
666
|
+
return metadata
|
667
|
+
}
|
668
|
+
}
|
669
|
+
|
670
|
+
validateLoopbackClientMetadata(
|
671
|
+
clientId: OAuthClientIdLoopback,
|
672
|
+
metadata: OAuthClientMetadata,
|
673
|
+
): OAuthClientMetadata {
|
674
|
+
if (metadata.client_uri) {
|
675
|
+
throw new InvalidClientMetadataError(
|
676
|
+
'client_uri is not allowed for loopback clients',
|
677
|
+
)
|
678
|
+
}
|
679
|
+
|
680
|
+
if (metadata.application_type !== 'native') {
|
681
|
+
throw new InvalidClientMetadataError(
|
682
|
+
'Loopback clients must have application_type "native"',
|
683
|
+
)
|
684
|
+
}
|
685
|
+
|
686
|
+
if (
|
687
|
+
!ALLOW_LOOPBACK_CLIENT_REFRESH_TOKEN &&
|
688
|
+
metadata.grant_types.includes('refresh_token')
|
689
|
+
) {
|
690
|
+
throw new InvalidClientMetadataError(
|
691
|
+
'Loopback clients are not allowed to use the "refresh_token" grant type',
|
692
|
+
)
|
693
|
+
}
|
694
|
+
|
695
|
+
for (const endpoint of OAUTH_AUTHENTICATED_ENDPOINT_NAMES) {
|
696
|
+
const method =
|
697
|
+
metadata[`${endpoint}_endpoint_auth_method`] ||
|
698
|
+
metadata[`token_endpoint_auth_method`]
|
699
|
+
|
700
|
+
if (method !== 'none') {
|
701
|
+
throw new InvalidClientMetadataError(
|
702
|
+
`Loopback clients are not allowed to use "${endpoint}_endpoint_auth_method" ${method}`,
|
703
|
+
)
|
704
|
+
}
|
705
|
+
}
|
706
|
+
|
707
|
+
for (const redirectUri of metadata.redirect_uris) {
|
708
|
+
const url = parseRedirectUri(redirectUri)
|
709
|
+
|
710
|
+
if (url.protocol !== 'http:') {
|
711
|
+
throw new InvalidRedirectUriError(
|
712
|
+
`Loopback clients must use HTTP redirect URIs`,
|
713
|
+
)
|
714
|
+
}
|
715
|
+
|
716
|
+
if (!isLoopbackHost(url.hostname)) {
|
717
|
+
throw new InvalidRedirectUriError(
|
718
|
+
`Loopback clients must use loopback redirect URIs`,
|
719
|
+
)
|
720
|
+
}
|
721
|
+
}
|
722
|
+
|
723
|
+
return metadata
|
724
|
+
}
|
725
|
+
|
726
|
+
validateDiscoverableClientMetadata(
|
727
|
+
clientId: OAuthClientIdDiscoverable,
|
728
|
+
metadata: OAuthClientMetadata,
|
729
|
+
): OAuthClientMetadata {
|
730
|
+
if (!metadata.client_id) {
|
731
|
+
// https://drafts.aaronpk.com/draft-parecki-oauth-client-id-metadata-document/draft-parecki-oauth-client-id-metadata-document.html
|
732
|
+
throw new InvalidClientMetadataError(
|
733
|
+
`client_id is required for discoverable clients`,
|
734
|
+
)
|
735
|
+
}
|
736
|
+
|
737
|
+
const clientIdUrl = parseDiscoverableClientId(clientId)
|
738
|
+
|
739
|
+
if (metadata.client_uri) {
|
740
|
+
// https://drafts.aaronpk.com/draft-parecki-oauth-client-id-metadata-document/draft-parecki-oauth-client-id-metadata-document.html
|
741
|
+
//
|
742
|
+
// The client_uri must be a parent of the client_id URL. This might be
|
743
|
+
// relaxed in the future.
|
744
|
+
|
745
|
+
const clientUriUrl = new URL(metadata.client_uri)
|
746
|
+
|
747
|
+
if (clientUriUrl.origin !== clientIdUrl.origin) {
|
748
|
+
throw new InvalidClientMetadataError(
|
749
|
+
`client_uri must have the same origin as the client_id`,
|
750
|
+
)
|
751
|
+
}
|
752
|
+
|
753
|
+
if (clientIdUrl.pathname !== clientUriUrl.pathname) {
|
754
|
+
if (
|
755
|
+
!clientIdUrl.pathname.startsWith(
|
756
|
+
clientUriUrl.pathname.endsWith('/')
|
757
|
+
? clientUriUrl.pathname
|
758
|
+
: `${clientUriUrl.pathname}/`,
|
759
|
+
)
|
760
|
+
) {
|
761
|
+
throw new InvalidClientMetadataError(
|
762
|
+
`client_uri must be a parent URL of the client_id`,
|
763
|
+
)
|
764
|
+
}
|
765
|
+
}
|
766
|
+
}
|
767
|
+
|
768
|
+
for (const endpoint of OAUTH_AUTHENTICATED_ENDPOINT_NAMES) {
|
769
|
+
const method = metadata[`${endpoint}_endpoint_auth_method`]
|
770
|
+
switch (method) {
|
771
|
+
case 'client_secret_post':
|
772
|
+
case 'client_secret_basic':
|
773
|
+
case 'client_secret_jwt':
|
774
|
+
throw new InvalidClientMetadataError(
|
775
|
+
`Client authentication method "${method}" is not allowed for discoverable clients`,
|
776
|
+
)
|
777
|
+
}
|
778
|
+
}
|
779
|
+
|
780
|
+
for (const redirectUri of metadata.redirect_uris) {
|
781
|
+
const url = parseRedirectUri(redirectUri)
|
782
|
+
|
783
|
+
if (isPrivateUseUriScheme(url)) {
|
784
|
+
// https://drafts.aaronpk.com/draft-parecki-oauth-client-id-metadata-document/draft-parecki-oauth-client-id-metadata-document.html
|
785
|
+
//
|
786
|
+
// Fully qualified domain name (FQDN) of the client_id, in reverse
|
787
|
+
// order. This could be relaxed to allow same apex domain names, or
|
788
|
+
// parent domains, but for now we require an exact match.
|
789
|
+
const protocol = `${reverseDomain(clientIdUrl.hostname)}:`
|
790
|
+
if (url.protocol !== protocol) {
|
791
|
+
throw new InvalidRedirectUriError(
|
792
|
+
`Private-Use URI Scheme redirect URI, for discoverable client metadata, must be the fully qualified domain name (FQDN) of the client_id, in reverse order (${protocol})`,
|
793
|
+
)
|
794
|
+
}
|
795
|
+
}
|
796
|
+
}
|
797
|
+
|
798
|
+
return metadata
|
799
|
+
}
|
800
|
+
}
|
801
|
+
|
802
|
+
function reverseDomain(domain: string) {
|
803
|
+
return domain.split('.').reverse().join('.')
|
804
|
+
}
|
805
|
+
|
806
|
+
function isPrivateUseUriScheme(uri: URL) {
|
807
|
+
return uri.protocol.includes('.')
|
808
|
+
}
|
809
|
+
|
810
|
+
function buildJsonGetRequest(uri: string, options?: GetCachedOptions) {
|
811
|
+
const headers = new Headers([['accept', 'application/json']])
|
812
|
+
if (options?.noCache) headers.set('cache-control', 'no-cache')
|
813
|
+
return new Request(uri, {
|
814
|
+
headers,
|
815
|
+
signal: options?.signal,
|
816
|
+
redirect: 'error',
|
817
|
+
})
|
818
|
+
}
|