@atproto/oauth-provider 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|