@_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,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DynamoDB `TokenStore`. Single-table design (`pk` / `sk` primary key,
|
|
3
|
+
* `family-index` and `subject-index` GSIs).
|
|
4
|
+
*
|
|
5
|
+
* Per `ports/CONSISTENCY.md`:
|
|
6
|
+
* - All `get`s use `consistentRead: true` — the default eventual-read
|
|
7
|
+
* would violate the auth-code / refresh-token contracts.
|
|
8
|
+
* - All `delete`s use `ReturnValues=ALL_OLD` so atomic delete-on-read is
|
|
9
|
+
* a single round trip.
|
|
10
|
+
* - `consumeRefresh` uses a conditional `UpdateItem` with
|
|
11
|
+
* `attribute_not_exists(consumed_at)` so concurrent presentations
|
|
12
|
+
* resolve to one winner.
|
|
13
|
+
*
|
|
14
|
+
* Layout:
|
|
15
|
+
* - `pk="code"`, `sk=<code>` — auth-code ciphertext blob (the
|
|
16
|
+
* domain encrypts before saveCode; the adapter stores verbatim).
|
|
17
|
+
* - `pk="refresh"`, `sk=<token>` — refresh token + JSON payload.
|
|
18
|
+
* GSI `family-index`: hash = `family`
|
|
19
|
+
* GSI `subject-index`: hash = `subject_key` (`<tenant>#<subject>`)
|
|
20
|
+
*/
|
|
21
|
+
import { AUTH_CODE_TTL_MS } from "../../domain/authorize"
|
|
22
|
+
import type { KeyStore } from "../../ports/key-store"
|
|
23
|
+
import type { TokenStore } from "../../ports/token-store"
|
|
24
|
+
import { authError } from "../../types/error"
|
|
25
|
+
import type { Result } from "../../types/result"
|
|
26
|
+
import { err, ok } from "../../types/result"
|
|
27
|
+
import type { TenantId } from "../../types/tenant"
|
|
28
|
+
import type { RefreshTokenPayload } from "../../types/token"
|
|
29
|
+
|
|
30
|
+
import type { DynamoExecutor } from "./client"
|
|
31
|
+
|
|
32
|
+
export type DynamoTokenStoreOptions = {
|
|
33
|
+
exec: DynamoExecutor
|
|
34
|
+
/**
|
|
35
|
+
* Optional — retained for API stability after M1 moved code-payload
|
|
36
|
+
* encryption to the domain layer. The adapter no longer touches it.
|
|
37
|
+
*/
|
|
38
|
+
keyStore?: KeyStore
|
|
39
|
+
clock?: () => number
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class DynamoTokenStore implements TokenStore {
|
|
43
|
+
#exec: DynamoExecutor
|
|
44
|
+
#clock: () => number
|
|
45
|
+
|
|
46
|
+
constructor(opts: DynamoTokenStoreOptions) {
|
|
47
|
+
this.#exec = opts.exec
|
|
48
|
+
this.#clock = opts.clock ?? (() => Date.now())
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async saveCode(
|
|
52
|
+
code: string,
|
|
53
|
+
ciphertext: string,
|
|
54
|
+
ttl: number,
|
|
55
|
+
): Promise<Result<void>> {
|
|
56
|
+
if (ttl <= 0 || ttl > AUTH_CODE_TTL_MS) {
|
|
57
|
+
return err(
|
|
58
|
+
authError.internalError(
|
|
59
|
+
`saveCode: ttl ${ttl} outside (0, ${AUTH_CODE_TTL_MS}] (auth-code TTL is fixed at 60s by OAuth 2.1 BCP)`,
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
const expiresAtMs = this.#clock() + ttl
|
|
64
|
+
try {
|
|
65
|
+
await this.#exec.put({
|
|
66
|
+
item: {
|
|
67
|
+
pk: "code",
|
|
68
|
+
sk: code,
|
|
69
|
+
ciphertext,
|
|
70
|
+
expires_at: expiresAtMs,
|
|
71
|
+
// DynamoDB native TTL is in seconds, named conventionally `ttl`.
|
|
72
|
+
// The adapter writes both so operators can enable TTL on the table.
|
|
73
|
+
ttl: Math.floor(expiresAtMs / 1000),
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
} catch (e) {
|
|
77
|
+
return err(authError.internalError("saveCode: put failed", e))
|
|
78
|
+
}
|
|
79
|
+
return ok(undefined)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async consumeCode(code: string): Promise<Result<string>> {
|
|
83
|
+
let row: Record<string, unknown> | undefined
|
|
84
|
+
try {
|
|
85
|
+
row = await this.#exec.delete({ key: { pk: "code", sk: code } })
|
|
86
|
+
} catch (e) {
|
|
87
|
+
return err(authError.internalError("consumeCode: delete failed", e))
|
|
88
|
+
}
|
|
89
|
+
if (!row) {
|
|
90
|
+
return err(
|
|
91
|
+
authError.invalidGrant("auth code unknown or already consumed"),
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
if (this.#clock() >= Number(row.expires_at)) {
|
|
95
|
+
return err(authError.invalidGrant("auth code expired"))
|
|
96
|
+
}
|
|
97
|
+
return ok(String(row.ciphertext))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async saveRefresh(
|
|
101
|
+
refresh: string,
|
|
102
|
+
payload: RefreshTokenPayload,
|
|
103
|
+
): Promise<Result<void>> {
|
|
104
|
+
try {
|
|
105
|
+
await this.#exec.put({
|
|
106
|
+
item: {
|
|
107
|
+
pk: "refresh",
|
|
108
|
+
sk: refresh,
|
|
109
|
+
payload: JSON.stringify(payload),
|
|
110
|
+
family: payload.family,
|
|
111
|
+
subject_key: `${payload.tenantId}#${payload.subjectId}`,
|
|
112
|
+
tenant_id: payload.tenantId,
|
|
113
|
+
subject_id: payload.subjectId,
|
|
114
|
+
expires_at: payload.expiresAt,
|
|
115
|
+
ttl: Math.floor(payload.expiresAt / 1000),
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
} catch (e) {
|
|
119
|
+
return err(authError.internalError("saveRefresh: put failed", e))
|
|
120
|
+
}
|
|
121
|
+
return ok(undefined)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async consumeRefresh(
|
|
125
|
+
refresh: string,
|
|
126
|
+
options: { reuseWindowMs?: number } = {},
|
|
127
|
+
): Promise<Result<RefreshTokenPayload>> {
|
|
128
|
+
const reuseWindow = options.reuseWindowMs ?? 60_000
|
|
129
|
+
const now = this.#clock()
|
|
130
|
+
let claimed: Record<string, unknown> | null
|
|
131
|
+
try {
|
|
132
|
+
claimed = await this.#exec.consumeRefresh({
|
|
133
|
+
key: { pk: "refresh", sk: refresh },
|
|
134
|
+
now,
|
|
135
|
+
})
|
|
136
|
+
} catch (e) {
|
|
137
|
+
return err(authError.internalError("consumeRefresh: claim failed", e))
|
|
138
|
+
}
|
|
139
|
+
if (claimed) {
|
|
140
|
+
return ok(parseRefresh(claimed.payload))
|
|
141
|
+
}
|
|
142
|
+
// No claim. Look up to disambiguate unknown vs already-consumed vs expired.
|
|
143
|
+
let existing: Record<string, unknown> | undefined
|
|
144
|
+
try {
|
|
145
|
+
existing = await this.#exec.get({
|
|
146
|
+
key: { pk: "refresh", sk: refresh },
|
|
147
|
+
consistentRead: true,
|
|
148
|
+
})
|
|
149
|
+
} catch (e) {
|
|
150
|
+
return err(authError.internalError("consumeRefresh: lookup failed", e))
|
|
151
|
+
}
|
|
152
|
+
if (!existing) {
|
|
153
|
+
return err(authError.invalidGrant("refresh token unknown"))
|
|
154
|
+
}
|
|
155
|
+
if (existing.consumed_at !== undefined && existing.consumed_at !== null) {
|
|
156
|
+
const consumedAt = Number(existing.consumed_at)
|
|
157
|
+
const withinWindow = now - consumedAt <= reuseWindow
|
|
158
|
+
if (withinWindow) {
|
|
159
|
+
const payload = parseRefresh(existing.payload)
|
|
160
|
+
await this.revokeFamily(payload.family)
|
|
161
|
+
return err(
|
|
162
|
+
authError.invalidGrant(
|
|
163
|
+
`refresh token reuse detected (family=${payload.family})`,
|
|
164
|
+
{
|
|
165
|
+
family: payload.family,
|
|
166
|
+
tenantId: payload.tenantId,
|
|
167
|
+
subjectId: payload.subjectId,
|
|
168
|
+
},
|
|
169
|
+
),
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
return err(authError.invalidGrant("refresh token already consumed"))
|
|
173
|
+
}
|
|
174
|
+
if (now >= Number(existing.expires_at)) {
|
|
175
|
+
return err(authError.invalidGrant("refresh token expired"))
|
|
176
|
+
}
|
|
177
|
+
return err(authError.internalError("consumeRefresh: unexplained miss"))
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async peekRefresh(refresh: string): Promise<Result<RefreshTokenPayload>> {
|
|
181
|
+
let existing: Record<string, unknown> | undefined
|
|
182
|
+
try {
|
|
183
|
+
existing = await this.#exec.get({
|
|
184
|
+
key: { pk: "refresh", sk: refresh },
|
|
185
|
+
consistentRead: true,
|
|
186
|
+
})
|
|
187
|
+
} catch (e) {
|
|
188
|
+
return err(authError.internalError("peekRefresh: lookup failed", e))
|
|
189
|
+
}
|
|
190
|
+
if (!existing) {
|
|
191
|
+
return err(authError.invalidGrant("refresh token unknown"))
|
|
192
|
+
}
|
|
193
|
+
if (this.#clock() >= Number(existing.expires_at)) {
|
|
194
|
+
return err(authError.invalidGrant("refresh token expired"))
|
|
195
|
+
}
|
|
196
|
+
return ok(parseRefresh(existing.payload))
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async revokeFamily(family: string): Promise<Result<void>> {
|
|
200
|
+
let items: Record<string, unknown>[]
|
|
201
|
+
try {
|
|
202
|
+
items = await this.#exec.queryByGsi({
|
|
203
|
+
indexName: "family-index",
|
|
204
|
+
hashKey: family,
|
|
205
|
+
})
|
|
206
|
+
} catch (e) {
|
|
207
|
+
return err(authError.internalError("revokeFamily: query failed", e))
|
|
208
|
+
}
|
|
209
|
+
for (const item of items) {
|
|
210
|
+
try {
|
|
211
|
+
await this.#exec.delete({
|
|
212
|
+
key: { pk: String(item.pk), sk: String(item.sk) },
|
|
213
|
+
})
|
|
214
|
+
} catch {
|
|
215
|
+
// Best-effort — a deletion losing a race is acceptable.
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return ok(undefined)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async revokeBySubject(
|
|
222
|
+
tenantId: TenantId,
|
|
223
|
+
subjectId: string,
|
|
224
|
+
): Promise<Result<void>> {
|
|
225
|
+
let items: Record<string, unknown>[]
|
|
226
|
+
try {
|
|
227
|
+
items = await this.#exec.queryByGsi({
|
|
228
|
+
indexName: "subject-index",
|
|
229
|
+
hashKey: `${tenantId}#${subjectId}`,
|
|
230
|
+
})
|
|
231
|
+
} catch (e) {
|
|
232
|
+
return err(authError.internalError("revokeBySubject: query failed", e))
|
|
233
|
+
}
|
|
234
|
+
for (const item of items) {
|
|
235
|
+
try {
|
|
236
|
+
await this.#exec.delete({
|
|
237
|
+
key: { pk: String(item.pk), sk: String(item.sk) },
|
|
238
|
+
})
|
|
239
|
+
} catch {}
|
|
240
|
+
}
|
|
241
|
+
return ok(undefined)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function parseRefresh(raw: unknown): RefreshTokenPayload {
|
|
246
|
+
if (typeof raw === "string") return JSON.parse(raw) as RefreshTokenPayload
|
|
247
|
+
return raw as RefreshTokenPayload
|
|
248
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory `KmsBackingStore`. Used by the KMS adapter conformance test and
|
|
3
|
+
* for single-instance dev. Production deployments pass a real backing store
|
|
4
|
+
* (Postgres, DynamoDB, S3-with-versioning, etc.) so KMS-wrapped key
|
|
5
|
+
* envelopes survive process restarts.
|
|
6
|
+
*/
|
|
7
|
+
import type {
|
|
8
|
+
KmsBackingStore,
|
|
9
|
+
WrappedEncryptionKey,
|
|
10
|
+
WrappedSigningKey,
|
|
11
|
+
} from "./types"
|
|
12
|
+
|
|
13
|
+
export function inMemoryKmsBackingStore(): KmsBackingStore {
|
|
14
|
+
const signing = new Map<string, WrappedSigningKey>()
|
|
15
|
+
const encryption = new Map<string, WrappedEncryptionKey>()
|
|
16
|
+
return {
|
|
17
|
+
async putSigningKey(row) {
|
|
18
|
+
signing.set(row.kid, row)
|
|
19
|
+
},
|
|
20
|
+
async getSigningKey(kid) {
|
|
21
|
+
return signing.get(kid)
|
|
22
|
+
},
|
|
23
|
+
async listSigningKeys() {
|
|
24
|
+
return Array.from(signing.values())
|
|
25
|
+
},
|
|
26
|
+
async putEncryptionKey(row) {
|
|
27
|
+
encryption.set(row.kid, row)
|
|
28
|
+
},
|
|
29
|
+
async getEncryptionKey(kid) {
|
|
30
|
+
return encryption.get(kid)
|
|
31
|
+
},
|
|
32
|
+
async listEncryptionKeys() {
|
|
33
|
+
return Array.from(encryption.values())
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KMS-backed `KeyStore` adapter set.
|
|
3
|
+
*
|
|
4
|
+
* Envelope encryption: private JWK material is wrapped under an AWS KMS
|
|
5
|
+
* master key and stored in a pluggable backing store (`KmsBackingStore`).
|
|
6
|
+
* KMS Encrypt / Decrypt are the only KMS operations on the hot path; the
|
|
7
|
+
* framework still receives a real `CryptoKey` for JWT signing so no surgery
|
|
8
|
+
* in `domain/jwt.ts` is needed.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* import { KMSClient } from "@aws-sdk/client-kms"
|
|
12
|
+
* import {
|
|
13
|
+
* fromKmsClient,
|
|
14
|
+
* inMemoryKmsBackingStore,
|
|
15
|
+
* KmsKeyStore,
|
|
16
|
+
* } from "@_mustachio/openauth/adapters/kms"
|
|
17
|
+
*
|
|
18
|
+
* const kms = fromKmsClient(
|
|
19
|
+
* new KMSClient({ region: "us-east-1" }),
|
|
20
|
+
* process.env.IDP_KMS_KEY_ARN!,
|
|
21
|
+
* )
|
|
22
|
+
* // For production, swap in a persistent KmsBackingStore (Postgres,
|
|
23
|
+
* // DynamoDB, etc.). The in-memory store is appropriate for single-instance
|
|
24
|
+
* // dev only.
|
|
25
|
+
* const keyStore = new KmsKeyStore({ kms, backing: inMemoryKmsBackingStore() })
|
|
26
|
+
*
|
|
27
|
+
* Operators with strict no-plaintext-cache requirements can disable the
|
|
28
|
+
* unwrapped-key cache via `cacheUnwrappedKeys: false` — every read then
|
|
29
|
+
* round-trips to KMS.
|
|
30
|
+
*/
|
|
31
|
+
export { inMemoryKmsBackingStore } from "./in-memory-backing"
|
|
32
|
+
export { KmsKeyStore, type KmsKeyStoreOptions } from "./key-store"
|
|
33
|
+
export { fromKmsClient } from "./sdk"
|
|
34
|
+
export type {
|
|
35
|
+
KmsBackingStore,
|
|
36
|
+
KmsClientLike,
|
|
37
|
+
WrappedEncryptionKey,
|
|
38
|
+
WrappedSigningKey,
|
|
39
|
+
} from "./types"
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KMS-backed `KeyStore`.
|
|
3
|
+
*
|
|
4
|
+
* Envelope encryption: generate a JWK key pair (or AES key) locally, wrap
|
|
5
|
+
* the private material with AWS KMS `Encrypt`, store the ciphertext in the
|
|
6
|
+
* supplied `KmsBackingStore`. On read, unwrap with KMS `Decrypt` and
|
|
7
|
+
* `importJWK` so the framework receives a normal `CryptoKey` for JWT signing
|
|
8
|
+
* — no surgery in `domain/jwt.ts`.
|
|
9
|
+
*
|
|
10
|
+
* Hot-path KMS calls are cached: signing keys are imported once per
|
|
11
|
+
* `kid` and re-used (a `CryptoKey` is safe to share across signs). Encryption
|
|
12
|
+
* keys are cached in plaintext bytes per `kid` so `consumeCode` doesn't make
|
|
13
|
+
* a KMS call per request.
|
|
14
|
+
*
|
|
15
|
+
* Operators with strict no-cache-of-plaintext requirements can disable the
|
|
16
|
+
* cache via `cacheUnwrappedKeys: false` — every read then round-trips to
|
|
17
|
+
* KMS.
|
|
18
|
+
*/
|
|
19
|
+
import type { JWK } from "jose"
|
|
20
|
+
import { exportJWK, generateKeyPair, importJWK } from "jose"
|
|
21
|
+
|
|
22
|
+
import { generateSymmetricKey, randomId, utf8 } from "../../domain/crypto"
|
|
23
|
+
import type { EncryptionKey, KeyStore, SigningKey } from "../../ports/key-store"
|
|
24
|
+
import { authError } from "../../types/error"
|
|
25
|
+
import type { Result } from "../../types/result"
|
|
26
|
+
import { err, ok } from "../../types/result"
|
|
27
|
+
|
|
28
|
+
import type {
|
|
29
|
+
KmsBackingStore,
|
|
30
|
+
KmsClientLike,
|
|
31
|
+
WrappedEncryptionKey,
|
|
32
|
+
WrappedSigningKey,
|
|
33
|
+
} from "./types"
|
|
34
|
+
|
|
35
|
+
export type KmsKeyStoreOptions = {
|
|
36
|
+
kms: KmsClientLike
|
|
37
|
+
backing: KmsBackingStore
|
|
38
|
+
clock?: () => number
|
|
39
|
+
signingAlg?: "ES256" | "EdDSA"
|
|
40
|
+
/** Default `true`. Set false for strict no-plaintext-cache deployments. */
|
|
41
|
+
cacheUnwrappedKeys?: boolean
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export class KmsKeyStore implements KeyStore {
|
|
45
|
+
#kms: KmsClientLike
|
|
46
|
+
#backing: KmsBackingStore
|
|
47
|
+
#clock: () => number
|
|
48
|
+
#signingAlg: "ES256" | "EdDSA"
|
|
49
|
+
#cacheUnwrapped: boolean
|
|
50
|
+
#signingCache = new Map<string, unknown>()
|
|
51
|
+
#encryptionCache = new Map<string, Uint8Array>()
|
|
52
|
+
|
|
53
|
+
constructor(opts: KmsKeyStoreOptions) {
|
|
54
|
+
this.#kms = opts.kms
|
|
55
|
+
this.#backing = opts.backing
|
|
56
|
+
this.#clock = opts.clock ?? (() => Date.now())
|
|
57
|
+
this.#signingAlg = opts.signingAlg ?? "ES256"
|
|
58
|
+
this.#cacheUnwrapped = opts.cacheUnwrappedKeys ?? true
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async currentSigningKey(): Promise<Result<SigningKey>> {
|
|
62
|
+
await this.#ensureSigningKey()
|
|
63
|
+
const all = await this.#backing.listSigningKeys()
|
|
64
|
+
const active = all.find((k) => k.status === "active")
|
|
65
|
+
if (!active) {
|
|
66
|
+
return err(
|
|
67
|
+
authError.internalError(
|
|
68
|
+
"no active signing key (should be unreachable)",
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
return ok(await this.#hydrateSigning(active))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async signingKeys(): Promise<Result<SigningKey[]>> {
|
|
76
|
+
await this.#ensureSigningKey()
|
|
77
|
+
const all = await this.#backing.listSigningKeys()
|
|
78
|
+
const keys = await Promise.all(all.map((row) => this.#hydrateSigning(row)))
|
|
79
|
+
return ok(keys)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async currentEncryptionKey(): Promise<Result<EncryptionKey>> {
|
|
83
|
+
await this.#ensureEncryptionKey()
|
|
84
|
+
const all = await this.#backing.listEncryptionKeys()
|
|
85
|
+
const active = all.find((k) => k.status === "active")
|
|
86
|
+
if (!active) {
|
|
87
|
+
return err(
|
|
88
|
+
authError.internalError(
|
|
89
|
+
"no active encryption key (should be unreachable)",
|
|
90
|
+
),
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
return ok(await this.#hydrateEncryption(active))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async getEncryptionKey(kid: string): Promise<Result<EncryptionKey>> {
|
|
97
|
+
const row = await this.#backing.getEncryptionKey(kid)
|
|
98
|
+
if (!row) {
|
|
99
|
+
return err(authError.internalError(`encryption key "${kid}" not found`))
|
|
100
|
+
}
|
|
101
|
+
return ok(await this.#hydrateEncryption(row))
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async #ensureSigningKey(): Promise<void> {
|
|
105
|
+
const all = await this.#backing.listSigningKeys()
|
|
106
|
+
if (all.some((k) => k.status === "active")) return
|
|
107
|
+
const { publicKey, privateKey } = await generateKeyPair(this.#signingAlg, {
|
|
108
|
+
extractable: true,
|
|
109
|
+
})
|
|
110
|
+
const publicJwk = await exportJWK(publicKey)
|
|
111
|
+
const privateJwk = await exportJWK(privateKey)
|
|
112
|
+
const plaintext = utf8.encode(JSON.stringify(privateJwk))
|
|
113
|
+
const wrapped = await this.#kms.encrypt(plaintext)
|
|
114
|
+
const row: WrappedSigningKey = {
|
|
115
|
+
kid: randomId(),
|
|
116
|
+
alg: this.#signingAlg,
|
|
117
|
+
...(publicJwk.crv ? { crv: publicJwk.crv } : {}),
|
|
118
|
+
publicJwk: publicJwk as unknown as Record<string, unknown>,
|
|
119
|
+
wrappedPrivate: wrapped,
|
|
120
|
+
status: "active",
|
|
121
|
+
createdAt: this.#clock(),
|
|
122
|
+
}
|
|
123
|
+
await this.#backing.putSigningKey(row)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async #ensureEncryptionKey(): Promise<void> {
|
|
127
|
+
const all = await this.#backing.listEncryptionKeys()
|
|
128
|
+
if (all.some((k) => k.status === "active")) return
|
|
129
|
+
const raw = generateSymmetricKey()
|
|
130
|
+
const wrapped = await this.#kms.encrypt(raw)
|
|
131
|
+
const row: WrappedEncryptionKey = {
|
|
132
|
+
kid: randomId(),
|
|
133
|
+
alg: "A256GCM",
|
|
134
|
+
wrappedKey: wrapped,
|
|
135
|
+
status: "active",
|
|
136
|
+
createdAt: this.#clock(),
|
|
137
|
+
}
|
|
138
|
+
await this.#backing.putEncryptionKey(row)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async #hydrateSigning(row: WrappedSigningKey): Promise<SigningKey> {
|
|
142
|
+
let imported = this.#signingCache.get(row.kid)
|
|
143
|
+
if (!imported) {
|
|
144
|
+
const plaintext = await this.#kms.decrypt(row.wrappedPrivate)
|
|
145
|
+
const jwk = JSON.parse(utf8.decode(plaintext)) as JWK
|
|
146
|
+
imported = await importJWK(jwk, row.alg)
|
|
147
|
+
if (this.#cacheUnwrapped) this.#signingCache.set(row.kid, imported)
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
kid: row.kid,
|
|
151
|
+
alg: row.alg,
|
|
152
|
+
...(row.crv ? { crv: row.crv } : {}),
|
|
153
|
+
publicJwk: row.publicJwk,
|
|
154
|
+
privateKeyRef: imported,
|
|
155
|
+
status: row.status,
|
|
156
|
+
createdAt: row.createdAt,
|
|
157
|
+
...(row.rotatedAt !== undefined ? { rotatedAt: row.rotatedAt } : {}),
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async #hydrateEncryption(row: WrappedEncryptionKey): Promise<EncryptionKey> {
|
|
162
|
+
let bytes = this.#encryptionCache.get(row.kid)
|
|
163
|
+
if (!bytes) {
|
|
164
|
+
bytes = await this.#kms.decrypt(row.wrappedKey)
|
|
165
|
+
if (this.#cacheUnwrapped) this.#encryptionCache.set(row.kid, bytes)
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
kid: row.kid,
|
|
169
|
+
alg: row.alg,
|
|
170
|
+
keyRef: bytes,
|
|
171
|
+
status: row.status,
|
|
172
|
+
createdAt: row.createdAt,
|
|
173
|
+
...(row.rotatedAt !== undefined ? { rotatedAt: row.rotatedAt } : {}),
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fromKmsClient` — wires the `KmsClientLike` interface to a real AWS KMS
|
|
3
|
+
* client. The KMS SDK is imported only from this file to keep the rest of
|
|
4
|
+
* the adapter bundle KMS-agnostic.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* import { KMSClient } from "@aws-sdk/client-kms"
|
|
8
|
+
* const kmsClient = new KMSClient({ region: "us-east-1" })
|
|
9
|
+
* const kms = fromKmsClient(kmsClient, process.env.IDP_KMS_KEY_ARN!)
|
|
10
|
+
* const backing = new PostgresKmsBackingStore({ exec })
|
|
11
|
+
* const keyStore = new KmsKeyStore({ kms, backing })
|
|
12
|
+
*/
|
|
13
|
+
import type { KMSClient } from "@aws-sdk/client-kms"
|
|
14
|
+
import { DecryptCommand, EncryptCommand } from "@aws-sdk/client-kms"
|
|
15
|
+
|
|
16
|
+
import type { KmsClientLike } from "./types"
|
|
17
|
+
|
|
18
|
+
export function fromKmsClient(client: KMSClient, keyId: string): KmsClientLike {
|
|
19
|
+
return {
|
|
20
|
+
async encrypt(plaintext: Uint8Array): Promise<Uint8Array> {
|
|
21
|
+
const result = await client.send(
|
|
22
|
+
new EncryptCommand({ KeyId: keyId, Plaintext: plaintext }),
|
|
23
|
+
)
|
|
24
|
+
if (!result.CiphertextBlob) {
|
|
25
|
+
throw new Error("KMS Encrypt returned no ciphertext")
|
|
26
|
+
}
|
|
27
|
+
return new Uint8Array(result.CiphertextBlob)
|
|
28
|
+
},
|
|
29
|
+
async decrypt(ciphertext: Uint8Array): Promise<Uint8Array> {
|
|
30
|
+
const result = await client.send(
|
|
31
|
+
new DecryptCommand({ KeyId: keyId, CiphertextBlob: ciphertext }),
|
|
32
|
+
)
|
|
33
|
+
if (!result.Plaintext) {
|
|
34
|
+
throw new Error("KMS Decrypt returned no plaintext")
|
|
35
|
+
}
|
|
36
|
+
return new Uint8Array(result.Plaintext)
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KMS adapter interfaces.
|
|
3
|
+
*
|
|
4
|
+
* The KMS-backed `KeyStore` uses **envelope encryption** for private key
|
|
5
|
+
* material: the JWK private bytes (signing) and the raw AES key bytes
|
|
6
|
+
* (encryption) are wrapped under a KMS master key. The ciphertext blob is
|
|
7
|
+
* stored in a pluggable backing store; KMS Encrypt / Decrypt are the only
|
|
8
|
+
* KMS operations on the hot path.
|
|
9
|
+
*
|
|
10
|
+
* This shape keeps private material out of plaintext at rest while
|
|
11
|
+
* preserving the JWT signing path: the framework still receives a real
|
|
12
|
+
* `CryptoKey` (`importJWK` of the unwrapped JWK) so `domain/jwt.ts` doesn't
|
|
13
|
+
* need to know KMS exists.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Minimal KMS surface the adapter depends on. */
|
|
17
|
+
export type KmsClientLike = {
|
|
18
|
+
/** Encrypt up to ~4 KB of plaintext under the master key. */
|
|
19
|
+
encrypt(plaintext: Uint8Array): Promise<Uint8Array>
|
|
20
|
+
/** Decrypt a previously-issued ciphertext under the master key. */
|
|
21
|
+
decrypt(ciphertext: Uint8Array): Promise<Uint8Array>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Persistent storage for KMS-wrapped key envelopes. Adapters provide their
|
|
26
|
+
* own (Postgres, DynamoDB, file), or pass `inMemoryKmsBackingStore()` for
|
|
27
|
+
* tests / single-instance dev.
|
|
28
|
+
*
|
|
29
|
+
* `wrappedPrivate` is the KMS-wrapped private material (JWK bytes for
|
|
30
|
+
* signing keys, raw AES key bytes for encryption keys). `publicJwk` is the
|
|
31
|
+
* public material (never sensitive); it's kept plaintext alongside the
|
|
32
|
+
* ciphertext so JWKS lookups don't need a KMS round trip.
|
|
33
|
+
*/
|
|
34
|
+
export type KmsBackingStore = {
|
|
35
|
+
putSigningKey(row: WrappedSigningKey): Promise<void>
|
|
36
|
+
getSigningKey(kid: string): Promise<WrappedSigningKey | undefined>
|
|
37
|
+
listSigningKeys(): Promise<WrappedSigningKey[]>
|
|
38
|
+
putEncryptionKey(row: WrappedEncryptionKey): Promise<void>
|
|
39
|
+
getEncryptionKey(kid: string): Promise<WrappedEncryptionKey | undefined>
|
|
40
|
+
listEncryptionKeys(): Promise<WrappedEncryptionKey[]>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type WrappedSigningKey = {
|
|
44
|
+
kid: string
|
|
45
|
+
alg: string
|
|
46
|
+
crv?: string
|
|
47
|
+
publicJwk: Record<string, unknown>
|
|
48
|
+
wrappedPrivate: Uint8Array
|
|
49
|
+
status: "active" | "next" | "retired"
|
|
50
|
+
createdAt: number
|
|
51
|
+
rotatedAt?: number
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type WrappedEncryptionKey = {
|
|
55
|
+
kid: string
|
|
56
|
+
alg: string
|
|
57
|
+
wrappedKey: Uint8Array
|
|
58
|
+
status: "active" | "next" | "retired"
|
|
59
|
+
createdAt: number
|
|
60
|
+
rotatedAt?: number
|
|
61
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare KV `AuditLog`. Append-only with per-event keys of the shape
|
|
3
|
+
* `audit:<paddedTs>:<rand>` so KV's lexicographic list returns events in
|
|
4
|
+
* insertion order. Suitable for low-volume audit (the IdP) where reads are
|
|
5
|
+
* SIEM-style after-the-fact, not hot-path. High-volume deployments should
|
|
6
|
+
* ship events out-of-band to a real append-log.
|
|
7
|
+
*/
|
|
8
|
+
import type { AuditEvent, AuditLog } from "../../ports/audit-log"
|
|
9
|
+
|
|
10
|
+
import type { KVNamespaceLike } from "./types"
|
|
11
|
+
|
|
12
|
+
const PREFIX = "audit:"
|
|
13
|
+
|
|
14
|
+
export type KvAuditLogOptions = {
|
|
15
|
+
kv: KVNamespaceLike
|
|
16
|
+
/** Optional TTL (seconds). 90 days minimum per CONSISTENCY.md SOC 2 baseline. */
|
|
17
|
+
expirationTtl?: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class KvAuditLog implements AuditLog {
|
|
21
|
+
#kv: KVNamespaceLike
|
|
22
|
+
#ttl: number | undefined
|
|
23
|
+
|
|
24
|
+
constructor(opts: KvAuditLogOptions) {
|
|
25
|
+
this.#kv = opts.kv
|
|
26
|
+
this.#ttl = opts.expirationTtl
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async log(event: AuditEvent & { timestamp: number }): Promise<void> {
|
|
30
|
+
const padded = String(event.timestamp).padStart(16, "0")
|
|
31
|
+
const suffix = Math.random().toString(36).slice(2, 10)
|
|
32
|
+
try {
|
|
33
|
+
await this.#kv.put(
|
|
34
|
+
`${PREFIX}${padded}:${suffix}`,
|
|
35
|
+
JSON.stringify(event),
|
|
36
|
+
this.#ttl ? { expirationTtl: this.#ttl } : undefined,
|
|
37
|
+
)
|
|
38
|
+
} catch {}
|
|
39
|
+
}
|
|
40
|
+
}
|