@_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,483 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/token` endpoint logic for `grant_type=authorization_code`.
|
|
3
|
+
*
|
|
4
|
+
* Steps:
|
|
5
|
+
* 1. Authenticate client (public: id only; confidential: id + secret).
|
|
6
|
+
* 2. `TokenStore.consumeCode(code)` — strong CAS; returns the decrypted
|
|
7
|
+
* `CodePayload`.
|
|
8
|
+
* 3. Verify the consumed code matches the request (`client_id`,
|
|
9
|
+
* `redirect_uri`). Any mismatch → `invalid_grant`.
|
|
10
|
+
* 4. Verify PKCE if the code carries a `clientPkce` (RP→IdP PKCE).
|
|
11
|
+
* 5. Call the user's `success` callback with `SuccessMapInput` to mint
|
|
12
|
+
* the `SubjectClaim` — the same role the legacy `auth.success`
|
|
13
|
+
* callback plays in `issuer.ts`.
|
|
14
|
+
* 6. (Optional) `persistUpstreamTokens` hook — runs here, not at
|
|
15
|
+
* callback time, so failed exchanges don't pollute the secrets store.
|
|
16
|
+
* 7. Mint access (JWT, ES256) + refresh (random opaque) tokens, save
|
|
17
|
+
* refresh, audit `token_issued`.
|
|
18
|
+
*
|
|
19
|
+
* Refresh-grant logic lives in `./refresh.ts`. Revocation in `./revoke.ts`.
|
|
20
|
+
*/
|
|
21
|
+
import type { AuditLog } from "../ports/audit-log"
|
|
22
|
+
import type { KeyStore } from "../ports/key-store"
|
|
23
|
+
import type { TokenStore } from "../ports/token-store"
|
|
24
|
+
import type { ConfigStore } from "../ports/config-store"
|
|
25
|
+
import type { AuthError } from "../types/error"
|
|
26
|
+
import { authError } from "../types/error"
|
|
27
|
+
import type { PersistUpstreamTokens, SuccessMapInput } from "../types/idp"
|
|
28
|
+
import type { Result } from "../types/result"
|
|
29
|
+
import { err, isErr, ok } from "../types/result"
|
|
30
|
+
import type { SubjectClaim } from "../types/subject"
|
|
31
|
+
import type { TenantContext } from "../types/tenant"
|
|
32
|
+
import type {
|
|
33
|
+
AccessTokenClaims,
|
|
34
|
+
CodePayload,
|
|
35
|
+
RefreshTokenPayload,
|
|
36
|
+
TokenResponse,
|
|
37
|
+
} from "../types/token"
|
|
38
|
+
|
|
39
|
+
import { safeAudit } from "./audit"
|
|
40
|
+
import { verifyClientCredentials } from "./client-auth"
|
|
41
|
+
import {
|
|
42
|
+
base64url,
|
|
43
|
+
decryptPayload,
|
|
44
|
+
encryptPayload,
|
|
45
|
+
randomId,
|
|
46
|
+
randomToken,
|
|
47
|
+
sha256,
|
|
48
|
+
utf8,
|
|
49
|
+
} from "./crypto"
|
|
50
|
+
import { buildIdTokenClaims, shouldIssueIdToken } from "./id-token"
|
|
51
|
+
import { signAccessToken, signIdToken } from "./jwt"
|
|
52
|
+
import { validatePkce } from "./pkce"
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Encrypt a `CodePayload` with the active `KeyStore` encryption key and
|
|
56
|
+
* persist it via `tokenStore.saveCode`. Centralizes the encrypt-then-store
|
|
57
|
+
* pattern so adapters never see plaintext (M1).
|
|
58
|
+
*/
|
|
59
|
+
export async function saveEncryptedCode(
|
|
60
|
+
code: string,
|
|
61
|
+
payload: CodePayload,
|
|
62
|
+
ttl: number,
|
|
63
|
+
deps: { keyStore: KeyStore; tokenStore: TokenStore },
|
|
64
|
+
): Promise<Result<void, AuthError>> {
|
|
65
|
+
const keyResult = await deps.keyStore.currentEncryptionKey()
|
|
66
|
+
if (isErr(keyResult)) return err(keyResult.error)
|
|
67
|
+
let ciphertext: string
|
|
68
|
+
try {
|
|
69
|
+
ciphertext = await encryptPayload(
|
|
70
|
+
payload,
|
|
71
|
+
keyResult.value.kid,
|
|
72
|
+
keyResult.value.keyRef as Uint8Array,
|
|
73
|
+
)
|
|
74
|
+
} catch (e) {
|
|
75
|
+
return err(authError.internalError("saveEncryptedCode: encrypt failed", e))
|
|
76
|
+
}
|
|
77
|
+
return deps.tokenStore.saveCode(code, ciphertext, ttl)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const DEFAULT_ACCESS_TTL_MS = 15 * 60 * 1000
|
|
81
|
+
export const DEFAULT_REFRESH_TTL_MS = 30 * 24 * 60 * 60 * 1000
|
|
82
|
+
|
|
83
|
+
export type TokenAuthCodeRequest = {
|
|
84
|
+
grantType: "authorization_code"
|
|
85
|
+
code: string
|
|
86
|
+
redirectUri: string
|
|
87
|
+
clientId: string
|
|
88
|
+
/** Confidential clients only. */
|
|
89
|
+
clientSecret?: string
|
|
90
|
+
/** Required if the original `/authorize` request had a `code_challenge`. */
|
|
91
|
+
codeVerifier?: string
|
|
92
|
+
/**
|
|
93
|
+
* RFC 9449 §6 — the JWK thumbprint of the presented DPoP proof. Set by
|
|
94
|
+
* the HTTP layer after verifying the `DPoP:` header against this
|
|
95
|
+
* request's actual method + URI. The domain binds the issued access
|
|
96
|
+
* token to this thumbprint via `cnf.jkt`.
|
|
97
|
+
*/
|
|
98
|
+
dpopJkt?: string
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export type ExchangeCodeDeps = {
|
|
102
|
+
configStore: ConfigStore
|
|
103
|
+
tokenStore: TokenStore
|
|
104
|
+
keyStore: KeyStore
|
|
105
|
+
auditLog?: AuditLog
|
|
106
|
+
success: (input: SuccessMapInput) => Promise<SubjectClaim>
|
|
107
|
+
persistUpstreamTokens?: PersistUpstreamTokens
|
|
108
|
+
issuerUrl: string
|
|
109
|
+
clock: () => number
|
|
110
|
+
newRefreshToken?: () => string
|
|
111
|
+
/** Test override. */
|
|
112
|
+
newRefreshFamily?: () => string
|
|
113
|
+
/**
|
|
114
|
+
* Host-supplied vendor scope → claim-names map. Forwarded to `mintTokens`
|
|
115
|
+
* for id_token + /userinfo scope-gating. See `IdPOptions.customScopeClaims`.
|
|
116
|
+
*/
|
|
117
|
+
customScopeClaims?: Record<string, ReadonlyArray<string>>
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function exchangeCode(
|
|
121
|
+
req: TokenAuthCodeRequest,
|
|
122
|
+
deps: ExchangeCodeDeps,
|
|
123
|
+
): Promise<Result<TokenResponse, AuthError>> {
|
|
124
|
+
// 1. Consume code (atomic; failure here means unknown / consumed / expired).
|
|
125
|
+
// The adapter returns the ciphertext blob verbatim — encryption is
|
|
126
|
+
// the domain's job (M1), so decrypt here before reading any field.
|
|
127
|
+
const consumed = await deps.tokenStore.consumeCode(req.code)
|
|
128
|
+
if (isErr(consumed)) return err(consumed.error)
|
|
129
|
+
let payload: CodePayload
|
|
130
|
+
try {
|
|
131
|
+
payload = await decryptPayload<CodePayload>(consumed.value, async (kid) => {
|
|
132
|
+
const keyResult = await deps.keyStore.getEncryptionKey(kid)
|
|
133
|
+
if (isErr(keyResult)) {
|
|
134
|
+
throw new Error(`unknown encryption kid ${kid}`)
|
|
135
|
+
}
|
|
136
|
+
return keyResult.value.keyRef as Uint8Array
|
|
137
|
+
})
|
|
138
|
+
} catch (e) {
|
|
139
|
+
return err(authError.internalError("exchangeCode: decrypt failed", e))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 2. Load tenant + client.
|
|
143
|
+
const tenantCfg = await deps.configStore.getTenantConfig(payload.tenantId)
|
|
144
|
+
if (isErr(tenantCfg)) return err(tenantCfg.error)
|
|
145
|
+
const client = tenantCfg.value.clients.find((c) => c.id === payload.clientId)
|
|
146
|
+
if (!client) {
|
|
147
|
+
return err(authError.invalidClient(`unknown client "${payload.clientId}"`))
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 3. Client auth.
|
|
151
|
+
if (client.id !== req.clientId) {
|
|
152
|
+
return err(authError.invalidGrant("client_id mismatch with auth code"))
|
|
153
|
+
}
|
|
154
|
+
const authResult = await verifyClientCredentials(client, req.clientSecret)
|
|
155
|
+
if (authResult) return err(authResult)
|
|
156
|
+
|
|
157
|
+
// RFC 9449 §5.2 — if the client is configured to require DPoP, a
|
|
158
|
+
// bearer-only request (no DPoP header → no `dpopJkt` threaded in) is
|
|
159
|
+
// refused with `invalid_dpop_proof` before any token is minted.
|
|
160
|
+
if (client.dpopRequired && req.dpopJkt === undefined) {
|
|
161
|
+
return err(
|
|
162
|
+
authError.invalidDpopProof(
|
|
163
|
+
`client "${client.id}" requires DPoP-bound tokens`,
|
|
164
|
+
),
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 4. Redirect URI binding.
|
|
169
|
+
if (payload.appRedirectUri !== req.redirectUri) {
|
|
170
|
+
return err(authError.invalidGrant("redirect_uri mismatch with auth code"))
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// 5. PKCE check (RP → IdP).
|
|
174
|
+
if (payload.clientPkce) {
|
|
175
|
+
if (!req.codeVerifier) {
|
|
176
|
+
return err(authError.invalidGrant("missing code_verifier"))
|
|
177
|
+
}
|
|
178
|
+
const pkceValid = await validatePkce(
|
|
179
|
+
req.codeVerifier,
|
|
180
|
+
payload.clientPkce.challenge,
|
|
181
|
+
)
|
|
182
|
+
if (!pkceValid) {
|
|
183
|
+
return err(authError.invalidGrant("PKCE verification failed"))
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// 6. Build the subject claim via user's success callback.
|
|
188
|
+
const tenant: TenantContext = {
|
|
189
|
+
id: payload.tenantId,
|
|
190
|
+
config: tenantCfg.value,
|
|
191
|
+
request: { raw: new Request("about:blank"), custom: {} },
|
|
192
|
+
}
|
|
193
|
+
let claim: SubjectClaim
|
|
194
|
+
try {
|
|
195
|
+
claim = await deps.success({
|
|
196
|
+
tenant,
|
|
197
|
+
methodId: payload.methodId,
|
|
198
|
+
methodKind: payload.methodKind,
|
|
199
|
+
providerSubject: payload.providerSubject,
|
|
200
|
+
properties: payload.properties,
|
|
201
|
+
context: payload.context ?? null,
|
|
202
|
+
})
|
|
203
|
+
} catch (e) {
|
|
204
|
+
return err(authError.serverError("success callback threw", e))
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// 7. Optional upstream-tokens hook (runs after success, before
|
|
208
|
+
// mint — failed mints below should NOT roll back this hook because
|
|
209
|
+
// by contract the hook itself decides whether to persist).
|
|
210
|
+
if (deps.persistUpstreamTokens) {
|
|
211
|
+
try {
|
|
212
|
+
await deps.persistUpstreamTokens({
|
|
213
|
+
tenant,
|
|
214
|
+
methodId: payload.methodId,
|
|
215
|
+
methodKind: payload.methodKind,
|
|
216
|
+
providerSubject: payload.providerSubject,
|
|
217
|
+
properties: payload.properties,
|
|
218
|
+
subjectClaim: claim,
|
|
219
|
+
})
|
|
220
|
+
} catch (e) {
|
|
221
|
+
return err(authError.serverError("persistUpstreamTokens hook threw", e))
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// 8. Mint access + refresh (+ id_token if `openid` scope was granted).
|
|
226
|
+
const minted = await mintTokens({
|
|
227
|
+
tenant,
|
|
228
|
+
claim,
|
|
229
|
+
payload: {
|
|
230
|
+
...payload,
|
|
231
|
+
...(req.dpopJkt !== undefined ? { dpopJkt: req.dpopJkt } : {}),
|
|
232
|
+
},
|
|
233
|
+
deps,
|
|
234
|
+
family: (deps.newRefreshFamily ?? randomId)(),
|
|
235
|
+
})
|
|
236
|
+
return minted
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Shared mint logic used by both `exchangeCode` and `refreshTokens` (which
|
|
241
|
+
* passes its previous `family` through to preserve the rotation chain).
|
|
242
|
+
*/
|
|
243
|
+
export async function mintTokens(args: {
|
|
244
|
+
tenant: TenantContext
|
|
245
|
+
claim: SubjectClaim
|
|
246
|
+
payload: Pick<
|
|
247
|
+
CodePayload,
|
|
248
|
+
"tenantId" | "clientId" | "methodId" | "methodKind" | "scopes" | "audience"
|
|
249
|
+
> & {
|
|
250
|
+
/**
|
|
251
|
+
* Present on `authorization_code` and `refresh_token` grants where an
|
|
252
|
+
* end-user actually authenticated. Absent on `client_credentials`
|
|
253
|
+
* (no end-user) — and absent means no `id_token` is emitted even if
|
|
254
|
+
* the requested scopes nominally include `openid`.
|
|
255
|
+
*/
|
|
256
|
+
authTime?: number
|
|
257
|
+
/** RP's OIDC `nonce` from `/authorize`, when present. */
|
|
258
|
+
appNonce?: string
|
|
259
|
+
/**
|
|
260
|
+
* DPoP key thumbprint (RFC 9449 §6.1). When present, the access
|
|
261
|
+
* token's `cnf.jkt` claim is set and `token_type` flips to `"DPoP"`;
|
|
262
|
+
* the saved refresh-token payload's `dpopJkt` is set so refresh
|
|
263
|
+
* rotation re-enforces sender constraint.
|
|
264
|
+
*/
|
|
265
|
+
dpopJkt?: string
|
|
266
|
+
/**
|
|
267
|
+
* OIDC Core §5.5 — RP-requested claims from `/authorize`. Carried
|
|
268
|
+
* into the id_token and forward across refresh rotations (§12) so
|
|
269
|
+
* later /userinfo calls keep returning the requested fields.
|
|
270
|
+
*/
|
|
271
|
+
claimsRequest?: import("../types/authorization").ClaimsRequest
|
|
272
|
+
}
|
|
273
|
+
family: string
|
|
274
|
+
/**
|
|
275
|
+
* `client_credentials` grants (RFC 6749 §4.4.3) and other paths where a
|
|
276
|
+
* refresh token is not appropriate set `skipRefresh: true`. The response
|
|
277
|
+
* then omits `refresh_token` and **no row is written** to `TokenStore`,
|
|
278
|
+
* so the durable store doesn't accumulate orphaned refresh entries that
|
|
279
|
+
* the response strips and discards.
|
|
280
|
+
*/
|
|
281
|
+
skipRefresh?: boolean
|
|
282
|
+
deps: {
|
|
283
|
+
keyStore: KeyStore
|
|
284
|
+
tokenStore: TokenStore
|
|
285
|
+
auditLog?: AuditLog
|
|
286
|
+
issuerUrl: string
|
|
287
|
+
clock: () => number
|
|
288
|
+
newRefreshToken?: () => string
|
|
289
|
+
/**
|
|
290
|
+
* Host-supplied vendor scope → claim-names map merged into the
|
|
291
|
+
* id_token + /userinfo scope-gating. See `IdPOptions.customScopeClaims`.
|
|
292
|
+
*/
|
|
293
|
+
customScopeClaims?: Record<string, ReadonlyArray<string>>
|
|
294
|
+
}
|
|
295
|
+
}): Promise<Result<TokenResponse, AuthError>> {
|
|
296
|
+
const { tenant, claim, payload, deps, family, skipRefresh } = args
|
|
297
|
+
const accessTtl =
|
|
298
|
+
tenant.config.accessTtl !== undefined
|
|
299
|
+
? tenant.config.accessTtl * 1000
|
|
300
|
+
: DEFAULT_ACCESS_TTL_MS
|
|
301
|
+
const refreshTtl =
|
|
302
|
+
tenant.config.refreshTtl !== undefined
|
|
303
|
+
? tenant.config.refreshTtl * 1000
|
|
304
|
+
: DEFAULT_REFRESH_TTL_MS
|
|
305
|
+
const now = deps.clock()
|
|
306
|
+
// OIDC Core §8.1 — `sectorIdentifier` from the receiving client drives
|
|
307
|
+
// pairwise vs public subject derivation. Look it up off the tenant
|
|
308
|
+
// config (already loaded by the grant flow). Absent = public.
|
|
309
|
+
const receivingClient = tenant.config.clients.find(
|
|
310
|
+
(c) => c.id === payload.clientId,
|
|
311
|
+
)
|
|
312
|
+
const subjectId = await deriveSubjectId(
|
|
313
|
+
claim,
|
|
314
|
+
receivingClient?.sectorIdentifier,
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
const keyRes = await deps.keyStore.currentSigningKey()
|
|
318
|
+
if (isErr(keyRes)) return err(keyRes.error)
|
|
319
|
+
const signingKey = keyRes.value
|
|
320
|
+
|
|
321
|
+
const userinfoClaimNames = Object.keys(payload.claimsRequest?.userinfo ?? {})
|
|
322
|
+
const claims: AccessTokenClaims = {
|
|
323
|
+
iss: deps.issuerUrl,
|
|
324
|
+
sub: subjectId,
|
|
325
|
+
aud: payload.audience ?? payload.clientId,
|
|
326
|
+
exp: Math.floor((now + accessTtl) / 1000),
|
|
327
|
+
iat: Math.floor(now / 1000),
|
|
328
|
+
tid: payload.tenantId,
|
|
329
|
+
mid: payload.methodId,
|
|
330
|
+
mkind: payload.methodKind,
|
|
331
|
+
scope: payload.scopes.join(" "),
|
|
332
|
+
claim,
|
|
333
|
+
...(payload.authTime !== undefined ? { auth_time: payload.authTime } : {}),
|
|
334
|
+
...(payload.dpopJkt !== undefined ? { cnf: { jkt: payload.dpopJkt } } : {}),
|
|
335
|
+
...(userinfoClaimNames.length > 0 ? { uic: userinfoClaimNames } : {}),
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
let accessToken: string
|
|
339
|
+
try {
|
|
340
|
+
accessToken = await signAccessToken(
|
|
341
|
+
claims,
|
|
342
|
+
// jose's KeyLike covers both CryptoKey (Web Crypto) and the
|
|
343
|
+
// node:crypto KeyObject — the in-memory KeyStore stashes the result
|
|
344
|
+
// of generateKeyPair directly.
|
|
345
|
+
signingKey.privateKeyRef as Parameters<typeof signAccessToken>[1],
|
|
346
|
+
signingKey.alg,
|
|
347
|
+
signingKey.kid,
|
|
348
|
+
)
|
|
349
|
+
} catch (e) {
|
|
350
|
+
return err(authError.serverError("access token sign failed", e))
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
let refresh: string | undefined
|
|
354
|
+
if (!skipRefresh) {
|
|
355
|
+
refresh = (deps.newRefreshToken ?? randomToken)()
|
|
356
|
+
const refreshPayload: RefreshTokenPayload = {
|
|
357
|
+
tenantId: payload.tenantId,
|
|
358
|
+
clientId: payload.clientId,
|
|
359
|
+
subjectId,
|
|
360
|
+
claim,
|
|
361
|
+
scopes: payload.scopes,
|
|
362
|
+
audience: payload.audience,
|
|
363
|
+
family,
|
|
364
|
+
methodId: payload.methodId,
|
|
365
|
+
methodKind: payload.methodKind,
|
|
366
|
+
// Carry the original end-user auth time forward. Refresh-grant
|
|
367
|
+
// reissue uses this verbatim so `id_token.auth_time` is stable per
|
|
368
|
+
// OIDC Core §12 (refresh does not re-authenticate the user).
|
|
369
|
+
authTime: payload.authTime ?? Math.floor(now / 1000),
|
|
370
|
+
...(payload.dpopJkt !== undefined ? { dpopJkt: payload.dpopJkt } : {}),
|
|
371
|
+
...(payload.claimsRequest !== undefined
|
|
372
|
+
? { claimsRequest: payload.claimsRequest }
|
|
373
|
+
: {}),
|
|
374
|
+
issuedAt: now,
|
|
375
|
+
expiresAt: now + refreshTtl,
|
|
376
|
+
}
|
|
377
|
+
const saved = await deps.tokenStore.saveRefresh(refresh, refreshPayload)
|
|
378
|
+
if (isErr(saved)) return err(saved.error)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// OIDC id_token issuance — only when the grant carried an end-user
|
|
382
|
+
// (`authTime` present) AND `openid` scope was granted. Client-credentials
|
|
383
|
+
// never satisfies the first condition; refresh + code do.
|
|
384
|
+
let idToken: string | undefined
|
|
385
|
+
if (payload.authTime !== undefined && shouldIssueIdToken(payload.scopes)) {
|
|
386
|
+
const idClaims = await buildIdTokenClaims({
|
|
387
|
+
issuerUrl: deps.issuerUrl,
|
|
388
|
+
audience: payload.clientId,
|
|
389
|
+
subjectId,
|
|
390
|
+
claim,
|
|
391
|
+
scopes: payload.scopes,
|
|
392
|
+
authTime: payload.authTime,
|
|
393
|
+
...(payload.appNonce !== undefined ? { appNonce: payload.appNonce } : {}),
|
|
394
|
+
now,
|
|
395
|
+
methodKind: payload.methodKind,
|
|
396
|
+
accessToken,
|
|
397
|
+
...(payload.claimsRequest !== undefined
|
|
398
|
+
? { claimsRequest: payload.claimsRequest }
|
|
399
|
+
: {}),
|
|
400
|
+
...(deps.customScopeClaims !== undefined
|
|
401
|
+
? { customScopeClaims: deps.customScopeClaims }
|
|
402
|
+
: {}),
|
|
403
|
+
})
|
|
404
|
+
try {
|
|
405
|
+
idToken = await signIdToken(
|
|
406
|
+
idClaims,
|
|
407
|
+
signingKey.privateKeyRef as Parameters<typeof signIdToken>[1],
|
|
408
|
+
signingKey.alg,
|
|
409
|
+
signingKey.kid,
|
|
410
|
+
)
|
|
411
|
+
} catch (e) {
|
|
412
|
+
return err(authError.serverError("id_token sign failed", e))
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
await safeAudit(deps, {
|
|
417
|
+
kind: "token_issued",
|
|
418
|
+
tenantId: payload.tenantId,
|
|
419
|
+
clientId: payload.clientId,
|
|
420
|
+
methodId: payload.methodId,
|
|
421
|
+
methodKind: payload.methodKind,
|
|
422
|
+
subjectId,
|
|
423
|
+
refreshTokenIdHash: refresh ? await hashTokenForAudit(refresh) : "",
|
|
424
|
+
...(idToken !== undefined ? { idTokenIssued: true } : {}),
|
|
425
|
+
...(payload.dpopJkt !== undefined ? { dpopBound: true } : {}),
|
|
426
|
+
timestamp: now,
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
return ok({
|
|
430
|
+
access_token: accessToken,
|
|
431
|
+
token_type: payload.dpopJkt !== undefined ? "DPoP" : "Bearer",
|
|
432
|
+
expires_in: Math.floor(accessTtl / 1000),
|
|
433
|
+
...(refresh !== undefined ? { refresh_token: refresh } : {}),
|
|
434
|
+
...(idToken !== undefined ? { id_token: idToken } : {}),
|
|
435
|
+
scope: payload.scopes.join(" "),
|
|
436
|
+
})
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Phase 2 client-secret hash format: SHA-256 over UTF-8 bytes, base64url.
|
|
441
|
+
*
|
|
442
|
+
* Production deployments will move to argon2id (per cross-cutting
|
|
443
|
+
* decisions) in Phase 8. The storage shape (`ClientConfig.secretHash:
|
|
444
|
+
* string`) is unchanged; only the hashing function swaps.
|
|
445
|
+
*/
|
|
446
|
+
export async function hashClientSecret(plain: string): Promise<string> {
|
|
447
|
+
return base64url.encode(await sha256(utf8.encode(plain)))
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Derive a stable subject id from the issued `SubjectClaim`. Hash inputs
|
|
452
|
+
* are canonicalized so reordered `properties` keys hash identically.
|
|
453
|
+
*
|
|
454
|
+
* OIDC Core §8.1 — when `sectorIdentifier` is supplied, the derivation
|
|
455
|
+
* mixes it in so the resulting `sub` is **pairwise**: identical across
|
|
456
|
+
* clients sharing that sector, distinct across sectors. Absent =
|
|
457
|
+
* **public** subject (same `sub` for every RP).
|
|
458
|
+
*/
|
|
459
|
+
async function deriveSubjectId(
|
|
460
|
+
claim: SubjectClaim,
|
|
461
|
+
sectorIdentifier?: string,
|
|
462
|
+
): Promise<string> {
|
|
463
|
+
const c = claim as { type: string; properties: Record<string, unknown> }
|
|
464
|
+
const ordered = canonicalize(c.properties)
|
|
465
|
+
const seed =
|
|
466
|
+
sectorIdentifier !== undefined
|
|
467
|
+
? `${sectorIdentifier}\0${c.type}\0${ordered}`
|
|
468
|
+
: `${c.type}\0${ordered}`
|
|
469
|
+
return base64url.encode(await sha256(seed)).slice(0, 22)
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function canonicalize(value: unknown): string {
|
|
473
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
474
|
+
return JSON.stringify(value)
|
|
475
|
+
}
|
|
476
|
+
const obj = value as Record<string, unknown>
|
|
477
|
+
const keys = Object.keys(obj).sort()
|
|
478
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalize(obj[k])}`).join(",")}}`
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
async function hashTokenForAudit(token: string): Promise<string> {
|
|
482
|
+
return base64url.encode(await sha256(utf8.encode(token))).slice(0, 16)
|
|
483
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/userinfo` endpoint logic (OIDC Core §5.3). Verifies the presented
|
|
3
|
+
* access token, optionally enforces a DPoP proof binding (RFC 9449 §7),
|
|
4
|
+
* and returns the inlined `SubjectClaim` claims, scope-gated per OIDC
|
|
5
|
+
* Core §5.4.
|
|
6
|
+
*
|
|
7
|
+
* Profile / email / phone / address claims appear in the response only
|
|
8
|
+
* when (a) the access token's `scope` includes the granting scope and
|
|
9
|
+
* (b) the value exists on `SubjectClaim.properties`. Other properties
|
|
10
|
+
* (host-specific subject fields like `userId`, `roles`, …) are returned
|
|
11
|
+
* under `properties` and not subject to OIDC scope gating — they're
|
|
12
|
+
* outside §5.4's universe.
|
|
13
|
+
*
|
|
14
|
+
* DPoP at the resource server (RFC 9449 §7):
|
|
15
|
+
* - If the access token has a `cnf.jkt` claim, the request MUST carry a
|
|
16
|
+
* matching `DPoP:` proof whose `ath` equals SHA-256(access_token).
|
|
17
|
+
* - If no `cnf.jkt`, the access token is plain Bearer and any DPoP
|
|
18
|
+
* proof is silently ignored at this layer (host can still enforce).
|
|
19
|
+
*/
|
|
20
|
+
import type { TokenStore } from "../ports/token-store"
|
|
21
|
+
import type { KeyStore } from "../ports/key-store"
|
|
22
|
+
import { authError, type AuthError } from "../types/error"
|
|
23
|
+
import type { Result } from "../types/result"
|
|
24
|
+
import { err, isErr, ok } from "../types/result"
|
|
25
|
+
import type { SubjectClaim } from "../types/subject"
|
|
26
|
+
import type { AccessTokenClaims } from "../types/token"
|
|
27
|
+
|
|
28
|
+
import { computeAth, verifyDpopProof } from "./dpop"
|
|
29
|
+
import { pickScopedClaims } from "./id-token"
|
|
30
|
+
import { verifyAccessToken } from "./jwt"
|
|
31
|
+
import type { ScopedProfileClaims } from "../types/token"
|
|
32
|
+
|
|
33
|
+
export type UserinfoResponse = ScopedProfileClaims & {
|
|
34
|
+
sub: string
|
|
35
|
+
/** Subject type discriminator (e.g. `"user"` / `"admin"`). */
|
|
36
|
+
subject_type: string
|
|
37
|
+
/** Inlined claim properties, host-specific and not gated by OIDC scope. */
|
|
38
|
+
properties: Record<string, unknown>
|
|
39
|
+
/** Scope granted to this access token. */
|
|
40
|
+
scope?: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type UserinfoDeps = {
|
|
44
|
+
keyStore: KeyStore
|
|
45
|
+
/** Required when DPoP-bound tokens are accepted. */
|
|
46
|
+
tokenStore?: TokenStore
|
|
47
|
+
issuerUrl?: string
|
|
48
|
+
/**
|
|
49
|
+
* Host-supplied vendor scope → claim-names map. Merged into the
|
|
50
|
+
* scope-gated /userinfo response just as it is for id_token. See
|
|
51
|
+
* `IdPOptions.customScopeClaims`.
|
|
52
|
+
*/
|
|
53
|
+
customScopeClaims?: Record<string, ReadonlyArray<string>>
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type UserinfoInput = {
|
|
57
|
+
/** Access token from `Authorization: Bearer ...` or `Authorization: DPoP ...`. */
|
|
58
|
+
accessToken: string
|
|
59
|
+
/** `Authorization`-scheme as presented. Defaults to `"Bearer"`. */
|
|
60
|
+
scheme?: "Bearer" | "DPoP"
|
|
61
|
+
/** Raw `DPoP:` header from the request, if any. */
|
|
62
|
+
dpopProof?: string
|
|
63
|
+
/** Canonical request URI for DPoP `htu` check (scheme + host + path). */
|
|
64
|
+
htu?: string
|
|
65
|
+
/** Request method for DPoP `htm` check, uppercase. */
|
|
66
|
+
htm?: string
|
|
67
|
+
/** Wall clock seconds for DPoP `iat` window. */
|
|
68
|
+
nowSec?: number
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function userinfo(
|
|
72
|
+
input: UserinfoInput,
|
|
73
|
+
deps: UserinfoDeps,
|
|
74
|
+
): Promise<Result<UserinfoResponse, AuthError>> {
|
|
75
|
+
const keysRes = await deps.keyStore.signingKeys()
|
|
76
|
+
if (isErr(keysRes)) return err(keysRes.error)
|
|
77
|
+
|
|
78
|
+
let claims: AccessTokenClaims
|
|
79
|
+
try {
|
|
80
|
+
claims = await verifyAccessToken(input.accessToken, keysRes.value, {
|
|
81
|
+
...(deps.issuerUrl ? { issuer: deps.issuerUrl } : {}),
|
|
82
|
+
})
|
|
83
|
+
} catch {
|
|
84
|
+
return err(authError.invalidGrant("access token invalid or expired"))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// RFC 9449 §7 — DPoP-bound tokens require a matching proof at the RS.
|
|
88
|
+
const boundJkt = claims.cnf?.jkt
|
|
89
|
+
if (boundJkt !== undefined) {
|
|
90
|
+
if (input.scheme !== "DPoP") {
|
|
91
|
+
return err(
|
|
92
|
+
authError.invalidDpopProof(
|
|
93
|
+
'access token is DPoP-bound; Authorization scheme must be "DPoP"',
|
|
94
|
+
),
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
if (!input.dpopProof) {
|
|
98
|
+
return err(
|
|
99
|
+
authError.invalidDpopProof(
|
|
100
|
+
"access token is DPoP-bound; request is missing a DPoP proof",
|
|
101
|
+
),
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
if (!input.htu || !input.htm || input.nowSec === undefined) {
|
|
105
|
+
return err(
|
|
106
|
+
authError.invalidDpopProof(
|
|
107
|
+
"userinfo dpop verification requires htu, htm, and nowSec",
|
|
108
|
+
),
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
if (!deps.tokenStore) {
|
|
112
|
+
return err(
|
|
113
|
+
authError.invalidDpopProof(
|
|
114
|
+
"userinfo dpop verification requires a token-store (jti replay protection)",
|
|
115
|
+
),
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
const expectedAth = await computeAth(input.accessToken)
|
|
119
|
+
const dpopRes = await verifyDpopProof(
|
|
120
|
+
{
|
|
121
|
+
proofJwt: input.dpopProof,
|
|
122
|
+
htu: input.htu,
|
|
123
|
+
htm: input.htm,
|
|
124
|
+
nowSec: input.nowSec,
|
|
125
|
+
expectedAth,
|
|
126
|
+
},
|
|
127
|
+
{ tokenStore: deps.tokenStore },
|
|
128
|
+
)
|
|
129
|
+
if (isErr(dpopRes)) return err(dpopRes.error)
|
|
130
|
+
if (dpopRes.value.jkt !== boundJkt) {
|
|
131
|
+
return err(
|
|
132
|
+
authError.invalidDpopProof(
|
|
133
|
+
"dpop proof key does not match access token cnf.jkt",
|
|
134
|
+
),
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
} else if (input.scheme === "DPoP") {
|
|
138
|
+
// Token isn't bound but caller used the DPoP scheme — reject so a
|
|
139
|
+
// confused-deputy can't accidentally rely on RS-side enforcement.
|
|
140
|
+
return err(
|
|
141
|
+
authError.invalidGrant(
|
|
142
|
+
"access token is not DPoP-bound; use Authorization: Bearer",
|
|
143
|
+
),
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const claim = claims.claim as SubjectClaim & {
|
|
148
|
+
type: string
|
|
149
|
+
properties: Record<string, unknown>
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const scopes = claims.scope ? claims.scope.split(" ").filter(Boolean) : []
|
|
153
|
+
// OIDC Core §5.5 — claims the RP requested via `claims=...userinfo:...`
|
|
154
|
+
// bypass scope gating. Names were captured into `uic` at mint time so
|
|
155
|
+
// the resource server doesn't need to re-resolve the original /authorize
|
|
156
|
+
// request.
|
|
157
|
+
const scopedClaims = pickScopedClaims(
|
|
158
|
+
claim,
|
|
159
|
+
scopes,
|
|
160
|
+
claims.uic ?? [],
|
|
161
|
+
deps.customScopeClaims ?? {},
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
return ok({
|
|
165
|
+
sub: claims.sub,
|
|
166
|
+
subject_type: claim.type,
|
|
167
|
+
properties: claim.properties,
|
|
168
|
+
scope: claims.scope,
|
|
169
|
+
...scopedClaims,
|
|
170
|
+
})
|
|
171
|
+
}
|