@_mustachio/openauth 0.9.1 → 0.10.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 +82 -0
- package/dist/esm/adapters/memory/token-store.js +91 -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 +26 -10
- package/dist/esm/domain/audit.js +13 -0
- package/dist/esm/domain/authorize.js +212 -0
- package/dist/esm/domain/callback.js +173 -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 +41 -0
- package/dist/esm/domain/introspect.js +51 -0
- package/dist/esm/domain/jwt.js +43 -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 +108 -0
- package/dist/esm/domain/pkce.js +16 -0
- package/dist/esm/domain/refresh.js +91 -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 +101 -0
- package/dist/esm/domain/token.js +205 -0
- package/dist/esm/domain/userinfo.js +27 -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 +163 -0
- package/dist/esm/http/handlers/callback.js +53 -0
- package/dist/esm/http/handlers/metadata.js +34 -0
- package/dist/esm/http/handlers/method-route.js +87 -0
- package/dist/esm/http/handlers/revocation.js +98 -0
- package/dist/esm/http/handlers/token.js +167 -0
- package/dist/esm/http/handlers/userinfo.js +54 -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 +35 -0
- package/dist/esm/http/schemas/authorize.js +24 -0
- package/dist/esm/http/schemas/revocation.js +18 -0
- package/dist/esm/http/schemas/token.js +57 -0
- package/dist/esm/index.js +121 -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 +65 -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 +19 -0
- package/dist/types/adapters/memory/session-store.d.ts.map +1 -0
- package/dist/types/adapters/memory/token-store.d.ts +28 -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 +1 -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 +52 -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 +44 -0
- package/dist/types/domain/discovery.d.ts.map +1 -0
- package/dist/types/domain/introspect.d.ts +71 -0
- package/dist/types/domain/introspect.d.ts.map +1 -0
- package/dist/types/domain/jwt.d.ts +48 -0
- package/dist/types/domain/jwt.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/pkce.d.ts +15 -0
- package/dist/types/domain/pkce.d.ts.map +1 -0
- package/dist/types/domain/refresh.d.ts +43 -0
- package/dist/types/domain/refresh.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 +67 -0
- package/dist/types/domain/token-exchange.d.ts.map +1 -0
- package/dist/types/domain/token.d.ts +100 -0
- package/dist/types/domain/token.d.ts.map +1 -0
- package/dist/types/domain/userinfo.d.ts +22 -0
- package/dist/types/domain/userinfo.d.ts.map +1 -0
- package/dist/types/http/context.d.ts +63 -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/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/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 +52 -0
- package/dist/types/http/schemas/authorize.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 +139 -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 +82 -0
- package/dist/types/ports/session-store.d.ts.map +1 -0
- package/dist/types/ports/token-store.d.ts +93 -0
- package/dist/types/ports/token-store.d.ts.map +1 -0
- package/dist/types/types/authorization.d.ts +52 -0
- package/dist/types/types/authorization.d.ts.map +1 -0
- package/dist/types/types/error.d.ts +92 -0
- package/dist/types/types/error.d.ts.map +1 -0
- package/dist/types/types/flow.d.ts +87 -0
- package/dist/types/types/flow.d.ts.map +1 -0
- package/dist/types/types/idp.d.ts +234 -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 +239 -0
- package/dist/types/types/tenant.d.ts.map +1 -0
- package/dist/types/types/token.d.ts +103 -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 +128 -0
- package/src/adapters/memory/token-store.ts +165 -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 +50 -16
- package/src/domain/audit.ts +28 -0
- package/src/domain/authorize.ts +405 -0
- package/src/domain/callback.ts +263 -0
- package/src/domain/client-auth.ts +116 -0
- package/src/domain/client-credentials.ts +178 -0
- package/src/domain/crypto.ts +168 -0
- package/src/domain/discovery.ts +83 -0
- package/src/domain/introspect.ts +145 -0
- package/src/domain/jwt.ts +106 -0
- package/src/domain/method-cache.ts +220 -0
- package/src/domain/method-dispatch.ts +95 -0
- package/src/domain/method-route.ts +208 -0
- package/src/domain/pkce.ts +37 -0
- package/src/domain/refresh.ts +169 -0
- package/src/domain/revoke.ts +161 -0
- package/src/domain/state-envelope.ts +142 -0
- package/src/domain/token-exchange.ts +232 -0
- package/src/domain/token.ts +357 -0
- package/src/domain/userinfo.ts +56 -0
- package/src/http/context.ts +86 -0
- package/src/http/cookies.ts +167 -0
- package/src/http/errors.ts +98 -0
- package/src/http/handlers/authorize.ts +221 -0
- package/src/http/handlers/callback.ts +77 -0
- package/src/http/handlers/metadata.ts +44 -0
- package/src/http/handlers/method-route.ts +115 -0
- package/src/http/handlers/revocation.ts +151 -0
- package/src/http/handlers/token.ts +244 -0
- package/src/http/handlers/userinfo.ts +66 -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 +66 -0
- package/src/http/schemas/authorize.ts +58 -0
- package/src/http/schemas/revocation.ts +23 -0
- package/src/http/schemas/token.ts +85 -0
- package/src/index.ts +266 -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 +88 -0
- package/src/ports/audit-log.ts +148 -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 +98 -0
- package/src/ports/token-store.ts +103 -0
- package/src/types/authorization.ts +53 -0
- package/src/types/error.ts +123 -0
- package/src/types/flow.ts +89 -0
- package/src/types/idp.ts +269 -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 +248 -0
- package/src/types/token.ts +101 -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,357 @@
|
|
|
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 { signAccessToken } from "./jwt"
|
|
51
|
+
import { validatePkce } from "./pkce"
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Encrypt a `CodePayload` with the active `KeyStore` encryption key and
|
|
55
|
+
* persist it via `tokenStore.saveCode`. Centralizes the encrypt-then-store
|
|
56
|
+
* pattern so adapters never see plaintext (M1).
|
|
57
|
+
*/
|
|
58
|
+
export async function saveEncryptedCode(
|
|
59
|
+
code: string,
|
|
60
|
+
payload: CodePayload,
|
|
61
|
+
ttl: number,
|
|
62
|
+
deps: { keyStore: KeyStore; tokenStore: TokenStore },
|
|
63
|
+
): Promise<Result<void, AuthError>> {
|
|
64
|
+
const keyResult = await deps.keyStore.currentEncryptionKey()
|
|
65
|
+
if (isErr(keyResult)) return err(keyResult.error)
|
|
66
|
+
let ciphertext: string
|
|
67
|
+
try {
|
|
68
|
+
ciphertext = await encryptPayload(
|
|
69
|
+
payload,
|
|
70
|
+
keyResult.value.kid,
|
|
71
|
+
keyResult.value.keyRef as Uint8Array,
|
|
72
|
+
)
|
|
73
|
+
} catch (e) {
|
|
74
|
+
return err(authError.internalError("saveEncryptedCode: encrypt failed", e))
|
|
75
|
+
}
|
|
76
|
+
return deps.tokenStore.saveCode(code, ciphertext, ttl)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const DEFAULT_ACCESS_TTL_MS = 15 * 60 * 1000
|
|
80
|
+
export const DEFAULT_REFRESH_TTL_MS = 30 * 24 * 60 * 60 * 1000
|
|
81
|
+
|
|
82
|
+
export type TokenAuthCodeRequest = {
|
|
83
|
+
grantType: "authorization_code"
|
|
84
|
+
code: string
|
|
85
|
+
redirectUri: string
|
|
86
|
+
clientId: string
|
|
87
|
+
/** Confidential clients only. */
|
|
88
|
+
clientSecret?: string
|
|
89
|
+
/** Required if the original `/authorize` request had a `code_challenge`. */
|
|
90
|
+
codeVerifier?: string
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export type ExchangeCodeDeps = {
|
|
94
|
+
configStore: ConfigStore
|
|
95
|
+
tokenStore: TokenStore
|
|
96
|
+
keyStore: KeyStore
|
|
97
|
+
auditLog?: AuditLog
|
|
98
|
+
success: (input: SuccessMapInput) => Promise<SubjectClaim>
|
|
99
|
+
persistUpstreamTokens?: PersistUpstreamTokens
|
|
100
|
+
issuerUrl: string
|
|
101
|
+
clock: () => number
|
|
102
|
+
newRefreshToken?: () => string
|
|
103
|
+
/** Test override. */
|
|
104
|
+
newRefreshFamily?: () => string
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function exchangeCode(
|
|
108
|
+
req: TokenAuthCodeRequest,
|
|
109
|
+
deps: ExchangeCodeDeps,
|
|
110
|
+
): Promise<Result<TokenResponse, AuthError>> {
|
|
111
|
+
// 1. Consume code (atomic; failure here means unknown / consumed / expired).
|
|
112
|
+
// The adapter returns the ciphertext blob verbatim — encryption is
|
|
113
|
+
// the domain's job (M1), so decrypt here before reading any field.
|
|
114
|
+
const consumed = await deps.tokenStore.consumeCode(req.code)
|
|
115
|
+
if (isErr(consumed)) return err(consumed.error)
|
|
116
|
+
let payload: CodePayload
|
|
117
|
+
try {
|
|
118
|
+
payload = await decryptPayload<CodePayload>(consumed.value, async (kid) => {
|
|
119
|
+
const keyResult = await deps.keyStore.getEncryptionKey(kid)
|
|
120
|
+
if (isErr(keyResult)) {
|
|
121
|
+
throw new Error(`unknown encryption kid ${kid}`)
|
|
122
|
+
}
|
|
123
|
+
return keyResult.value.keyRef as Uint8Array
|
|
124
|
+
})
|
|
125
|
+
} catch (e) {
|
|
126
|
+
return err(authError.internalError("exchangeCode: decrypt failed", e))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 2. Load tenant + client.
|
|
130
|
+
const tenantCfg = await deps.configStore.getTenantConfig(payload.tenantId)
|
|
131
|
+
if (isErr(tenantCfg)) return err(tenantCfg.error)
|
|
132
|
+
const client = tenantCfg.value.clients.find((c) => c.id === payload.clientId)
|
|
133
|
+
if (!client) {
|
|
134
|
+
return err(authError.invalidClient(`unknown client "${payload.clientId}"`))
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 3. Client auth.
|
|
138
|
+
if (client.id !== req.clientId) {
|
|
139
|
+
return err(authError.invalidGrant("client_id mismatch with auth code"))
|
|
140
|
+
}
|
|
141
|
+
const authResult = await verifyClientCredentials(client, req.clientSecret)
|
|
142
|
+
if (authResult) return err(authResult)
|
|
143
|
+
|
|
144
|
+
// 4. Redirect URI binding.
|
|
145
|
+
if (payload.appRedirectUri !== req.redirectUri) {
|
|
146
|
+
return err(authError.invalidGrant("redirect_uri mismatch with auth code"))
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 5. PKCE check (RP → IdP).
|
|
150
|
+
if (payload.clientPkce) {
|
|
151
|
+
if (!req.codeVerifier) {
|
|
152
|
+
return err(authError.invalidGrant("missing code_verifier"))
|
|
153
|
+
}
|
|
154
|
+
const pkceValid = await validatePkce(
|
|
155
|
+
req.codeVerifier,
|
|
156
|
+
payload.clientPkce.challenge,
|
|
157
|
+
)
|
|
158
|
+
if (!pkceValid) {
|
|
159
|
+
return err(authError.invalidGrant("PKCE verification failed"))
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 6. Build the subject claim via user's success callback.
|
|
164
|
+
const tenant: TenantContext = {
|
|
165
|
+
id: payload.tenantId,
|
|
166
|
+
config: tenantCfg.value,
|
|
167
|
+
request: { raw: new Request("about:blank"), custom: {} },
|
|
168
|
+
}
|
|
169
|
+
let claim: SubjectClaim
|
|
170
|
+
try {
|
|
171
|
+
claim = await deps.success({
|
|
172
|
+
tenant,
|
|
173
|
+
methodId: payload.methodId,
|
|
174
|
+
methodKind: payload.methodKind,
|
|
175
|
+
providerSubject: payload.providerSubject,
|
|
176
|
+
properties: payload.properties,
|
|
177
|
+
context: payload.context ?? null,
|
|
178
|
+
})
|
|
179
|
+
} catch (e) {
|
|
180
|
+
return err(authError.serverError("success callback threw", e))
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 7. Optional upstream-tokens hook (runs after success, before
|
|
184
|
+
// mint — failed mints below should NOT roll back this hook because
|
|
185
|
+
// by contract the hook itself decides whether to persist).
|
|
186
|
+
if (deps.persistUpstreamTokens) {
|
|
187
|
+
try {
|
|
188
|
+
await deps.persistUpstreamTokens({
|
|
189
|
+
tenant,
|
|
190
|
+
methodId: payload.methodId,
|
|
191
|
+
methodKind: payload.methodKind,
|
|
192
|
+
providerSubject: payload.providerSubject,
|
|
193
|
+
properties: payload.properties,
|
|
194
|
+
subjectClaim: claim,
|
|
195
|
+
})
|
|
196
|
+
} catch (e) {
|
|
197
|
+
return err(authError.serverError("persistUpstreamTokens hook threw", e))
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 8. Mint access + refresh.
|
|
202
|
+
const minted = await mintTokens({
|
|
203
|
+
tenant,
|
|
204
|
+
claim,
|
|
205
|
+
payload,
|
|
206
|
+
deps,
|
|
207
|
+
family: (deps.newRefreshFamily ?? randomId)(),
|
|
208
|
+
})
|
|
209
|
+
return minted
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Shared mint logic used by both `exchangeCode` and `refreshTokens` (which
|
|
214
|
+
* passes its previous `family` through to preserve the rotation chain).
|
|
215
|
+
*/
|
|
216
|
+
export async function mintTokens(args: {
|
|
217
|
+
tenant: TenantContext
|
|
218
|
+
claim: SubjectClaim
|
|
219
|
+
payload: Pick<
|
|
220
|
+
CodePayload,
|
|
221
|
+
"tenantId" | "clientId" | "methodId" | "methodKind" | "scopes" | "audience"
|
|
222
|
+
>
|
|
223
|
+
family: string
|
|
224
|
+
/**
|
|
225
|
+
* `client_credentials` grants (RFC 6749 §4.4.3) and other paths where a
|
|
226
|
+
* refresh token is not appropriate set `skipRefresh: true`. The response
|
|
227
|
+
* then omits `refresh_token` and **no row is written** to `TokenStore`,
|
|
228
|
+
* so the durable store doesn't accumulate orphaned refresh entries that
|
|
229
|
+
* the response strips and discards.
|
|
230
|
+
*/
|
|
231
|
+
skipRefresh?: boolean
|
|
232
|
+
deps: {
|
|
233
|
+
keyStore: KeyStore
|
|
234
|
+
tokenStore: TokenStore
|
|
235
|
+
auditLog?: AuditLog
|
|
236
|
+
issuerUrl: string
|
|
237
|
+
clock: () => number
|
|
238
|
+
newRefreshToken?: () => string
|
|
239
|
+
}
|
|
240
|
+
}): Promise<Result<TokenResponse, AuthError>> {
|
|
241
|
+
const { tenant, claim, payload, deps, family, skipRefresh } = args
|
|
242
|
+
const accessTtl =
|
|
243
|
+
tenant.config.accessTtl !== undefined
|
|
244
|
+
? tenant.config.accessTtl * 1000
|
|
245
|
+
: DEFAULT_ACCESS_TTL_MS
|
|
246
|
+
const refreshTtl =
|
|
247
|
+
tenant.config.refreshTtl !== undefined
|
|
248
|
+
? tenant.config.refreshTtl * 1000
|
|
249
|
+
: DEFAULT_REFRESH_TTL_MS
|
|
250
|
+
const now = deps.clock()
|
|
251
|
+
const subjectId = await deriveSubjectId(claim)
|
|
252
|
+
|
|
253
|
+
const keyRes = await deps.keyStore.currentSigningKey()
|
|
254
|
+
if (isErr(keyRes)) return err(keyRes.error)
|
|
255
|
+
const signingKey = keyRes.value
|
|
256
|
+
|
|
257
|
+
const claims: AccessTokenClaims = {
|
|
258
|
+
iss: deps.issuerUrl,
|
|
259
|
+
sub: subjectId,
|
|
260
|
+
aud: payload.audience ?? payload.clientId,
|
|
261
|
+
exp: Math.floor((now + accessTtl) / 1000),
|
|
262
|
+
iat: Math.floor(now / 1000),
|
|
263
|
+
tid: payload.tenantId,
|
|
264
|
+
mid: payload.methodId,
|
|
265
|
+
mkind: payload.methodKind,
|
|
266
|
+
scope: payload.scopes.join(" "),
|
|
267
|
+
claim,
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
let accessToken: string
|
|
271
|
+
try {
|
|
272
|
+
accessToken = await signAccessToken(
|
|
273
|
+
claims,
|
|
274
|
+
// jose's KeyLike covers both CryptoKey (Web Crypto) and the
|
|
275
|
+
// node:crypto KeyObject — the in-memory KeyStore stashes the result
|
|
276
|
+
// of generateKeyPair directly.
|
|
277
|
+
signingKey.privateKeyRef as Parameters<typeof signAccessToken>[1],
|
|
278
|
+
signingKey.alg,
|
|
279
|
+
signingKey.kid,
|
|
280
|
+
)
|
|
281
|
+
} catch (e) {
|
|
282
|
+
return err(authError.serverError("access token sign failed", e))
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
let refresh: string | undefined
|
|
286
|
+
if (!skipRefresh) {
|
|
287
|
+
refresh = (deps.newRefreshToken ?? randomToken)()
|
|
288
|
+
const refreshPayload: RefreshTokenPayload = {
|
|
289
|
+
tenantId: payload.tenantId,
|
|
290
|
+
clientId: payload.clientId,
|
|
291
|
+
subjectId,
|
|
292
|
+
claim,
|
|
293
|
+
scopes: payload.scopes,
|
|
294
|
+
audience: payload.audience,
|
|
295
|
+
family,
|
|
296
|
+
methodId: payload.methodId,
|
|
297
|
+
methodKind: payload.methodKind,
|
|
298
|
+
issuedAt: now,
|
|
299
|
+
expiresAt: now + refreshTtl,
|
|
300
|
+
}
|
|
301
|
+
const saved = await deps.tokenStore.saveRefresh(refresh, refreshPayload)
|
|
302
|
+
if (isErr(saved)) return err(saved.error)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
await safeAudit(deps, {
|
|
306
|
+
kind: "token_issued",
|
|
307
|
+
tenantId: payload.tenantId,
|
|
308
|
+
clientId: payload.clientId,
|
|
309
|
+
methodId: payload.methodId,
|
|
310
|
+
methodKind: payload.methodKind,
|
|
311
|
+
subjectId,
|
|
312
|
+
refreshTokenIdHash: refresh ? await hashTokenForAudit(refresh) : "",
|
|
313
|
+
timestamp: now,
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
return ok({
|
|
317
|
+
access_token: accessToken,
|
|
318
|
+
token_type: "Bearer",
|
|
319
|
+
expires_in: Math.floor(accessTtl / 1000),
|
|
320
|
+
...(refresh !== undefined ? { refresh_token: refresh } : {}),
|
|
321
|
+
scope: payload.scopes.join(" "),
|
|
322
|
+
})
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Phase 2 client-secret hash format: SHA-256 over UTF-8 bytes, base64url.
|
|
327
|
+
*
|
|
328
|
+
* Production deployments will move to argon2id (per cross-cutting
|
|
329
|
+
* decisions) in Phase 8. The storage shape (`ClientConfig.secretHash:
|
|
330
|
+
* string`) is unchanged; only the hashing function swaps.
|
|
331
|
+
*/
|
|
332
|
+
export async function hashClientSecret(plain: string): Promise<string> {
|
|
333
|
+
return base64url.encode(await sha256(utf8.encode(plain)))
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Derive a stable subject id from the issued `SubjectClaim`. Hash inputs
|
|
338
|
+
* are canonicalized so reordered `properties` keys hash identically.
|
|
339
|
+
*/
|
|
340
|
+
async function deriveSubjectId(claim: SubjectClaim): Promise<string> {
|
|
341
|
+
const c = claim as { type: string; properties: Record<string, unknown> }
|
|
342
|
+
const ordered = canonicalize(c.properties)
|
|
343
|
+
return base64url.encode(await sha256(`${c.type}\0${ordered}`)).slice(0, 22)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function canonicalize(value: unknown): string {
|
|
347
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
348
|
+
return JSON.stringify(value)
|
|
349
|
+
}
|
|
350
|
+
const obj = value as Record<string, unknown>
|
|
351
|
+
const keys = Object.keys(obj).sort()
|
|
352
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalize(obj[k])}`).join(",")}}`
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async function hashTokenForAudit(token: string): Promise<string> {
|
|
356
|
+
return base64url.encode(await sha256(utf8.encode(token))).slice(0, 16)
|
|
357
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/userinfo` endpoint logic (OIDC Core §5.3). Verifies the bearer access
|
|
3
|
+
* token and returns the inlined `SubjectClaim` claims.
|
|
4
|
+
*/
|
|
5
|
+
import type { KeyStore } from "../ports/key-store"
|
|
6
|
+
import { authError, type AuthError } from "../types/error"
|
|
7
|
+
import type { Result } from "../types/result"
|
|
8
|
+
import { err, isErr, ok } from "../types/result"
|
|
9
|
+
import type { SubjectClaim } from "../types/subject"
|
|
10
|
+
import type { AccessTokenClaims } from "../types/token"
|
|
11
|
+
|
|
12
|
+
import { verifyAccessToken } from "./jwt"
|
|
13
|
+
|
|
14
|
+
export type UserinfoResponse = {
|
|
15
|
+
sub: string
|
|
16
|
+
/** Subject type discriminator (e.g. `"user"` / `"admin"`). */
|
|
17
|
+
subject_type: string
|
|
18
|
+
/** Inlined claim properties. */
|
|
19
|
+
properties: Record<string, unknown>
|
|
20
|
+
/** Scope granted to this access token. */
|
|
21
|
+
scope?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type UserinfoDeps = {
|
|
25
|
+
keyStore: KeyStore
|
|
26
|
+
issuerUrl?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function userinfo(
|
|
30
|
+
bearerToken: string,
|
|
31
|
+
deps: UserinfoDeps,
|
|
32
|
+
): Promise<Result<UserinfoResponse, AuthError>> {
|
|
33
|
+
const keysRes = await deps.keyStore.signingKeys()
|
|
34
|
+
if (isErr(keysRes)) return err(keysRes.error)
|
|
35
|
+
|
|
36
|
+
let claims: AccessTokenClaims
|
|
37
|
+
try {
|
|
38
|
+
claims = await verifyAccessToken(bearerToken, keysRes.value, {
|
|
39
|
+
...(deps.issuerUrl ? { issuer: deps.issuerUrl } : {}),
|
|
40
|
+
})
|
|
41
|
+
} catch {
|
|
42
|
+
return err(authError.invalidGrant("access token invalid or expired"))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const claim = claims.claim as SubjectClaim & {
|
|
46
|
+
type: string
|
|
47
|
+
properties: Record<string, unknown>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return ok({
|
|
51
|
+
sub: claims.sub,
|
|
52
|
+
subject_type: claim.type,
|
|
53
|
+
properties: claim.properties,
|
|
54
|
+
scope: claims.scope,
|
|
55
|
+
})
|
|
56
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared deps + per-request context for the HTTP layer.
|
|
3
|
+
*
|
|
4
|
+
* `HttpDeps` is constructed once by `createIdP` and threaded through every
|
|
5
|
+
* handler. `HttpVars` is the per-request Hono-variable shape: tenant
|
|
6
|
+
* resolution, parsed cookies, optional recovery info from the callback chain.
|
|
7
|
+
*/
|
|
8
|
+
import type { Context } from "hono"
|
|
9
|
+
|
|
10
|
+
import type { AuditLog } from "../ports/audit-log"
|
|
11
|
+
import type { ConfigStore } from "../ports/config-store"
|
|
12
|
+
import type { KeyStore } from "../ports/key-store"
|
|
13
|
+
import type { MethodStore } from "../ports/method-store"
|
|
14
|
+
import type { SessionStore } from "../ports/session-store"
|
|
15
|
+
import type { TokenStore } from "../ports/token-store"
|
|
16
|
+
import type { MethodCache } from "../domain/method-cache"
|
|
17
|
+
import type { AuthError } from "../types/error"
|
|
18
|
+
import type {
|
|
19
|
+
ExchangeAudience,
|
|
20
|
+
PersistUpstreamTokens,
|
|
21
|
+
RenderPicker,
|
|
22
|
+
SuccessMapInput,
|
|
23
|
+
} from "../types/idp"
|
|
24
|
+
import type { Result } from "../types/result"
|
|
25
|
+
import type { SubjectClaim } from "../types/subject"
|
|
26
|
+
import type {
|
|
27
|
+
StateKeyRing,
|
|
28
|
+
TenantContext,
|
|
29
|
+
TenantId,
|
|
30
|
+
TenantRecovery,
|
|
31
|
+
} from "../types/tenant"
|
|
32
|
+
|
|
33
|
+
import type { CookieDefaults } from "./cookies"
|
|
34
|
+
|
|
35
|
+
/** Long-lived deps the HTTP layer shares across requests. */
|
|
36
|
+
export type HttpDeps = {
|
|
37
|
+
configStore: ConfigStore
|
|
38
|
+
tokenStore: TokenStore
|
|
39
|
+
sessionStore: SessionStore
|
|
40
|
+
keyStore: KeyStore
|
|
41
|
+
methodStore?: MethodStore
|
|
42
|
+
auditLog?: AuditLog
|
|
43
|
+
methodCache: MethodCache
|
|
44
|
+
stateKeys: StateKeyRing
|
|
45
|
+
/** Resolves the issuer URL — string or per-request function. */
|
|
46
|
+
resolveIssuer: (req: Request) => string
|
|
47
|
+
/** Optional partitioned-host helper (recovery #2). */
|
|
48
|
+
callbackHostFor?: (tenantId: TenantId) => string
|
|
49
|
+
resolveTenant: (req: Request) => Promise<Result<TenantId, AuthError>>
|
|
50
|
+
success: (input: SuccessMapInput) => Promise<SubjectClaim>
|
|
51
|
+
persistUpstreamTokens?: PersistUpstreamTokens
|
|
52
|
+
exchangeAudience?: ExchangeAudience
|
|
53
|
+
renderPicker?: RenderPicker
|
|
54
|
+
/** Builds `TenantContext.request.custom` for every request. See `IdPOptions.buildCustomContext`. */
|
|
55
|
+
buildCustomContext?: (
|
|
56
|
+
req: Request,
|
|
57
|
+
) => Record<string, unknown> | Promise<Record<string, unknown>>
|
|
58
|
+
clock: () => number
|
|
59
|
+
cookieDefaults: CookieDefaults
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Per-request variables populated by middleware. */
|
|
63
|
+
export type HttpVars = {
|
|
64
|
+
/** Parsed `Cookie:` header. */
|
|
65
|
+
cookies: Map<string, string>
|
|
66
|
+
/** Populated by the tenant middleware. May be `null` for unauthenticated public endpoints. */
|
|
67
|
+
tenant: TenantContext | null
|
|
68
|
+
/** Outcome of the callback-recovery chain on `/cb/*` requests. */
|
|
69
|
+
recovery: TenantRecovery | null
|
|
70
|
+
/** Issuer URL for this request — pre-resolved by middleware. */
|
|
71
|
+
issuerUrl: string
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type HttpEnv = {
|
|
75
|
+
Variables: HttpVars
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type HttpContext = Context<HttpEnv>
|
|
79
|
+
|
|
80
|
+
/** Convenience: build the initial `HttpVars` value for a request. */
|
|
81
|
+
export function initialVars(
|
|
82
|
+
issuerUrl: string,
|
|
83
|
+
cookies: Map<string, string>,
|
|
84
|
+
): HttpVars {
|
|
85
|
+
return { cookies, tenant: null, recovery: null, issuerUrl }
|
|
86
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cookie parsing + serialization for the HTTP layer.
|
|
3
|
+
*
|
|
4
|
+
* The framework owns cookie policy (per `ARCHITECTURE.md` §"Response
|
|
5
|
+
* sanitization"). Methods never set cookies through `Response.headers`; they
|
|
6
|
+
* return `SetCookie[]` data and this module renders them. We additionally
|
|
7
|
+
* strip method-returned `Set-Cookie` / security / `Cache-Control` headers so
|
|
8
|
+
* methods cannot bypass policy.
|
|
9
|
+
*/
|
|
10
|
+
import type { SetCookie } from "../types/method"
|
|
11
|
+
|
|
12
|
+
/** Parse an incoming `Cookie:` header into a read-only map. */
|
|
13
|
+
export function parseCookieHeader(header: string | null): Map<string, string> {
|
|
14
|
+
const out = new Map<string, string>()
|
|
15
|
+
if (!header) return out
|
|
16
|
+
for (const part of header.split(";")) {
|
|
17
|
+
const trimmed = part.trim()
|
|
18
|
+
if (!trimmed) continue
|
|
19
|
+
const eq = trimmed.indexOf("=")
|
|
20
|
+
if (eq === -1) {
|
|
21
|
+
out.set(trimmed, "")
|
|
22
|
+
continue
|
|
23
|
+
}
|
|
24
|
+
const name = trimmed.slice(0, eq).trim()
|
|
25
|
+
const raw = trimmed.slice(eq + 1).trim()
|
|
26
|
+
const value =
|
|
27
|
+
raw.startsWith('"') && raw.endsWith('"') ? raw.slice(1, -1) : raw
|
|
28
|
+
try {
|
|
29
|
+
out.set(name, decodeURIComponent(value))
|
|
30
|
+
} catch {
|
|
31
|
+
out.set(name, value)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return out
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type CookieDefaults = {
|
|
38
|
+
/** Secure flag default. Production should pass `true`. */
|
|
39
|
+
secure?: boolean
|
|
40
|
+
domain?: string
|
|
41
|
+
path?: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const RESERVED_PREFIX = /^(auth|idp)\./
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Characters explicitly forbidden in a cookie name. RFC 6265 §4.1.1 defers
|
|
48
|
+
* to RFC 2616's `token` grammar (no separators, no CTLs); rather than the
|
|
49
|
+
* full grammar we reject the chars most likely to corrupt the header on
|
|
50
|
+
* splice: `=`, `;`, `,`, and any whitespace. Method-returned cookie names
|
|
51
|
+
* are the realistic source of bad input here — internal callers use fixed
|
|
52
|
+
* `auth.*` / `idp.*` names that pass.
|
|
53
|
+
*/
|
|
54
|
+
const INVALID_COOKIE_NAME = /[=;,\s]/
|
|
55
|
+
|
|
56
|
+
function assertValidCookieName(name: string): void {
|
|
57
|
+
if (!name || INVALID_COOKIE_NAME.test(name)) {
|
|
58
|
+
throw new TypeError(
|
|
59
|
+
`invalid cookie name ${JSON.stringify(name)} — must be non-empty and contain no '=', ';', ',' or whitespace`,
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Serialize a single `SetCookie` data instruction into a `Set-Cookie` header
|
|
66
|
+
* string. Enforces framework policy:
|
|
67
|
+
* - `Secure` forced on if `defaults.secure === true`.
|
|
68
|
+
* - `SameSite` defaults to `Lax` if unspecified.
|
|
69
|
+
* - `HttpOnly` defaults to `true` for any cookie name in the `auth.*` /
|
|
70
|
+
* `idp.*` reserved namespace.
|
|
71
|
+
* - `null` value → `Max-Age=0` to clear.
|
|
72
|
+
*/
|
|
73
|
+
export function serializeSetCookie(
|
|
74
|
+
cookie: SetCookie,
|
|
75
|
+
defaults: CookieDefaults = {},
|
|
76
|
+
): string {
|
|
77
|
+
assertValidCookieName(cookie.name)
|
|
78
|
+
const parts: string[] = []
|
|
79
|
+
const value = cookie.value ?? ""
|
|
80
|
+
parts.push(`${cookie.name}=${encodeURIComponent(value)}`)
|
|
81
|
+
|
|
82
|
+
const path = cookie.path ?? defaults.path ?? "/"
|
|
83
|
+
parts.push(`Path=${path}`)
|
|
84
|
+
|
|
85
|
+
const domain = cookie.domain ?? defaults.domain
|
|
86
|
+
if (domain) parts.push(`Domain=${domain}`)
|
|
87
|
+
|
|
88
|
+
if (cookie.value === null) {
|
|
89
|
+
parts.push("Max-Age=0")
|
|
90
|
+
} else if (cookie.maxAge !== undefined) {
|
|
91
|
+
parts.push(`Max-Age=${cookie.maxAge}`)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const secure = cookie.secure ?? defaults.secure ?? false
|
|
95
|
+
if (secure) parts.push("Secure")
|
|
96
|
+
|
|
97
|
+
const httpOnly = cookie.httpOnly ?? RESERVED_PREFIX.test(cookie.name)
|
|
98
|
+
if (httpOnly) parts.push("HttpOnly")
|
|
99
|
+
|
|
100
|
+
const sameSite = cookie.sameSite ?? "lax"
|
|
101
|
+
parts.push(`SameSite=${sameSite.charAt(0).toUpperCase() + sameSite.slice(1)}`)
|
|
102
|
+
|
|
103
|
+
return parts.join("; ")
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Headers the framework strips from any method-returned `Response`. */
|
|
107
|
+
const STRIPPED_HEADERS = [
|
|
108
|
+
"set-cookie",
|
|
109
|
+
"set-cookie2",
|
|
110
|
+
"strict-transport-security",
|
|
111
|
+
"content-security-policy",
|
|
112
|
+
"content-security-policy-report-only",
|
|
113
|
+
"x-frame-options",
|
|
114
|
+
"x-content-type-options",
|
|
115
|
+
"referrer-policy",
|
|
116
|
+
"permissions-policy",
|
|
117
|
+
"cache-control",
|
|
118
|
+
] as const
|
|
119
|
+
|
|
120
|
+
export type ApplyOptions = {
|
|
121
|
+
setCookies?: SetCookie[]
|
|
122
|
+
cookieDefaults?: CookieDefaults
|
|
123
|
+
/** When set, framework writes this `Cache-Control` value. Default `no-store`. */
|
|
124
|
+
cacheControl?: string
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Sanitize a method-returned `Response` and apply framework-owned headers.
|
|
129
|
+
*
|
|
130
|
+
* Returns a new `Response` so the original is left untouched. When the
|
|
131
|
+
* method's response carried any of the stripped headers, emit a
|
|
132
|
+
* `console.warn` so a programmer notices their header got dropped
|
|
133
|
+
* silently. (Per ARCHITECTURE.md §"Response sanitization"; switches to
|
|
134
|
+
* the Logger port when that lands in Phase 8.)
|
|
135
|
+
*/
|
|
136
|
+
export function applyResponsePolicy(
|
|
137
|
+
response: Response,
|
|
138
|
+
opts: ApplyOptions = {},
|
|
139
|
+
): Response {
|
|
140
|
+
const headers = new Headers(response.headers)
|
|
141
|
+
const stripped: string[] = []
|
|
142
|
+
for (const h of STRIPPED_HEADERS) {
|
|
143
|
+
if (headers.has(h)) {
|
|
144
|
+
stripped.push(h)
|
|
145
|
+
headers.delete(h)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (stripped.length > 0) {
|
|
149
|
+
console.warn(
|
|
150
|
+
`[openauth] dropped method-returned header(s) ${stripped.join(", ")} — the framework owns cookie/security/cache headers; methods should return cookies via MethodResult.setCookies.`,
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
headers.set("Cache-Control", opts.cacheControl ?? "no-store")
|
|
155
|
+
|
|
156
|
+
if (opts.setCookies) {
|
|
157
|
+
for (const c of opts.setCookies) {
|
|
158
|
+
headers.append("Set-Cookie", serializeSetCookie(c, opts.cookieDefaults))
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return new Response(response.body, {
|
|
163
|
+
status: response.status,
|
|
164
|
+
statusText: response.statusText,
|
|
165
|
+
headers,
|
|
166
|
+
})
|
|
167
|
+
}
|