@_mustachio/openauth 0.9.2 → 0.11.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/dist/esm/adapters/d1/audit-log.js +15 -0
- package/dist/esm/adapters/d1/config-store.js +45 -0
- package/dist/esm/adapters/d1/index.js +12 -0
- package/dist/esm/adapters/d1/key-store.js +116 -0
- package/dist/esm/adapters/d1/method-store.js +52 -0
- package/dist/esm/adapters/d1/migrations.js +77 -0
- package/dist/esm/adapters/d1/session-store.js +134 -0
- package/dist/esm/adapters/d1/session.js +18 -0
- package/dist/esm/adapters/d1/token-store.js +140 -0
- package/dist/esm/adapters/durable-object/index.js +3 -0
- package/dist/esm/adapters/durable-object/session-store.js +139 -0
- package/dist/esm/adapters/durable-object/types.js +0 -0
- package/dist/esm/adapters/dynamo/audit-log.js +23 -0
- package/dist/esm/adapters/dynamo/client.js +0 -0
- package/dist/esm/adapters/dynamo/config-store.js +57 -0
- package/dist/esm/adapters/dynamo/index.js +10 -0
- package/dist/esm/adapters/dynamo/key-store.js +188 -0
- package/dist/esm/adapters/dynamo/method-store.js +72 -0
- package/dist/esm/adapters/dynamo/passkey-credential-store.js +83 -0
- package/dist/esm/adapters/dynamo/sdk.js +128 -0
- package/dist/esm/adapters/dynamo/session-store.js +173 -0
- package/dist/esm/adapters/dynamo/token-store.js +178 -0
- package/dist/esm/adapters/kms/in-memory-backing.js +28 -0
- package/dist/esm/adapters/kms/index.js +5 -0
- package/dist/esm/adapters/kms/key-store.js +129 -0
- package/dist/esm/adapters/kms/sdk.js +23 -0
- package/dist/esm/adapters/kms/types.js +0 -0
- package/dist/esm/adapters/kv/audit-log.js +21 -0
- package/dist/esm/adapters/kv/config-store.js +43 -0
- package/dist/esm/adapters/kv/index.js +5 -0
- package/dist/esm/adapters/kv/method-store.js +63 -0
- package/dist/esm/adapters/kv/types.js +0 -0
- package/dist/esm/adapters/memory/audit-log.js +16 -0
- package/dist/esm/adapters/memory/clock.js +5 -0
- package/dist/esm/adapters/memory/config-store.js +31 -0
- package/dist/esm/adapters/memory/index.js +10 -0
- package/dist/esm/adapters/memory/key-store.js +78 -0
- package/dist/esm/adapters/memory/method-store.js +43 -0
- package/dist/esm/adapters/memory/passkey-credential-store.js +53 -0
- package/dist/esm/adapters/memory/session-store.js +104 -0
- package/dist/esm/adapters/memory/token-store.js +104 -0
- package/dist/esm/adapters/postgres/audit-log.js +16 -0
- package/dist/esm/adapters/postgres/config-store.js +48 -0
- package/dist/esm/adapters/postgres/executor.js +21 -0
- package/dist/esm/adapters/postgres/index.js +13 -0
- package/dist/esm/adapters/postgres/key-store.js +191 -0
- package/dist/esm/adapters/postgres/method-store.js +61 -0
- package/dist/esm/adapters/postgres/migrations.js +111 -0
- package/dist/esm/adapters/postgres/passkey-credential-store.js +80 -0
- package/dist/esm/adapters/postgres/session-store.js +135 -0
- package/dist/esm/adapters/postgres/token-store.js +151 -0
- package/dist/esm/client.js +35 -12
- package/dist/esm/domain/audit.js +13 -0
- package/dist/esm/domain/authorize.js +217 -0
- package/dist/esm/domain/callback.js +176 -0
- package/dist/esm/domain/client-auth.js +61 -0
- package/dist/esm/domain/client-credentials.js +91 -0
- package/dist/esm/domain/crypto.js +97 -0
- package/dist/esm/domain/discovery.js +83 -0
- package/dist/esm/domain/dpop.js +122 -0
- package/dist/esm/domain/id-token.js +94 -0
- package/dist/esm/domain/introspect.js +55 -0
- package/dist/esm/domain/jwt.js +71 -0
- package/dist/esm/domain/logout.js +75 -0
- package/dist/esm/domain/method-cache.js +136 -0
- package/dist/esm/domain/method-dispatch.js +36 -0
- package/dist/esm/domain/method-route.js +111 -0
- package/dist/esm/domain/par.js +51 -0
- package/dist/esm/domain/pkce.js +16 -0
- package/dist/esm/domain/refresh.js +102 -0
- package/dist/esm/domain/register.js +74 -0
- package/dist/esm/domain/revoke.js +81 -0
- package/dist/esm/domain/state-envelope.js +80 -0
- package/dist/esm/domain/token-exchange.js +102 -0
- package/dist/esm/domain/token.js +246 -0
- package/dist/esm/domain/userinfo.js +62 -0
- package/dist/esm/http/context.js +7 -0
- package/dist/esm/http/cookies.js +98 -0
- package/dist/esm/http/errors.js +71 -0
- package/dist/esm/http/handlers/authorize.js +250 -0
- package/dist/esm/http/handlers/callback.js +53 -0
- package/dist/esm/http/handlers/end-session.js +64 -0
- package/dist/esm/http/handlers/metadata.js +37 -0
- package/dist/esm/http/handlers/method-route.js +87 -0
- package/dist/esm/http/handlers/par.js +53 -0
- package/dist/esm/http/handlers/register.js +46 -0
- package/dist/esm/http/handlers/revocation.js +98 -0
- package/dist/esm/http/handlers/token.js +197 -0
- package/dist/esm/http/handlers/userinfo.js +60 -0
- package/dist/esm/http/middleware/error.js +15 -0
- package/dist/esm/http/middleware/tenant.js +102 -0
- package/dist/esm/http/resolver.js +15 -0
- package/dist/esm/http/router.js +45 -0
- package/dist/esm/http/schemas/authorize.js +30 -0
- package/dist/esm/http/schemas/end-session.js +13 -0
- package/dist/esm/http/schemas/par.js +9 -0
- package/dist/esm/http/schemas/register.js +16 -0
- package/dist/esm/http/schemas/revocation.js +18 -0
- package/dist/esm/http/schemas/token.js +57 -0
- package/dist/esm/index.js +126 -9
- package/dist/esm/methods/code.js +221 -0
- package/dist/esm/methods/m2m.js +41 -0
- package/dist/esm/methods/oauth2-factory.js +84 -0
- package/dist/esm/methods/oauth2-generic.js +196 -0
- package/dist/esm/methods/oidc-generic.js +53 -0
- package/dist/esm/methods/passkey.js +275 -0
- package/dist/esm/methods/password-hash.js +79 -0
- package/dist/esm/methods/password.js +166 -0
- package/dist/esm/methods/providers/apple.js +29 -0
- package/dist/esm/methods/providers/cognito.js +37 -0
- package/dist/esm/methods/providers/discord.js +28 -0
- package/dist/esm/methods/providers/facebook.js +42 -0
- package/dist/esm/methods/providers/github.js +29 -0
- package/dist/esm/methods/providers/google.js +29 -0
- package/dist/esm/methods/providers/index.js +17 -0
- package/dist/esm/methods/providers/jumpcloud.js +27 -0
- package/dist/esm/methods/providers/keycloak.js +32 -0
- package/dist/esm/methods/providers/linkedin.js +28 -0
- package/dist/esm/methods/providers/microsoft.js +28 -0
- package/dist/esm/methods/providers/slack.js +30 -0
- package/dist/esm/methods/providers/spotify.js +28 -0
- package/dist/esm/methods/providers/twitch.js +28 -0
- package/dist/esm/methods/providers/x.js +28 -0
- package/dist/esm/methods/providers/yahoo.js +27 -0
- package/dist/esm/pkce.js +4 -16
- package/dist/esm/ports/audit-log.js +0 -0
- package/dist/esm/ports/config-store.js +0 -0
- package/dist/esm/ports/key-store.js +0 -0
- package/dist/esm/ports/method-store.js +0 -0
- package/dist/esm/ports/session-store.js +0 -0
- package/dist/esm/ports/token-store.js +0 -0
- package/dist/esm/types/authorization.js +0 -0
- package/dist/esm/types/error.js +70 -0
- package/dist/esm/types/flow.js +0 -0
- package/dist/esm/types/idp.js +0 -0
- package/dist/esm/types/method.js +0 -0
- package/dist/esm/types/picker.js +0 -0
- package/dist/esm/types/result.js +11 -0
- package/dist/esm/types/subject.js +0 -0
- package/dist/esm/types/tenant.js +5 -0
- package/dist/esm/types/token.js +0 -0
- package/dist/esm/ui/forms.js +253 -0
- package/dist/esm/ui/index.js +8 -0
- package/dist/esm/ui/picker.js +49 -0
- package/dist/types/adapters/d1/audit-log.d.ts +19 -0
- package/dist/types/adapters/d1/audit-log.d.ts.map +1 -0
- package/dist/types/adapters/d1/config-store.d.ts +20 -0
- package/dist/types/adapters/d1/config-store.d.ts.map +1 -0
- package/dist/types/adapters/d1/index.d.ts +35 -0
- package/dist/types/adapters/d1/index.d.ts.map +1 -0
- package/dist/types/adapters/d1/key-store.d.ts +17 -0
- package/dist/types/adapters/d1/key-store.d.ts.map +1 -0
- package/dist/types/adapters/d1/method-store.d.ts +21 -0
- package/dist/types/adapters/d1/method-store.d.ts.map +1 -0
- package/dist/types/adapters/d1/migrations.d.ts +18 -0
- package/dist/types/adapters/d1/migrations.d.ts.map +1 -0
- package/dist/types/adapters/d1/session-store.d.ts +20 -0
- package/dist/types/adapters/d1/session-store.d.ts.map +1 -0
- package/dist/types/adapters/d1/session.d.ts +18 -0
- package/dist/types/adapters/d1/session.d.ts.map +1 -0
- package/dist/types/adapters/d1/token-store.d.ts +29 -0
- package/dist/types/adapters/d1/token-store.d.ts.map +1 -0
- package/dist/types/adapters/d1/types.d.ts +43 -0
- package/dist/types/adapters/d1/types.d.ts.map +1 -0
- package/dist/types/adapters/durable-object/index.d.ts +17 -0
- package/dist/types/adapters/durable-object/index.d.ts.map +1 -0
- package/dist/types/adapters/durable-object/session-store.d.ts +21 -0
- package/dist/types/adapters/durable-object/session-store.d.ts.map +1 -0
- package/dist/types/adapters/durable-object/types.d.ts +23 -0
- package/dist/types/adapters/durable-object/types.d.ts.map +1 -0
- package/dist/types/adapters/dynamo/audit-log.d.ts +19 -0
- package/dist/types/adapters/dynamo/audit-log.d.ts.map +1 -0
- package/dist/types/adapters/dynamo/client.d.ts +111 -0
- package/dist/types/adapters/dynamo/client.d.ts.map +1 -0
- package/dist/types/adapters/dynamo/config-store.d.ts +21 -0
- package/dist/types/adapters/dynamo/config-store.d.ts.map +1 -0
- package/dist/types/adapters/dynamo/index.d.ts +60 -0
- package/dist/types/adapters/dynamo/index.d.ts.map +1 -0
- package/dist/types/adapters/dynamo/key-store.d.ts +23 -0
- package/dist/types/adapters/dynamo/key-store.d.ts.map +1 -0
- package/dist/types/adapters/dynamo/method-store.d.ts +20 -0
- package/dist/types/adapters/dynamo/method-store.d.ts.map +1 -0
- package/dist/types/adapters/dynamo/passkey-credential-store.d.ts +47 -0
- package/dist/types/adapters/dynamo/passkey-credential-store.d.ts.map +1 -0
- package/dist/types/adapters/dynamo/sdk.d.ts +23 -0
- package/dist/types/adapters/dynamo/sdk.d.ts.map +1 -0
- package/dist/types/adapters/dynamo/session-store.d.ts +20 -0
- package/dist/types/adapters/dynamo/session-store.d.ts.map +1 -0
- package/dist/types/adapters/dynamo/token-store.d.ts +29 -0
- package/dist/types/adapters/dynamo/token-store.d.ts.map +1 -0
- package/dist/types/adapters/kms/in-memory-backing.d.ts +9 -0
- package/dist/types/adapters/kms/in-memory-backing.d.ts.map +1 -0
- package/dist/types/adapters/kms/index.d.ts +35 -0
- package/dist/types/adapters/kms/index.d.ts.map +1 -0
- package/dist/types/adapters/kms/key-store.d.ts +20 -0
- package/dist/types/adapters/kms/key-store.d.ts.map +1 -0
- package/dist/types/adapters/kms/sdk.d.ts +16 -0
- package/dist/types/adapters/kms/sdk.d.ts.map +1 -0
- package/dist/types/adapters/kms/types.d.ts +58 -0
- package/dist/types/adapters/kms/types.d.ts.map +1 -0
- package/dist/types/adapters/kv/audit-log.d.ts +22 -0
- package/dist/types/adapters/kv/audit-log.d.ts.map +1 -0
- package/dist/types/adapters/kv/config-store.d.ts +25 -0
- package/dist/types/adapters/kv/config-store.d.ts.map +1 -0
- package/dist/types/adapters/kv/index.d.ts +21 -0
- package/dist/types/adapters/kv/index.d.ts.map +1 -0
- package/dist/types/adapters/kv/method-store.d.ts +23 -0
- package/dist/types/adapters/kv/method-store.d.ts.map +1 -0
- package/dist/types/adapters/kv/types.d.ts +28 -0
- package/dist/types/adapters/kv/types.d.ts.map +1 -0
- package/dist/types/adapters/memory/audit-log.d.ts +18 -0
- package/dist/types/adapters/memory/audit-log.d.ts.map +1 -0
- package/dist/types/adapters/memory/clock.d.ts +7 -0
- package/dist/types/adapters/memory/clock.d.ts.map +1 -0
- package/dist/types/adapters/memory/config-store.d.ts +19 -0
- package/dist/types/adapters/memory/config-store.d.ts.map +1 -0
- package/dist/types/adapters/memory/index.d.ts +15 -0
- package/dist/types/adapters/memory/index.d.ts.map +1 -0
- package/dist/types/adapters/memory/key-store.d.ts +17 -0
- package/dist/types/adapters/memory/key-store.d.ts.map +1 -0
- package/dist/types/adapters/memory/method-store.d.ts +26 -0
- package/dist/types/adapters/memory/method-store.d.ts.map +1 -0
- package/dist/types/adapters/memory/passkey-credential-store.d.ts +43 -0
- package/dist/types/adapters/memory/passkey-credential-store.d.ts.map +1 -0
- package/dist/types/adapters/memory/session-store.d.ts +21 -0
- package/dist/types/adapters/memory/session-store.d.ts.map +1 -0
- package/dist/types/adapters/memory/token-store.d.ts +29 -0
- package/dist/types/adapters/memory/token-store.d.ts.map +1 -0
- package/dist/types/adapters/postgres/audit-log.d.ts +18 -0
- package/dist/types/adapters/postgres/audit-log.d.ts.map +1 -0
- package/dist/types/adapters/postgres/config-store.d.ts +21 -0
- package/dist/types/adapters/postgres/config-store.d.ts.map +1 -0
- package/dist/types/adapters/postgres/executor.d.ts +48 -0
- package/dist/types/adapters/postgres/executor.d.ts.map +1 -0
- package/dist/types/adapters/postgres/index.d.ts +39 -0
- package/dist/types/adapters/postgres/index.d.ts.map +1 -0
- package/dist/types/adapters/postgres/key-store.d.ts +23 -0
- package/dist/types/adapters/postgres/key-store.d.ts.map +1 -0
- package/dist/types/adapters/postgres/method-store.d.ts +21 -0
- package/dist/types/adapters/postgres/method-store.d.ts.map +1 -0
- package/dist/types/adapters/postgres/migrations.d.ts +22 -0
- package/dist/types/adapters/postgres/migrations.d.ts.map +1 -0
- package/dist/types/adapters/postgres/passkey-credential-store.d.ts +48 -0
- package/dist/types/adapters/postgres/passkey-credential-store.d.ts.map +1 -0
- package/dist/types/adapters/postgres/session-store.d.ts +20 -0
- package/dist/types/adapters/postgres/session-store.d.ts.map +1 -0
- package/dist/types/adapters/postgres/token-store.d.ts +30 -0
- package/dist/types/adapters/postgres/token-store.d.ts.map +1 -0
- package/dist/types/client.d.ts +20 -1
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/domain/audit.d.ts +17 -0
- package/dist/types/domain/audit.d.ts.map +1 -0
- package/dist/types/domain/authorize.d.ts +125 -0
- package/dist/types/domain/authorize.d.ts.map +1 -0
- package/dist/types/domain/callback.d.ts +70 -0
- package/dist/types/domain/callback.d.ts.map +1 -0
- package/dist/types/domain/client-auth.d.ts +57 -0
- package/dist/types/domain/client-auth.d.ts.map +1 -0
- package/dist/types/domain/client-credentials.d.ts +54 -0
- package/dist/types/domain/client-credentials.d.ts.map +1 -0
- package/dist/types/domain/crypto.d.ts +53 -0
- package/dist/types/domain/crypto.d.ts.map +1 -0
- package/dist/types/domain/discovery.d.ts +81 -0
- package/dist/types/domain/discovery.d.ts.map +1 -0
- package/dist/types/domain/dpop.d.ts +61 -0
- package/dist/types/domain/dpop.d.ts.map +1 -0
- package/dist/types/domain/id-token.d.ts +83 -0
- package/dist/types/domain/id-token.d.ts.map +1 -0
- package/dist/types/domain/introspect.d.ts +79 -0
- package/dist/types/domain/introspect.d.ts.map +1 -0
- package/dist/types/domain/jwt.d.ts +74 -0
- package/dist/types/domain/jwt.d.ts.map +1 -0
- package/dist/types/domain/logout.d.ts +69 -0
- package/dist/types/domain/logout.d.ts.map +1 -0
- package/dist/types/domain/method-cache.d.ts +42 -0
- package/dist/types/domain/method-cache.d.ts.map +1 -0
- package/dist/types/domain/method-dispatch.d.ts +37 -0
- package/dist/types/domain/method-dispatch.d.ts.map +1 -0
- package/dist/types/domain/method-route.d.ts +77 -0
- package/dist/types/domain/method-route.d.ts.map +1 -0
- package/dist/types/domain/par.d.ts +62 -0
- package/dist/types/domain/par.d.ts.map +1 -0
- package/dist/types/domain/pkce.d.ts +15 -0
- package/dist/types/domain/pkce.d.ts.map +1 -0
- package/dist/types/domain/refresh.d.ts +52 -0
- package/dist/types/domain/refresh.d.ts.map +1 -0
- package/dist/types/domain/register.d.ts +33 -0
- package/dist/types/domain/register.d.ts.map +1 -0
- package/dist/types/domain/revoke.d.ts +61 -0
- package/dist/types/domain/revoke.d.ts.map +1 -0
- package/dist/types/domain/state-envelope.d.ts +30 -0
- package/dist/types/domain/state-envelope.d.ts.map +1 -0
- package/dist/types/domain/token-exchange.d.ts +69 -0
- package/dist/types/domain/token-exchange.d.ts.map +1 -0
- package/dist/types/domain/token.d.ts +140 -0
- package/dist/types/domain/token.d.ts.map +1 -0
- package/dist/types/domain/userinfo.d.ts +61 -0
- package/dist/types/domain/userinfo.d.ts.map +1 -0
- package/dist/types/http/context.d.ts +66 -0
- package/dist/types/http/context.d.ts.map +1 -0
- package/dist/types/http/cookies.d.ts +45 -0
- package/dist/types/http/cookies.d.ts.map +1 -0
- package/dist/types/http/errors.d.ts +36 -0
- package/dist/types/http/errors.d.ts.map +1 -0
- package/dist/types/http/handlers/authorize.d.ts +3 -0
- package/dist/types/http/handlers/authorize.d.ts.map +1 -0
- package/dist/types/http/handlers/callback.d.ts +3 -0
- package/dist/types/http/handlers/callback.d.ts.map +1 -0
- package/dist/types/http/handlers/end-session.d.ts +3 -0
- package/dist/types/http/handlers/end-session.d.ts.map +1 -0
- package/dist/types/http/handlers/metadata.d.ts +4 -0
- package/dist/types/http/handlers/metadata.d.ts.map +1 -0
- package/dist/types/http/handlers/method-route.d.ts +3 -0
- package/dist/types/http/handlers/method-route.d.ts.map +1 -0
- package/dist/types/http/handlers/par.d.ts +3 -0
- package/dist/types/http/handlers/par.d.ts.map +1 -0
- package/dist/types/http/handlers/register.d.ts +3 -0
- package/dist/types/http/handlers/register.d.ts.map +1 -0
- package/dist/types/http/handlers/revocation.d.ts +4 -0
- package/dist/types/http/handlers/revocation.d.ts.map +1 -0
- package/dist/types/http/handlers/token.d.ts +3 -0
- package/dist/types/http/handlers/token.d.ts.map +1 -0
- package/dist/types/http/handlers/userinfo.d.ts +3 -0
- package/dist/types/http/handlers/userinfo.d.ts.map +1 -0
- package/dist/types/http/middleware/error.d.ts +9 -0
- package/dist/types/http/middleware/error.d.ts.map +1 -0
- package/dist/types/http/middleware/tenant.d.ts +30 -0
- package/dist/types/http/middleware/tenant.d.ts.map +1 -0
- package/dist/types/http/resolver.d.ts +22 -0
- package/dist/types/http/resolver.d.ts.map +1 -0
- package/dist/types/http/router.d.ts +21 -0
- package/dist/types/http/router.d.ts.map +1 -0
- package/dist/types/http/schemas/authorize.d.ts +92 -0
- package/dist/types/http/schemas/authorize.d.ts.map +1 -0
- package/dist/types/http/schemas/end-session.d.ts +31 -0
- package/dist/types/http/schemas/end-session.d.ts.map +1 -0
- package/dist/types/http/schemas/par.d.ts +20 -0
- package/dist/types/http/schemas/par.d.ts.map +1 -0
- package/dist/types/http/schemas/register.d.ts +42 -0
- package/dist/types/http/schemas/register.d.ts.map +1 -0
- package/dist/types/http/schemas/revocation.d.ts +41 -0
- package/dist/types/http/schemas/revocation.d.ts.map +1 -0
- package/dist/types/http/schemas/token.d.ts +227 -0
- package/dist/types/http/schemas/token.d.ts.map +1 -0
- package/dist/types/index.d.ts +63 -13
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/methods/code.d.ts +96 -0
- package/dist/types/methods/code.d.ts.map +1 -0
- package/dist/types/methods/m2m.d.ts +41 -0
- package/dist/types/methods/m2m.d.ts.map +1 -0
- package/dist/types/methods/oauth2-factory.d.ts +133 -0
- package/dist/types/methods/oauth2-factory.d.ts.map +1 -0
- package/dist/types/methods/oauth2-generic.d.ts +75 -0
- package/dist/types/methods/oauth2-generic.d.ts.map +1 -0
- package/dist/types/methods/oidc-generic.d.ts +26 -0
- package/dist/types/methods/oidc-generic.d.ts.map +1 -0
- package/dist/types/methods/passkey.d.ts +106 -0
- package/dist/types/methods/passkey.d.ts.map +1 -0
- package/dist/types/methods/password-hash.d.ts +25 -0
- package/dist/types/methods/password-hash.d.ts.map +1 -0
- package/dist/types/methods/password.d.ts +70 -0
- package/dist/types/methods/password.d.ts.map +1 -0
- package/dist/types/methods/providers/apple.d.ts +29 -0
- package/dist/types/methods/providers/apple.d.ts.map +1 -0
- package/dist/types/methods/providers/cognito.d.ts +36 -0
- package/dist/types/methods/providers/cognito.d.ts.map +1 -0
- package/dist/types/methods/providers/discord.d.ts +22 -0
- package/dist/types/methods/providers/discord.d.ts.map +1 -0
- package/dist/types/methods/providers/facebook.d.ts +29 -0
- package/dist/types/methods/providers/facebook.d.ts.map +1 -0
- package/dist/types/methods/providers/github.d.ts +25 -0
- package/dist/types/methods/providers/github.d.ts.map +1 -0
- package/dist/types/methods/providers/google.d.ts +28 -0
- package/dist/types/methods/providers/google.d.ts.map +1 -0
- package/dist/types/methods/providers/index.d.ts +24 -0
- package/dist/types/methods/providers/index.d.ts.map +1 -0
- package/dist/types/methods/providers/jumpcloud.d.ts +22 -0
- package/dist/types/methods/providers/jumpcloud.d.ts.map +1 -0
- package/dist/types/methods/providers/keycloak.d.ts +30 -0
- package/dist/types/methods/providers/keycloak.d.ts.map +1 -0
- package/dist/types/methods/providers/linkedin.d.ts +22 -0
- package/dist/types/methods/providers/linkedin.d.ts.map +1 -0
- package/dist/types/methods/providers/microsoft.d.ts +30 -0
- package/dist/types/methods/providers/microsoft.d.ts.map +1 -0
- package/dist/types/methods/providers/slack.d.ts +25 -0
- package/dist/types/methods/providers/slack.d.ts.map +1 -0
- package/dist/types/methods/providers/spotify.d.ts +22 -0
- package/dist/types/methods/providers/spotify.d.ts.map +1 -0
- package/dist/types/methods/providers/twitch.d.ts +23 -0
- package/dist/types/methods/providers/twitch.d.ts.map +1 -0
- package/dist/types/methods/providers/x.d.ts +25 -0
- package/dist/types/methods/providers/x.d.ts.map +1 -0
- package/dist/types/methods/providers/yahoo.d.ts +22 -0
- package/dist/types/methods/providers/yahoo.d.ts.map +1 -0
- package/dist/types/pkce.d.ts +1 -2
- package/dist/types/pkce.d.ts.map +1 -1
- package/dist/types/ports/audit-log.d.ts +166 -0
- package/dist/types/ports/audit-log.d.ts.map +1 -0
- package/dist/types/ports/config-store.d.ts +32 -0
- package/dist/types/ports/config-store.d.ts.map +1 -0
- package/dist/types/ports/key-store.d.ts +98 -0
- package/dist/types/ports/key-store.d.ts.map +1 -0
- package/dist/types/ports/method-store.d.ts +29 -0
- package/dist/types/ports/method-store.d.ts.map +1 -0
- package/dist/types/ports/session-store.d.ts +109 -0
- package/dist/types/ports/session-store.d.ts.map +1 -0
- package/dist/types/ports/token-store.d.ts +105 -0
- package/dist/types/ports/token-store.d.ts.map +1 -0
- package/dist/types/types/authorization.d.ts +75 -0
- package/dist/types/types/authorization.d.ts.map +1 -0
- package/dist/types/types/error.d.ts +101 -0
- package/dist/types/types/error.d.ts.map +1 -0
- package/dist/types/types/flow.d.ts +97 -0
- package/dist/types/types/flow.d.ts.map +1 -0
- package/dist/types/types/idp.d.ts +324 -0
- package/dist/types/types/idp.d.ts.map +1 -0
- package/dist/types/types/method.d.ts +256 -0
- package/dist/types/types/method.d.ts.map +1 -0
- package/dist/types/types/picker.d.ts +31 -0
- package/dist/types/types/picker.d.ts.map +1 -0
- package/dist/types/types/result.d.ts +29 -0
- package/dist/types/types/result.d.ts.map +1 -0
- package/dist/types/types/subject.d.ts +47 -0
- package/dist/types/types/subject.d.ts.map +1 -0
- package/dist/types/types/tenant.d.ts +276 -0
- package/dist/types/types/tenant.d.ts.map +1 -0
- package/dist/types/types/token.d.ts +231 -0
- package/dist/types/types/token.d.ts.map +1 -0
- package/dist/types/ui/forms.d.ts +35 -0
- package/dist/types/ui/forms.d.ts.map +1 -0
- package/dist/types/ui/index.d.ts +14 -0
- package/dist/types/ui/index.d.ts.map +1 -0
- package/dist/types/ui/picker.d.ts +18 -0
- package/dist/types/ui/picker.d.ts.map +1 -0
- package/package.json +41 -6
- package/src/adapters/d1/audit-log.ts +32 -0
- package/src/adapters/d1/config-store.ts +67 -0
- package/src/adapters/d1/index.ts +41 -0
- package/src/adapters/d1/key-store.ts +218 -0
- package/src/adapters/d1/method-store.ts +103 -0
- package/src/adapters/d1/migrations.ts +89 -0
- package/src/adapters/d1/session-store.ts +230 -0
- package/src/adapters/d1/session.ts +38 -0
- package/src/adapters/d1/token-store.ts +251 -0
- package/src/adapters/d1/types.ts +53 -0
- package/src/adapters/durable-object/index.ts +23 -0
- package/src/adapters/durable-object/session-store.ts +227 -0
- package/src/adapters/durable-object/types.ts +25 -0
- package/src/adapters/dynamo/audit-log.ts +35 -0
- package/src/adapters/dynamo/client.ts +118 -0
- package/src/adapters/dynamo/config-store.ts +72 -0
- package/src/adapters/dynamo/index.ts +81 -0
- package/src/adapters/dynamo/key-store.ts +267 -0
- package/src/adapters/dynamo/method-store.ts +105 -0
- package/src/adapters/dynamo/passkey-credential-store.ts +132 -0
- package/src/adapters/dynamo/sdk.ts +189 -0
- package/src/adapters/dynamo/session-store.ts +229 -0
- package/src/adapters/dynamo/token-store.ts +248 -0
- package/src/adapters/kms/in-memory-backing.ts +36 -0
- package/src/adapters/kms/index.ts +39 -0
- package/src/adapters/kms/key-store.ts +176 -0
- package/src/adapters/kms/sdk.ts +39 -0
- package/src/adapters/kms/types.ts +61 -0
- package/src/adapters/kv/audit-log.ts +40 -0
- package/src/adapters/kv/config-store.ts +63 -0
- package/src/adapters/kv/index.ts +20 -0
- package/src/adapters/kv/method-store.ts +98 -0
- package/src/adapters/kv/types.ts +26 -0
- package/src/adapters/memory/audit-log.ts +28 -0
- package/src/adapters/memory/clock.ts +7 -0
- package/src/adapters/memory/config-store.ts +41 -0
- package/src/adapters/memory/index.ts +26 -0
- package/src/adapters/memory/key-store.ts +102 -0
- package/src/adapters/memory/method-store.ts +75 -0
- package/src/adapters/memory/passkey-credential-store.ts +97 -0
- package/src/adapters/memory/session-store.ts +171 -0
- package/src/adapters/memory/token-store.ts +181 -0
- package/src/adapters/postgres/audit-log.ts +34 -0
- package/src/adapters/postgres/config-store.ts +72 -0
- package/src/adapters/postgres/executor.ts +76 -0
- package/src/adapters/postgres/index.ts +60 -0
- package/src/adapters/postgres/key-store.ts +334 -0
- package/src/adapters/postgres/method-store.ts +104 -0
- package/src/adapters/postgres/migrations.ts +131 -0
- package/src/adapters/postgres/passkey-credential-store.ts +150 -0
- package/src/adapters/postgres/session-store.ts +216 -0
- package/src/adapters/postgres/token-store.ts +263 -0
- package/src/client.ts +82 -16
- package/src/domain/audit.ts +28 -0
- package/src/domain/authorize.ts +414 -0
- package/src/domain/callback.ts +268 -0
- package/src/domain/client-auth.ts +116 -0
- package/src/domain/client-credentials.ts +180 -0
- package/src/domain/crypto.ts +168 -0
- package/src/domain/discovery.ts +161 -0
- package/src/domain/dpop.ts +293 -0
- package/src/domain/id-token.ts +213 -0
- package/src/domain/introspect.ts +155 -0
- package/src/domain/jwt.ts +185 -0
- package/src/domain/logout.ts +180 -0
- package/src/domain/method-cache.ts +220 -0
- package/src/domain/method-dispatch.ts +95 -0
- package/src/domain/method-route.ts +213 -0
- package/src/domain/par.ts +141 -0
- package/src/domain/pkce.ts +37 -0
- package/src/domain/refresh.ts +210 -0
- package/src/domain/register.ts +168 -0
- package/src/domain/revoke.ts +161 -0
- package/src/domain/state-envelope.ts +142 -0
- package/src/domain/token-exchange.ts +243 -0
- package/src/domain/token.ts +483 -0
- package/src/domain/userinfo.ts +171 -0
- package/src/http/context.ts +90 -0
- package/src/http/cookies.ts +167 -0
- package/src/http/errors.ts +98 -0
- package/src/http/handlers/authorize.ts +362 -0
- package/src/http/handlers/callback.ts +77 -0
- package/src/http/handlers/end-session.ts +106 -0
- package/src/http/handlers/metadata.ts +49 -0
- package/src/http/handlers/method-route.ts +115 -0
- package/src/http/handlers/par.ts +84 -0
- package/src/http/handlers/register.ts +70 -0
- package/src/http/handlers/revocation.ts +151 -0
- package/src/http/handlers/token.ts +300 -0
- package/src/http/handlers/userinfo.ts +90 -0
- package/src/http/middleware/error.ts +26 -0
- package/src/http/middleware/tenant.ts +165 -0
- package/src/http/resolver.ts +34 -0
- package/src/http/router.ts +85 -0
- package/src/http/schemas/authorize.ts +85 -0
- package/src/http/schemas/end-session.ts +23 -0
- package/src/http/schemas/par.ts +23 -0
- package/src/http/schemas/register.ts +29 -0
- package/src/http/schemas/revocation.ts +23 -0
- package/src/http/schemas/token.ts +85 -0
- package/src/index.ts +283 -20
- package/src/methods/code.ts +359 -0
- package/src/methods/m2m.ts +82 -0
- package/src/methods/oauth2-factory.ts +151 -0
- package/src/methods/oauth2-generic.ts +360 -0
- package/src/methods/oidc-generic.ts +109 -0
- package/src/methods/passkey.ts +419 -0
- package/src/methods/password-hash.ts +134 -0
- package/src/methods/password.ts +298 -0
- package/src/methods/providers/apple.ts +42 -0
- package/src/methods/providers/cognito.ts +55 -0
- package/src/methods/providers/discord.ts +37 -0
- package/src/methods/providers/facebook.ts +54 -0
- package/src/methods/providers/github.ts +42 -0
- package/src/methods/providers/google.ts +43 -0
- package/src/methods/providers/index.ts +23 -0
- package/src/methods/providers/jumpcloud.ts +36 -0
- package/src/methods/providers/keycloak.ts +42 -0
- package/src/methods/providers/linkedin.ts +37 -0
- package/src/methods/providers/microsoft.ts +42 -0
- package/src/methods/providers/slack.ts +42 -0
- package/src/methods/providers/spotify.ts +37 -0
- package/src/methods/providers/twitch.ts +38 -0
- package/src/methods/providers/x.ts +40 -0
- package/src/methods/providers/yahoo.ts +36 -0
- package/src/pkce.ts +20 -21
- package/src/ports/CONSISTENCY.md +119 -0
- package/src/ports/audit-log.ts +177 -0
- package/src/ports/config-store.ts +34 -0
- package/src/ports/key-store.ts +106 -0
- package/src/ports/method-store.ts +41 -0
- package/src/ports/session-store.ts +131 -0
- package/src/ports/token-store.ts +116 -0
- package/src/types/authorization.ts +78 -0
- package/src/types/error.ts +146 -0
- package/src/types/flow.ts +99 -0
- package/src/types/idp.ts +365 -0
- package/src/types/method.ts +270 -0
- package/src/types/picker.ts +32 -0
- package/src/types/result.ts +23 -0
- package/src/types/subject.ts +50 -0
- package/src/types/tenant.ts +285 -0
- package/src/types/token.ts +236 -0
- package/src/ui/forms.ts +289 -0
- package/src/ui/index.ts +14 -0
- package/src/ui/picker.ts +63 -0
- package/dist/esm/issuer.js +0 -691
- package/dist/esm/jwt.js +0 -16
- package/dist/esm/keys.js +0 -113
- package/dist/esm/provider/apple.js +0 -28
- package/dist/esm/provider/arctic.js +0 -43
- package/dist/esm/provider/code.js +0 -58
- package/dist/esm/provider/cognito.js +0 -16
- package/dist/esm/provider/discord.js +0 -15
- package/dist/esm/provider/facebook.js +0 -24
- package/dist/esm/provider/github.js +0 -15
- package/dist/esm/provider/google.js +0 -25
- package/dist/esm/provider/index.js +0 -3
- package/dist/esm/provider/jumpcloud.js +0 -15
- package/dist/esm/provider/keycloak.js +0 -15
- package/dist/esm/provider/linkedin.js +0 -15
- package/dist/esm/provider/m2m.js +0 -16
- package/dist/esm/provider/microsoft.js +0 -24
- package/dist/esm/provider/oauth2.js +0 -119
- package/dist/esm/provider/oidc.js +0 -69
- package/dist/esm/provider/passkey.js +0 -315
- package/dist/esm/provider/password.js +0 -306
- package/dist/esm/provider/provider.js +0 -10
- package/dist/esm/provider/slack.js +0 -15
- package/dist/esm/provider/spotify.js +0 -15
- package/dist/esm/provider/twitch.js +0 -15
- package/dist/esm/provider/x.js +0 -16
- package/dist/esm/provider/yahoo.js +0 -15
- package/dist/esm/random.js +0 -27
- package/dist/esm/storage/aws.js +0 -39
- package/dist/esm/storage/cloudflare.js +0 -42
- package/dist/esm/storage/dynamo.js +0 -116
- package/dist/esm/storage/memory.js +0 -88
- package/dist/esm/storage/storage.js +0 -36
- package/dist/esm/subject.js +0 -7
- package/dist/esm/ui/base.js +0 -407
- package/dist/esm/ui/code.js +0 -151
- package/dist/esm/ui/form.js +0 -43
- package/dist/esm/ui/icon.js +0 -92
- package/dist/esm/ui/passkey.js +0 -329
- package/dist/esm/ui/password.js +0 -338
- package/dist/esm/ui/select.js +0 -187
- package/dist/esm/ui/theme.js +0 -115
- package/dist/esm/util.js +0 -54
- package/dist/types/issuer.d.ts +0 -607
- package/dist/types/issuer.d.ts.map +0 -1
- package/dist/types/jwt.d.ts +0 -6
- package/dist/types/jwt.d.ts.map +0 -1
- package/dist/types/keys.d.ts +0 -18
- package/dist/types/keys.d.ts.map +0 -1
- package/dist/types/provider/apple.d.ts +0 -108
- package/dist/types/provider/apple.d.ts.map +0 -1
- package/dist/types/provider/arctic.d.ts +0 -16
- package/dist/types/provider/arctic.d.ts.map +0 -1
- package/dist/types/provider/code.d.ts +0 -74
- package/dist/types/provider/code.d.ts.map +0 -1
- package/dist/types/provider/cognito.d.ts +0 -64
- package/dist/types/provider/cognito.d.ts.map +0 -1
- package/dist/types/provider/discord.d.ts +0 -38
- package/dist/types/provider/discord.d.ts.map +0 -1
- package/dist/types/provider/facebook.d.ts +0 -74
- package/dist/types/provider/facebook.d.ts.map +0 -1
- package/dist/types/provider/github.d.ts +0 -38
- package/dist/types/provider/github.d.ts.map +0 -1
- package/dist/types/provider/google.d.ts +0 -74
- package/dist/types/provider/google.d.ts.map +0 -1
- package/dist/types/provider/index.d.ts +0 -4
- package/dist/types/provider/index.d.ts.map +0 -1
- package/dist/types/provider/jumpcloud.d.ts +0 -38
- package/dist/types/provider/jumpcloud.d.ts.map +0 -1
- package/dist/types/provider/keycloak.d.ts +0 -67
- package/dist/types/provider/keycloak.d.ts.map +0 -1
- package/dist/types/provider/linkedin.d.ts +0 -6
- package/dist/types/provider/linkedin.d.ts.map +0 -1
- package/dist/types/provider/m2m.d.ts +0 -34
- package/dist/types/provider/m2m.d.ts.map +0 -1
- package/dist/types/provider/microsoft.d.ts +0 -89
- package/dist/types/provider/microsoft.d.ts.map +0 -1
- package/dist/types/provider/oauth2.d.ts +0 -133
- package/dist/types/provider/oauth2.d.ts.map +0 -1
- package/dist/types/provider/oidc.d.ts +0 -91
- package/dist/types/provider/oidc.d.ts.map +0 -1
- package/dist/types/provider/passkey.d.ts +0 -143
- package/dist/types/provider/passkey.d.ts.map +0 -1
- package/dist/types/provider/password.d.ts +0 -210
- package/dist/types/provider/password.d.ts.map +0 -1
- package/dist/types/provider/provider.d.ts +0 -29
- package/dist/types/provider/provider.d.ts.map +0 -1
- package/dist/types/provider/slack.d.ts +0 -59
- package/dist/types/provider/slack.d.ts.map +0 -1
- package/dist/types/provider/spotify.d.ts +0 -38
- package/dist/types/provider/spotify.d.ts.map +0 -1
- package/dist/types/provider/twitch.d.ts +0 -38
- package/dist/types/provider/twitch.d.ts.map +0 -1
- package/dist/types/provider/x.d.ts +0 -38
- package/dist/types/provider/x.d.ts.map +0 -1
- package/dist/types/provider/yahoo.d.ts +0 -38
- package/dist/types/provider/yahoo.d.ts.map +0 -1
- package/dist/types/random.d.ts +0 -3
- package/dist/types/random.d.ts.map +0 -1
- package/dist/types/storage/aws.d.ts +0 -4
- package/dist/types/storage/aws.d.ts.map +0 -1
- package/dist/types/storage/cloudflare.d.ts +0 -34
- package/dist/types/storage/cloudflare.d.ts.map +0 -1
- package/dist/types/storage/dynamo.d.ts +0 -65
- package/dist/types/storage/dynamo.d.ts.map +0 -1
- package/dist/types/storage/memory.d.ts +0 -49
- package/dist/types/storage/memory.d.ts.map +0 -1
- package/dist/types/storage/storage.d.ts +0 -15
- package/dist/types/storage/storage.d.ts.map +0 -1
- package/dist/types/subject.d.ts +0 -122
- package/dist/types/subject.d.ts.map +0 -1
- package/dist/types/ui/base.d.ts +0 -5
- package/dist/types/ui/base.d.ts.map +0 -1
- package/dist/types/ui/code.d.ts +0 -104
- package/dist/types/ui/code.d.ts.map +0 -1
- package/dist/types/ui/form.d.ts +0 -6
- package/dist/types/ui/form.d.ts.map +0 -1
- package/dist/types/ui/icon.d.ts +0 -6
- package/dist/types/ui/icon.d.ts.map +0 -1
- package/dist/types/ui/passkey.d.ts +0 -5
- package/dist/types/ui/passkey.d.ts.map +0 -1
- package/dist/types/ui/password.d.ts +0 -139
- package/dist/types/ui/password.d.ts.map +0 -1
- package/dist/types/ui/select.d.ts +0 -55
- package/dist/types/ui/select.d.ts.map +0 -1
- package/dist/types/ui/theme.d.ts +0 -207
- package/dist/types/ui/theme.d.ts.map +0 -1
- package/dist/types/util.d.ts +0 -8
- package/dist/types/util.d.ts.map +0 -1
- package/src/css.d.ts +0 -4
- package/src/issuer.ts +0 -1628
- package/src/jwt.ts +0 -17
- package/src/keys.ts +0 -139
- package/src/provider/apple.ts +0 -127
- package/src/provider/arctic.ts +0 -66
- package/src/provider/code.ts +0 -227
- package/src/provider/cognito.ts +0 -74
- package/src/provider/discord.ts +0 -45
- package/src/provider/facebook.ts +0 -84
- package/src/provider/github.ts +0 -45
- package/src/provider/google.ts +0 -85
- package/src/provider/index.ts +0 -3
- package/src/provider/jumpcloud.ts +0 -45
- package/src/provider/keycloak.ts +0 -75
- package/src/provider/linkedin.ts +0 -12
- package/src/provider/m2m.ts +0 -56
- package/src/provider/microsoft.ts +0 -100
- package/src/provider/oauth2.ts +0 -297
- package/src/provider/oidc.ts +0 -179
- package/src/provider/passkey.ts +0 -655
- package/src/provider/password.ts +0 -672
- package/src/provider/provider.ts +0 -33
- package/src/provider/slack.ts +0 -67
- package/src/provider/spotify.ts +0 -45
- package/src/provider/twitch.ts +0 -45
- package/src/provider/x.ts +0 -46
- package/src/provider/yahoo.ts +0 -45
- package/src/random.ts +0 -24
- package/src/storage/aws.ts +0 -59
- package/src/storage/cloudflare.ts +0 -77
- package/src/storage/dynamo.ts +0 -193
- package/src/storage/memory.ts +0 -135
- package/src/storage/storage.ts +0 -46
- package/src/subject.ts +0 -130
- package/src/ui/base.tsx +0 -118
- package/src/ui/code.tsx +0 -215
- package/src/ui/form.tsx +0 -40
- package/src/ui/icon.tsx +0 -95
- package/src/ui/passkey.tsx +0 -321
- package/src/ui/password.tsx +0 -405
- package/src/ui/select.tsx +0 -221
- package/src/ui/theme.ts +0 -319
- package/src/ui/ui.css +0 -252
- package/src/util.ts +0 -58
- /package/dist/esm/{css.d.js → adapters/d1/types.js} +0 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DPoP — Demonstration of Proof-of-Possession (RFC 9449).
|
|
3
|
+
*
|
|
4
|
+
* The RP/client generates an asymmetric key pair, sends a fresh proof
|
|
5
|
+
* JWT with each authenticated request, and the IdP binds the issued
|
|
6
|
+
* access token to the public key's SHA-256 thumbprint (`cnf.jkt`).
|
|
7
|
+
*
|
|
8
|
+
* This module parses + verifies DPoP proof JWTs. Replay protection is
|
|
9
|
+
* delegated to `TokenStore.recordDpopJti`; adapters without that method
|
|
10
|
+
* cannot satisfy DPoP and the verifier returns `invalid_dpop_proof`.
|
|
11
|
+
*
|
|
12
|
+
* Per RFC 9449 §4.2/§4.3, the proof JWT carries:
|
|
13
|
+
* - header: `typ: "dpop+jwt"`, `alg: <asymmetric>`, `jwk: <pub-JWK>`
|
|
14
|
+
* - payload: `htu`, `htm`, `iat`, `jti`, optional `ath`, optional `nonce`
|
|
15
|
+
*
|
|
16
|
+
* Verification chain (§4.3):
|
|
17
|
+
* 1. Header `typ` is exactly `"dpop+jwt"`.
|
|
18
|
+
* 2. `alg` is in the asymmetric allow-list (`ES256`, `EdDSA`).
|
|
19
|
+
* 3. `jwk` is a public key in JWK form; signature verifies under it.
|
|
20
|
+
* 4. `htm` equals the request method (case-sensitive).
|
|
21
|
+
* 5. `htu` equals the request URI (scheme + host + path; query / frag stripped).
|
|
22
|
+
* 6. `iat` is within ±`iatToleranceSec` of `now`.
|
|
23
|
+
* 7. `jti` has not been seen within the replay window (TokenStore-tracked).
|
|
24
|
+
* 8. If `expectedAth` supplied (resource-server check), `ath` matches.
|
|
25
|
+
*
|
|
26
|
+
* The thumbprint (`jkt`) is computed via RFC 7638 over the public JWK.
|
|
27
|
+
*/
|
|
28
|
+
import type { JWK, KeyLike } from "jose"
|
|
29
|
+
import {
|
|
30
|
+
calculateJwkThumbprint,
|
|
31
|
+
decodeProtectedHeader,
|
|
32
|
+
importJWK,
|
|
33
|
+
jwtVerify,
|
|
34
|
+
} from "jose"
|
|
35
|
+
|
|
36
|
+
import type { TokenStore } from "../ports/token-store"
|
|
37
|
+
import { authError, type AuthError } from "../types/error"
|
|
38
|
+
import type { Result } from "../types/result"
|
|
39
|
+
import { err, isErr, ok } from "../types/result"
|
|
40
|
+
|
|
41
|
+
import { base64url, sha256, utf8 } from "./crypto"
|
|
42
|
+
|
|
43
|
+
/** Allow-list of `alg` values accepted in DPoP proofs. Asymmetric only. */
|
|
44
|
+
const DPOP_ASYMMETRIC_ALGS: ReadonlySet<string> = new Set(["ES256", "EdDSA"])
|
|
45
|
+
|
|
46
|
+
/** RFC 9449 §4.2 — fixed 60-second tolerance is the conventional default. */
|
|
47
|
+
export const DEFAULT_DPOP_IAT_TOLERANCE_SEC = 60
|
|
48
|
+
|
|
49
|
+
/** Replay-window TTL for `jti` tracking. Matches `iat` tolerance × 2. */
|
|
50
|
+
export const DEFAULT_DPOP_JTI_TTL_MS = 2 * DEFAULT_DPOP_IAT_TOLERANCE_SEC * 1000
|
|
51
|
+
|
|
52
|
+
export type DpopProofPayload = {
|
|
53
|
+
htu: string
|
|
54
|
+
htm: string
|
|
55
|
+
iat: number
|
|
56
|
+
jti: string
|
|
57
|
+
ath?: string
|
|
58
|
+
nonce?: string
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type VerifiedDpopProof = {
|
|
62
|
+
/** RFC 7638 JWK thumbprint of the proof's public key. */
|
|
63
|
+
jkt: string
|
|
64
|
+
/** The decoded + signature-verified proof payload. */
|
|
65
|
+
payload: DpopProofPayload
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type VerifyDpopProofInput = {
|
|
69
|
+
proofJwt: string
|
|
70
|
+
/** Actual request URI (scheme + host + port + path; query/frag stripped). */
|
|
71
|
+
htu: string
|
|
72
|
+
/** Actual HTTP method, uppercase. */
|
|
73
|
+
htm: string
|
|
74
|
+
/** Wall clock seconds. */
|
|
75
|
+
nowSec: number
|
|
76
|
+
/** Optional skew allowance. */
|
|
77
|
+
iatToleranceSec?: number
|
|
78
|
+
/**
|
|
79
|
+
* Required at resource servers when the proof must bind to a presented
|
|
80
|
+
* access token. The verifier checks the proof's `ath` equals
|
|
81
|
+
* base64url(SHA-256(access_token ASCII)). Omit at the AS `/token`
|
|
82
|
+
* endpoint — no access token exists yet there.
|
|
83
|
+
*/
|
|
84
|
+
expectedAth?: string
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type VerifyDpopProofDeps = {
|
|
88
|
+
tokenStore: TokenStore
|
|
89
|
+
jtiTtlMs?: number
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Verify a DPoP proof JWT and return its `jkt` + payload. Side-effect:
|
|
94
|
+
* records the `jti` in `TokenStore` so a replay within the TTL window
|
|
95
|
+
* is rejected.
|
|
96
|
+
*/
|
|
97
|
+
export async function verifyDpopProof(
|
|
98
|
+
input: VerifyDpopProofInput,
|
|
99
|
+
deps: VerifyDpopProofDeps,
|
|
100
|
+
): Promise<Result<VerifiedDpopProof, AuthError>> {
|
|
101
|
+
if (!deps.tokenStore.recordDpopJti) {
|
|
102
|
+
return err(
|
|
103
|
+
authError.invalidDpopProof(
|
|
104
|
+
"token-store adapter does not support DPoP replay protection",
|
|
105
|
+
),
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
// 1. Decode the header to recover the embedded JWK + check `typ`/`alg`
|
|
109
|
+
// before the signature verification call so we can return precise
|
|
110
|
+
// errors instead of a generic jose failure.
|
|
111
|
+
let header: ReturnType<typeof decodeProtectedHeader>
|
|
112
|
+
try {
|
|
113
|
+
header = decodeProtectedHeader(input.proofJwt)
|
|
114
|
+
} catch (e) {
|
|
115
|
+
return err(
|
|
116
|
+
authError.invalidDpopProof(
|
|
117
|
+
`dpop proof header is not a valid compact JWS: ${stringifyError(e)}`,
|
|
118
|
+
),
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
if (header.typ !== "dpop+jwt") {
|
|
122
|
+
return err(
|
|
123
|
+
authError.invalidDpopProof(
|
|
124
|
+
`dpop proof header.typ must be "dpop+jwt", got "${String(header.typ)}"`,
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
if (!header.alg || !DPOP_ASYMMETRIC_ALGS.has(header.alg)) {
|
|
129
|
+
return err(
|
|
130
|
+
authError.invalidDpopProof(
|
|
131
|
+
`dpop proof alg "${String(header.alg)}" not in {ES256, EdDSA}`,
|
|
132
|
+
),
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
const jwk = header.jwk as JWK | undefined
|
|
136
|
+
if (!jwk || typeof jwk !== "object") {
|
|
137
|
+
return err(
|
|
138
|
+
authError.invalidDpopProof(
|
|
139
|
+
"dpop proof header.jwk missing or not an object",
|
|
140
|
+
),
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
// §4.2: the embedded JWK MUST be a public key. Reject anything that
|
|
144
|
+
// looks like a private key (`d` for EC / OKP, `d`+`p`+`q` for RSA).
|
|
145
|
+
if ("d" in jwk) {
|
|
146
|
+
return err(
|
|
147
|
+
authError.invalidDpopProof(
|
|
148
|
+
"dpop proof header.jwk must be a public key (private material present)",
|
|
149
|
+
),
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 2. Verify the signature with the embedded JWK.
|
|
154
|
+
let key: KeyLike
|
|
155
|
+
try {
|
|
156
|
+
key = (await importJWK(jwk, header.alg)) as KeyLike
|
|
157
|
+
} catch (e) {
|
|
158
|
+
return err(
|
|
159
|
+
authError.invalidDpopProof(
|
|
160
|
+
`dpop proof JWK import failed: ${stringifyError(e)}`,
|
|
161
|
+
),
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
let verified
|
|
165
|
+
try {
|
|
166
|
+
verified = await jwtVerify(input.proofJwt, key, {
|
|
167
|
+
algorithms: [header.alg],
|
|
168
|
+
})
|
|
169
|
+
} catch (e) {
|
|
170
|
+
return err(
|
|
171
|
+
authError.invalidDpopProof(
|
|
172
|
+
`dpop proof signature verification failed: ${stringifyError(e)}`,
|
|
173
|
+
),
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
const payload = verified.payload as Partial<DpopProofPayload>
|
|
177
|
+
|
|
178
|
+
// 3. Payload-shape checks. The compact set: htu, htm, iat, jti.
|
|
179
|
+
if (typeof payload.htm !== "string") {
|
|
180
|
+
return err(authError.invalidDpopProof("dpop proof missing string htm"))
|
|
181
|
+
}
|
|
182
|
+
if (typeof payload.htu !== "string") {
|
|
183
|
+
return err(authError.invalidDpopProof("dpop proof missing string htu"))
|
|
184
|
+
}
|
|
185
|
+
if (typeof payload.iat !== "number") {
|
|
186
|
+
return err(authError.invalidDpopProof("dpop proof missing numeric iat"))
|
|
187
|
+
}
|
|
188
|
+
if (typeof payload.jti !== "string" || payload.jti.length === 0) {
|
|
189
|
+
return err(
|
|
190
|
+
authError.invalidDpopProof("dpop proof missing non-empty string jti"),
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 4. htm / htu equality.
|
|
195
|
+
if (payload.htm !== input.htm) {
|
|
196
|
+
return err(
|
|
197
|
+
authError.invalidDpopProof(
|
|
198
|
+
`dpop htm "${payload.htm}" does not match request method "${input.htm}"`,
|
|
199
|
+
),
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
if (payload.htu !== input.htu) {
|
|
203
|
+
return err(
|
|
204
|
+
authError.invalidDpopProof(
|
|
205
|
+
`dpop htu "${payload.htu}" does not match request uri "${input.htu}"`,
|
|
206
|
+
),
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// 5. iat freshness.
|
|
211
|
+
const tolerance = input.iatToleranceSec ?? DEFAULT_DPOP_IAT_TOLERANCE_SEC
|
|
212
|
+
const skew = Math.abs(input.nowSec - payload.iat)
|
|
213
|
+
if (skew > tolerance) {
|
|
214
|
+
return err(
|
|
215
|
+
authError.invalidDpopProof(
|
|
216
|
+
`dpop iat skew ${skew}s exceeds tolerance ${tolerance}s`,
|
|
217
|
+
),
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// 6. ath check (RS use only).
|
|
222
|
+
if (input.expectedAth !== undefined) {
|
|
223
|
+
if (typeof payload.ath !== "string" || payload.ath !== input.expectedAth) {
|
|
224
|
+
return err(
|
|
225
|
+
authError.invalidDpopProof(
|
|
226
|
+
"dpop ath does not match SHA-256 of the access token",
|
|
227
|
+
),
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// 7. jti replay protection — atomic record-or-fail in the token store.
|
|
233
|
+
const recorded = await deps.tokenStore.recordDpopJti(
|
|
234
|
+
payload.jti,
|
|
235
|
+
deps.jtiTtlMs ?? DEFAULT_DPOP_JTI_TTL_MS,
|
|
236
|
+
)
|
|
237
|
+
if (isErr(recorded)) {
|
|
238
|
+
return err(
|
|
239
|
+
authError.invalidDpopProof(`dpop jti "${payload.jti}" replayed`, {
|
|
240
|
+
jti: payload.jti,
|
|
241
|
+
}),
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// 8. Compute jkt thumbprint (RFC 7638) — bound onto the issued token.
|
|
246
|
+
let jkt: string
|
|
247
|
+
try {
|
|
248
|
+
jkt = await calculateJwkThumbprint(jwk, "sha256")
|
|
249
|
+
} catch (e) {
|
|
250
|
+
return err(
|
|
251
|
+
authError.invalidDpopProof(
|
|
252
|
+
`dpop jkt thumbprint computation failed: ${stringifyError(e)}`,
|
|
253
|
+
),
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return ok({
|
|
258
|
+
jkt,
|
|
259
|
+
payload: {
|
|
260
|
+
htm: payload.htm,
|
|
261
|
+
htu: payload.htu,
|
|
262
|
+
iat: payload.iat,
|
|
263
|
+
jti: payload.jti,
|
|
264
|
+
...(payload.ath !== undefined ? { ath: payload.ath } : {}),
|
|
265
|
+
...(payload.nonce !== undefined ? { nonce: payload.nonce } : {}),
|
|
266
|
+
},
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Canonicalize a request URL for `htu` comparison (RFC 9449 §4.3).
|
|
272
|
+
* Strips query string and fragment; preserves scheme, host, port, path.
|
|
273
|
+
* Default port for the scheme is removed.
|
|
274
|
+
*/
|
|
275
|
+
export function canonicalHtu(rawUrl: string): string {
|
|
276
|
+
const u = new URL(rawUrl)
|
|
277
|
+
u.search = ""
|
|
278
|
+
u.hash = ""
|
|
279
|
+
// Normalize default ports per the URL spec — `new URL("https://x:443").port` is "".
|
|
280
|
+
return u.toString()
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Compute the `ath` value (RFC 9449 §4.2): base64url(SHA-256(access_token ASCII)).
|
|
285
|
+
* Resource servers compare the presented DPoP proof's `ath` to this.
|
|
286
|
+
*/
|
|
287
|
+
export async function computeAth(accessToken: string): Promise<string> {
|
|
288
|
+
return base64url.encode(await sha256(utf8.encode(accessToken)))
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function stringifyError(e: unknown): string {
|
|
292
|
+
return e instanceof Error ? e.message : String(e)
|
|
293
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `id_token` assembly — claim selection, scope gating, `at_hash`, `amr`
|
|
3
|
+
* derivation.
|
|
4
|
+
*
|
|
5
|
+
* OIDC Core §2 mandates `iss`, `sub`, `aud`, `exp`, `iat`. `auth_time` is
|
|
6
|
+
* required when `max_age` was requested or `auth_time` is essential; we
|
|
7
|
+
* always include it because we always know it (stamped at `success`).
|
|
8
|
+
* `nonce` is required to echo the RP's `/authorize` value when present.
|
|
9
|
+
*
|
|
10
|
+
* Scope→claim mapping follows OIDC Core §5.4 exactly. Properties are
|
|
11
|
+
* sourced from `SubjectClaim.properties` — hosts populate that record in
|
|
12
|
+
* their `IdPOptions.success` callback. We only emit a profile claim
|
|
13
|
+
* when (a) the corresponding scope was granted and (b) the value is a
|
|
14
|
+
* recognized JSON-serializable type. Unknown values are silently
|
|
15
|
+
* dropped (forward compat with subject schemas the host adds later).
|
|
16
|
+
*
|
|
17
|
+
* `at_hash` is computed per §3.1.3.6: left-half SHA-256 of the access
|
|
18
|
+
* token's ASCII octets, base64url-encoded with no padding.
|
|
19
|
+
*
|
|
20
|
+
* `amr` (RFC 8176) is derived from `methodKind` via a small fixed table.
|
|
21
|
+
* Methods that aren't in the table simply omit the claim — `amr` is
|
|
22
|
+
* optional. Hosts that want richer amr semantics should add the claim
|
|
23
|
+
* via the host claim hook (Phase E).
|
|
24
|
+
*/
|
|
25
|
+
import { base64url, sha256, utf8 } from "./crypto"
|
|
26
|
+
import type { ClaimsRequest } from "../types/authorization"
|
|
27
|
+
import type { IdTokenClaims, ScopedProfileClaims } from "../types/token"
|
|
28
|
+
import type { SubjectClaim } from "../types/subject"
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Map of OIDC scope name → list of profile claim names that scope grants
|
|
32
|
+
* (OIDC Core §5.4). `openid` itself is the discriminator that an id_token
|
|
33
|
+
* should be issued at all — it grants no profile claims.
|
|
34
|
+
*/
|
|
35
|
+
const SCOPE_TO_CLAIMS: Record<string, ReadonlyArray<string>> = {
|
|
36
|
+
profile: [
|
|
37
|
+
"name",
|
|
38
|
+
"given_name",
|
|
39
|
+
"family_name",
|
|
40
|
+
"middle_name",
|
|
41
|
+
"nickname",
|
|
42
|
+
"preferred_username",
|
|
43
|
+
"profile",
|
|
44
|
+
"picture",
|
|
45
|
+
"website",
|
|
46
|
+
"gender",
|
|
47
|
+
"birthdate",
|
|
48
|
+
"zoneinfo",
|
|
49
|
+
"locale",
|
|
50
|
+
"updated_at",
|
|
51
|
+
],
|
|
52
|
+
email: ["email", "email_verified"],
|
|
53
|
+
phone: ["phone_number", "phone_number_verified"],
|
|
54
|
+
address: ["address"],
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Derive `amr` from the originating `methodKind`. Values are RFC 8176
|
|
59
|
+
* registered AMR codes. Returns `undefined` when no clean mapping exists
|
|
60
|
+
* — `amr` is an OPTIONAL id_token claim, so omission is spec-compliant.
|
|
61
|
+
*/
|
|
62
|
+
export function deriveAmr(methodKind: string): string[] | undefined {
|
|
63
|
+
switch (methodKind) {
|
|
64
|
+
case "password":
|
|
65
|
+
return ["pwd"]
|
|
66
|
+
case "code":
|
|
67
|
+
return ["otp"]
|
|
68
|
+
case "passkey":
|
|
69
|
+
// Resident credentials backed by a platform authenticator. Best
|
|
70
|
+
// approximated by `hwk` (hardware key) per RFC 8176; platform
|
|
71
|
+
// authenticators are usually hardware-backed (Secure Enclave,
|
|
72
|
+
// TPM, etc.) even when the key is software-resident on disk.
|
|
73
|
+
return ["hwk"]
|
|
74
|
+
default:
|
|
75
|
+
// External federated providers (google, github, oidc-generic, …)
|
|
76
|
+
// — no standardized AMR for "logged in elsewhere", so omit.
|
|
77
|
+
return undefined
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* `at_hash` per OIDC Core §3.1.3.6: left-half of SHA-256(access_token
|
|
83
|
+
* ASCII), base64url. Always 16 bytes → 22 base64url characters for
|
|
84
|
+
* SHA-256 (256-bit → 128-bit half → 16 bytes → 22 chars).
|
|
85
|
+
*/
|
|
86
|
+
export async function computeAtHash(accessToken: string): Promise<string> {
|
|
87
|
+
const digest = await sha256(utf8.encode(accessToken))
|
|
88
|
+
const half = digest.slice(0, digest.byteLength / 2)
|
|
89
|
+
return base64url.encode(half)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Filter a `SubjectClaim.properties` record down to claims granted by the
|
|
94
|
+
* requested OIDC scopes, then optionally augment with claim names the RP
|
|
95
|
+
* specifically requested via the OIDC Core §5.5 `claims` parameter.
|
|
96
|
+
* Returns a typed `ScopedProfileClaims` ready to spread into the id_token
|
|
97
|
+
* or `/userinfo` response. Properties not named by any granted scope or
|
|
98
|
+
* explicit claim request are dropped.
|
|
99
|
+
*
|
|
100
|
+
* `extra` is the set of claim names the RP requested via `claims`
|
|
101
|
+
* parameter for THIS surface (id_token vs userinfo). Listing a name in
|
|
102
|
+
* `extra` bypasses scope gating per §5.5 ("works without the requestor
|
|
103
|
+
* having to include the scope value").
|
|
104
|
+
*
|
|
105
|
+
* `customMappings` is a host-supplied scope → claim-names map merged on
|
|
106
|
+
* top of the OIDC Core §5.4 table. Lets the host expose vendor-specific
|
|
107
|
+
* identity fields (e.g. `tenant_id`, `org_role`) via a custom scope
|
|
108
|
+
* (`scope=foo` granting `[tenant_id, …]`). Reserved §5.4 names cannot be
|
|
109
|
+
* shadowed — the standard mapping always wins on collision so an
|
|
110
|
+
* `email` scope can't accidentally start meaning something else.
|
|
111
|
+
*
|
|
112
|
+
* Host-supplied values are trusted to match their declared OIDC types
|
|
113
|
+
* — schemas live on `IdPOptions.subjects`, not here. The narrowing cast
|
|
114
|
+
* is the boundary between "host's responsibility" and "framework's
|
|
115
|
+
* responsibility."
|
|
116
|
+
*/
|
|
117
|
+
export function pickScopedClaims(
|
|
118
|
+
claim: SubjectClaim,
|
|
119
|
+
scopes: ReadonlyArray<string>,
|
|
120
|
+
extra: ReadonlyArray<string> = [],
|
|
121
|
+
customMappings: Record<string, ReadonlyArray<string>> = {},
|
|
122
|
+
): ScopedProfileClaims {
|
|
123
|
+
const props = (claim as { properties: Record<string, unknown> }).properties
|
|
124
|
+
if (!props || typeof props !== "object") return {}
|
|
125
|
+
// Standard §5.4 names take precedence: spread custom first, then
|
|
126
|
+
// SCOPE_TO_CLAIMS, so a host can't override `email → [email, ...]`.
|
|
127
|
+
const mappings: Record<string, ReadonlyArray<string>> = {
|
|
128
|
+
...customMappings,
|
|
129
|
+
...SCOPE_TO_CLAIMS,
|
|
130
|
+
}
|
|
131
|
+
const granted = new Set<string>()
|
|
132
|
+
for (const scope of scopes) {
|
|
133
|
+
const list = mappings[scope]
|
|
134
|
+
if (!list) continue
|
|
135
|
+
for (const c of list) granted.add(c)
|
|
136
|
+
}
|
|
137
|
+
for (const name of extra) granted.add(name)
|
|
138
|
+
const out: Record<string, unknown> = {}
|
|
139
|
+
for (const key of granted) {
|
|
140
|
+
if (key in props && props[key] !== undefined) out[key] = props[key]
|
|
141
|
+
}
|
|
142
|
+
return out as ScopedProfileClaims
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export type BuildIdTokenInput = {
|
|
146
|
+
issuerUrl: string
|
|
147
|
+
/** RP `client_id` — the id_token's `aud`. */
|
|
148
|
+
audience: string
|
|
149
|
+
/** Stable subject identifier (same value as the access token's `sub`). */
|
|
150
|
+
subjectId: string
|
|
151
|
+
claim: SubjectClaim
|
|
152
|
+
scopes: ReadonlyArray<string>
|
|
153
|
+
/** Seconds-since-epoch the user authenticated. */
|
|
154
|
+
authTime: number
|
|
155
|
+
/** RP-supplied OIDC nonce, when present. */
|
|
156
|
+
appNonce?: string
|
|
157
|
+
/** Wall clock (ms). */
|
|
158
|
+
now: number
|
|
159
|
+
/** id_token TTL (ms). Default 5 minutes. */
|
|
160
|
+
ttlMs?: number
|
|
161
|
+
/** Method kind for `amr` derivation. */
|
|
162
|
+
methodKind: string
|
|
163
|
+
/** Pre-signed access token whose `at_hash` is bound into the id_token. */
|
|
164
|
+
accessToken: string
|
|
165
|
+
/** OIDC Core §5.5 — RP-requested claims (this drives only id_token here). */
|
|
166
|
+
claimsRequest?: ClaimsRequest
|
|
167
|
+
/**
|
|
168
|
+
* Host-supplied vendor scope → claim-names map. Merged on top of OIDC
|
|
169
|
+
* Core §5.4 at scope-gating time. See `pickScopedClaims`.
|
|
170
|
+
*/
|
|
171
|
+
customScopeClaims?: Record<string, ReadonlyArray<string>>
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export const DEFAULT_ID_TOKEN_TTL_MS = 5 * 60 * 1000
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Assemble an `IdTokenClaims` object. Pure — no crypto except `at_hash`.
|
|
178
|
+
* Caller signs the result via `signIdToken`.
|
|
179
|
+
*
|
|
180
|
+
* Composition is via spread so the type stays narrow: the base claim set
|
|
181
|
+
* (REQUIRED OIDC §2 + auth_time + at_hash + optional nonce/amr) is built
|
|
182
|
+
* first; the scope-gated §5.1 profile claims merge over the top via
|
|
183
|
+
* `...pickScopedClaims(...)`. No dynamic index assignment, no cast.
|
|
184
|
+
*/
|
|
185
|
+
export async function buildIdTokenClaims(
|
|
186
|
+
input: BuildIdTokenInput,
|
|
187
|
+
): Promise<IdTokenClaims> {
|
|
188
|
+
const ttl = input.ttlMs ?? DEFAULT_ID_TOKEN_TTL_MS
|
|
189
|
+
const amr = deriveAmr(input.methodKind)
|
|
190
|
+
const extraClaims = Object.keys(input.claimsRequest?.id_token ?? {})
|
|
191
|
+
return {
|
|
192
|
+
iss: input.issuerUrl,
|
|
193
|
+
sub: input.subjectId,
|
|
194
|
+
aud: input.audience,
|
|
195
|
+
exp: Math.floor((input.now + ttl) / 1000),
|
|
196
|
+
iat: Math.floor(input.now / 1000),
|
|
197
|
+
auth_time: input.authTime,
|
|
198
|
+
at_hash: await computeAtHash(input.accessToken),
|
|
199
|
+
...(input.appNonce !== undefined ? { nonce: input.appNonce } : {}),
|
|
200
|
+
...(amr ? { amr } : {}),
|
|
201
|
+
...pickScopedClaims(
|
|
202
|
+
input.claim,
|
|
203
|
+
input.scopes,
|
|
204
|
+
extraClaims,
|
|
205
|
+
input.customScopeClaims ?? {},
|
|
206
|
+
),
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Predicate: does this token request warrant an `id_token` being issued? */
|
|
211
|
+
export function shouldIssueIdToken(scopes: ReadonlyArray<string>): boolean {
|
|
212
|
+
return scopes.includes("openid")
|
|
213
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/introspect` endpoint logic (RFC 7662).
|
|
3
|
+
*
|
|
4
|
+
* RFC 7662 §2.1 REQUIRES authentication: "To prevent token scanning
|
|
5
|
+
* attacks, the endpoint MUST also require some form of authorization to
|
|
6
|
+
* access this endpoint." Confidential-client credentials are validated
|
|
7
|
+
* against the **presenter's** tenant — resolved by the HTTP layer from
|
|
8
|
+
* the request, exactly the same way `/token client_credentials` resolves
|
|
9
|
+
* tenant. Authenticating before touching the token's `tid` means:
|
|
10
|
+
*
|
|
11
|
+
* - Unknown presenting client OR wrong secret → `invalid_client`
|
|
12
|
+
* (uniform — the response says nothing about the token).
|
|
13
|
+
* - Token belongs to a different tenant than the presenter, or names a
|
|
14
|
+
* different client as audience, or fails verification → `{active: false}`.
|
|
15
|
+
*
|
|
16
|
+
* Pre-fix the order was "verify token → load token's tenant → look up
|
|
17
|
+
* presenter in *that* tenant", which let a client authenticated in
|
|
18
|
+
* tenant X probe whether its `client_id` existed in tenant Y by
|
|
19
|
+
* watching for `invalid_client` versus `{active: false}` responses.
|
|
20
|
+
*
|
|
21
|
+
* Audience check (RFC 7662 §2.2): an authenticated client may only
|
|
22
|
+
* inspect tokens that name it as the audience. Tokens belonging to a
|
|
23
|
+
* different client return `{ active: false }` — never a structured
|
|
24
|
+
* error — to avoid leaking which tokens exist.
|
|
25
|
+
*
|
|
26
|
+
* Refresh tokens are not introspectable here; RFC 7662 §2.1 only
|
|
27
|
+
* requires access-token introspection, and our refresh tokens are
|
|
28
|
+
* opaque and don't carry the standard introspection metadata.
|
|
29
|
+
*/
|
|
30
|
+
import type { ConfigStore } from "../ports/config-store"
|
|
31
|
+
import type { KeyStore } from "../ports/key-store"
|
|
32
|
+
import type { AuthError } from "../types/error"
|
|
33
|
+
import { authError } from "../types/error"
|
|
34
|
+
import type { Result } from "../types/result"
|
|
35
|
+
import { err, isErr, ok } from "../types/result"
|
|
36
|
+
import type { AccessTokenClaims } from "../types/token"
|
|
37
|
+
import type { TenantId } from "../types/tenant"
|
|
38
|
+
|
|
39
|
+
import { verifyClientCredentials } from "./client-auth"
|
|
40
|
+
import { verifyAccessToken } from "./jwt"
|
|
41
|
+
|
|
42
|
+
export type IntrospectResponse =
|
|
43
|
+
| { active: false }
|
|
44
|
+
| {
|
|
45
|
+
active: true
|
|
46
|
+
sub: string
|
|
47
|
+
aud: string
|
|
48
|
+
iss: string
|
|
49
|
+
exp: number
|
|
50
|
+
iat: number
|
|
51
|
+
scope?: string
|
|
52
|
+
client_id?: string
|
|
53
|
+
/** RFC 7662 §2.2 — the token's type indicator. */
|
|
54
|
+
token_type: "Bearer" | "DPoP"
|
|
55
|
+
tid: string
|
|
56
|
+
mid?: string
|
|
57
|
+
mkind?: string
|
|
58
|
+
/** Library-specific — subject schema discriminator (e.g. "user", "admin"). */
|
|
59
|
+
subject_type?: string
|
|
60
|
+
/** RFC 9449 §6 — present when the access token is DPoP-bound. */
|
|
61
|
+
cnf?: { jkt: string }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type IntrospectRequest = {
|
|
65
|
+
token: string
|
|
66
|
+
tokenTypeHint?: "access_token" | "refresh_token"
|
|
67
|
+
/** REQUIRED — RFC 7662 §2.1. */
|
|
68
|
+
clientId: string
|
|
69
|
+
clientSecret?: string
|
|
70
|
+
/**
|
|
71
|
+
* The tenant the presenting client belongs to. Resolved by the HTTP
|
|
72
|
+
* layer via `IdPOptions.resolveTenant`, just like for the m2m grant.
|
|
73
|
+
* Cross-tenant introspection collapses to `{active: false}` regardless
|
|
74
|
+
* of whether the presenter's client exists in the token's tenant.
|
|
75
|
+
*/
|
|
76
|
+
presenterTenantId: TenantId
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type IntrospectDeps = {
|
|
80
|
+
keyStore: KeyStore
|
|
81
|
+
configStore: ConfigStore
|
|
82
|
+
/** Expected issuer for additional validation. */
|
|
83
|
+
issuerUrl?: string
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function introspect(
|
|
87
|
+
req: IntrospectRequest,
|
|
88
|
+
deps: IntrospectDeps,
|
|
89
|
+
): Promise<Result<IntrospectResponse, AuthError>> {
|
|
90
|
+
// 1. Authenticate the presenting client against its OWN tenant before
|
|
91
|
+
// looking at the token. Failure here = `invalid_client` (RFC 7662
|
|
92
|
+
// §2.1). The presenter's tenant is the only signal we trust until
|
|
93
|
+
// auth succeeds.
|
|
94
|
+
const presenterTenantCfg = await deps.configStore.getTenantConfig(
|
|
95
|
+
req.presenterTenantId,
|
|
96
|
+
)
|
|
97
|
+
if (isErr(presenterTenantCfg)) {
|
|
98
|
+
return err(authError.invalidClient(`unknown client "${req.clientId}"`))
|
|
99
|
+
}
|
|
100
|
+
const presenter = presenterTenantCfg.value.clients.find(
|
|
101
|
+
(c) => c.id === req.clientId,
|
|
102
|
+
)
|
|
103
|
+
if (!presenter) {
|
|
104
|
+
return err(authError.invalidClient(`unknown client "${req.clientId}"`))
|
|
105
|
+
}
|
|
106
|
+
const authErr = await verifyClientCredentials(presenter, req.clientSecret)
|
|
107
|
+
if (authErr) return err(authErr)
|
|
108
|
+
|
|
109
|
+
// 2. Try to verify the token. Unverifiable / wrong issuer / expired →
|
|
110
|
+
// `{active: false}` so a presenter cannot tell the difference
|
|
111
|
+
// between a forged token and a valid one belonging to another
|
|
112
|
+
// tenant or client.
|
|
113
|
+
const keysRes = await deps.keyStore.signingKeys()
|
|
114
|
+
if (isErr(keysRes)) return err(keysRes.error)
|
|
115
|
+
let claims: AccessTokenClaims | null
|
|
116
|
+
try {
|
|
117
|
+
claims = await verifyAccessToken(req.token, keysRes.value, {
|
|
118
|
+
...(deps.issuerUrl ? { issuer: deps.issuerUrl } : {}),
|
|
119
|
+
})
|
|
120
|
+
} catch {
|
|
121
|
+
return ok({ active: false })
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 3. Token's tenant must match the presenter's tenant. RFC 7662 lets
|
|
125
|
+
// us return `{active: false}` here — never a structured error —
|
|
126
|
+
// so a presenter can't probe across tenants. (H6 of the
|
|
127
|
+
// post-rebuild review.)
|
|
128
|
+
if ((claims.tid as TenantId) !== req.presenterTenantId) {
|
|
129
|
+
return ok({ active: false })
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 4. Audience check (RFC 7662 §2.2): only the client the token names
|
|
133
|
+
// as audience may see its claims.
|
|
134
|
+
if (claims.aud !== req.clientId) {
|
|
135
|
+
return ok({ active: false })
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const subjectType = (claims.claim as { type?: string } | undefined)?.type
|
|
139
|
+
return ok({
|
|
140
|
+
active: true,
|
|
141
|
+
sub: claims.sub,
|
|
142
|
+
aud: claims.aud,
|
|
143
|
+
iss: claims.iss,
|
|
144
|
+
exp: claims.exp,
|
|
145
|
+
iat: claims.iat,
|
|
146
|
+
scope: claims.scope,
|
|
147
|
+
client_id: claims.aud,
|
|
148
|
+
token_type: claims.cnf?.jkt !== undefined ? "DPoP" : "Bearer",
|
|
149
|
+
tid: claims.tid,
|
|
150
|
+
mid: claims.mid,
|
|
151
|
+
mkind: claims.mkind,
|
|
152
|
+
...(subjectType !== undefined ? { subject_type: subjectType } : {}),
|
|
153
|
+
...(claims.cnf !== undefined ? { cnf: claims.cnf } : {}),
|
|
154
|
+
})
|
|
155
|
+
}
|