@robelest/convex-auth 0.0.4-preview.25 → 0.0.4-preview.28
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/README.md +43 -36
- package/dist/bin.js +5765 -4880
- package/dist/browser/index.d.ts +30 -0
- package/dist/browser/index.js +93 -0
- package/dist/browser/locks.js +11 -0
- package/dist/browser/navigation.js +14 -0
- package/dist/{factors → browser}/passkey.js +23 -32
- package/dist/browser/runtime.js +92 -0
- package/dist/client/core/types.d.ts +452 -5
- package/dist/client/core/types.js +17 -0
- package/dist/client/errors.js +19 -0
- package/dist/client/factors/device.js +94 -0
- package/dist/{factors → client/factors}/totp.js +12 -4
- package/dist/client/index.d.ts +47 -1
- package/dist/client/index.js +269 -232
- package/dist/client/runtime/mutex.js +24 -0
- package/dist/client/runtime/proxy.js +30 -0
- package/dist/client/runtime/storage.js +45 -0
- package/dist/client/services/adapters.js +7 -0
- package/dist/client/services/http.js +6 -0
- package/dist/client/services/resolve.js +13 -0
- package/dist/client/services/runtime.js +6 -0
- package/dist/component/_generated/component.d.ts +1355 -1399
- package/dist/component/convex.config.d.ts +2 -2
- package/dist/component/index.d.ts +4 -26
- package/dist/component/index.js +1 -1
- package/dist/component/model.d.ts +26 -112
- package/dist/component/model.js +76 -54
- package/dist/component/modules.js +38 -0
- package/dist/component/public/factors/devices.js +1 -1
- package/dist/component/public/factors/passkeys.js +1 -1
- package/dist/component/public/factors/totp.js +1 -1
- package/dist/component/public/groups/core.js +2 -2
- package/dist/component/public/groups/invites.js +1 -1
- package/dist/component/public/groups/members.js +1 -1
- package/dist/component/public/identity/accounts.js +1 -1
- package/dist/component/public/identity/codes.js +1 -1
- package/dist/component/public/identity/sessions.js +39 -2
- package/dist/component/public/identity/tokens.js +82 -4
- package/dist/component/public/identity/users.js +1 -1
- package/dist/component/public/identity/verifiers.js +10 -4
- package/dist/component/public/security/keys.js +1 -1
- package/dist/component/public/security/limits.js +1 -1
- package/dist/component/public/{enterprise → sso}/audit.js +26 -26
- package/dist/component/public/sso/core.js +263 -0
- package/dist/component/public/sso/domains.js +280 -0
- package/dist/component/public/{enterprise → sso}/scim.js +87 -87
- package/dist/component/public/sso/secrets.js +125 -0
- package/dist/component/public/{enterprise → sso}/webhooks.js +59 -59
- package/dist/component/public.js +9 -9
- package/dist/component/schema.d.ts +472 -393
- package/dist/component/schema.js +36 -35
- package/dist/core/index.d.ts +380 -0
- package/dist/core/index.js +83 -0
- package/dist/otel.d.ts +69 -0
- package/dist/otel.js +82 -0
- package/dist/providers/anonymous.d.ts +15 -34
- package/dist/providers/anonymous.js +27 -35
- package/dist/providers/apple.d.ts +59 -0
- package/dist/providers/apple.js +58 -0
- package/dist/providers/credentials.d.ts +18 -34
- package/dist/providers/credentials.js +16 -27
- package/dist/providers/custom.d.ts +94 -0
- package/dist/providers/custom.js +119 -0
- package/dist/providers/device.d.ts +15 -49
- package/dist/providers/device.js +17 -34
- package/dist/providers/email.d.ts +21 -38
- package/dist/providers/email.js +36 -55
- package/dist/providers/github.d.ts +54 -0
- package/dist/providers/github.js +75 -0
- package/dist/providers/google.d.ts +54 -0
- package/dist/providers/google.js +61 -0
- package/dist/providers/index.d.ts +16 -12
- package/dist/providers/index.js +15 -11
- package/dist/providers/microsoft.d.ts +57 -0
- package/dist/providers/microsoft.js +101 -0
- package/dist/providers/passkey.d.ts +19 -35
- package/dist/providers/passkey.js +20 -30
- package/dist/providers/password.d.ts +17 -18
- package/dist/providers/password.js +121 -143
- package/dist/providers/phone.d.ts +13 -28
- package/dist/providers/phone.js +21 -46
- package/dist/providers/sso.d.ts +16 -36
- package/dist/providers/sso.js +21 -22
- package/dist/providers/totp.d.ts +13 -29
- package/dist/providers/totp.js +17 -27
- package/dist/server/auth-context.d.ts +204 -0
- package/dist/server/auth-context.js +76 -0
- package/dist/server/auth.d.ts +99 -244
- package/dist/server/auth.js +56 -152
- package/dist/server/componentContext.d.ts +12 -0
- package/dist/server/componentContext.js +1 -0
- package/dist/server/config.js +6 -67
- package/dist/server/constants.js +6 -0
- package/dist/server/contract.d.ts +105 -0
- package/dist/server/contract.js +43 -0
- package/dist/server/cookies.js +3 -2
- package/dist/server/core.js +31 -36
- package/dist/server/crypto.js +34 -44
- package/dist/server/db.js +6 -1
- package/dist/server/device.js +96 -130
- package/dist/server/env.js +48 -0
- package/dist/server/errors.js +20 -0
- package/dist/server/http.d.ts +15 -59
- package/dist/server/http.js +136 -120
- package/dist/server/identity.js +2 -2
- package/dist/server/index.d.ts +5 -4
- package/dist/server/index.js +3 -3
- package/dist/server/keys.js +10 -1
- package/dist/server/limits.js +26 -26
- package/dist/server/log.js +28 -0
- package/dist/server/mounts.d.ts +1107 -296
- package/dist/server/mounts.js +315 -196
- package/dist/server/mutations/account.js +11 -14
- package/dist/server/mutations/code.js +6 -5
- package/dist/server/mutations/invalidate.js +9 -11
- package/dist/server/mutations/oauth.js +112 -73
- package/dist/server/mutations/refresh.js +47 -97
- package/dist/server/mutations/register.js +37 -35
- package/dist/server/mutations/retrieve.js +16 -16
- package/dist/server/mutations/signature.js +15 -18
- package/dist/server/mutations/signin.js +10 -5
- package/dist/server/mutations/signout.js +11 -14
- package/dist/server/mutations/store.js +25 -18
- package/dist/server/mutations/verifier.js +11 -8
- package/dist/server/mutations/verify.js +53 -41
- package/dist/server/oauth/factory.js +44 -0
- package/dist/server/oauth/index.js +12 -0
- package/dist/server/oauth/runtime.js +248 -0
- package/dist/server/passkey.js +331 -365
- package/dist/server/payloads.d.ts +16 -0
- package/dist/server/payloads.js +30 -0
- package/dist/server/{ssr.d.ts → prefetch.d.ts} +2 -2
- package/dist/server/prefetch.js +635 -0
- package/dist/server/random.js +19 -0
- package/dist/server/redirects.js +10 -5
- package/dist/server/refresh.js +14 -86
- package/dist/server/runtime.d.ts +531 -31
- package/dist/server/runtime.js +106 -267
- package/dist/server/secret.js +44 -0
- package/dist/server/services/config.js +10 -0
- package/dist/server/services/group.js +211 -0
- package/dist/server/services/logger.js +8 -0
- package/dist/server/services/providers.js +22 -0
- package/dist/server/services/refresh.js +8 -0
- package/dist/server/services/resolve.js +27 -0
- package/dist/server/services/signin.js +8 -0
- package/dist/server/sessions.js +35 -34
- package/dist/server/signin.js +229 -140
- package/dist/server/{enterprise → sso}/config.js +10 -3
- package/dist/server/sso/domain.d.ts +614 -0
- package/dist/server/sso/domain.js +1175 -0
- package/dist/server/sso/http.js +1060 -0
- package/dist/server/sso/oidc.js +324 -0
- package/dist/server/sso/policies.js +59 -0
- package/dist/server/sso/policy.js +139 -0
- package/dist/server/sso/profile.js +22 -0
- package/dist/server/sso/provision.js +179 -0
- package/dist/{component/server/enterprise → server/sso}/saml.js +142 -56
- package/dist/{component/server/enterprise → server/sso}/scim.js +13 -7
- package/dist/server/sso/shared.js +74 -0
- package/dist/server/sso/validators.js +88 -0
- package/dist/server/sso/webhook.js +94 -0
- package/dist/server/tokens.js +16 -4
- package/dist/server/totp.js +155 -164
- package/dist/server/types.d.ts +306 -296
- package/dist/server/types.js +1 -30
- package/dist/server/url.js +32 -0
- package/dist/server/users.js +74 -40
- package/dist/server/utils/cache.js +51 -0
- package/dist/server/utils/dispatch.js +36 -0
- package/dist/server/utils/retry.js +24 -0
- package/dist/server/utils/span.js +32 -0
- package/dist/shared/errors.js +19 -0
- package/dist/shared/log.js +45 -0
- package/{src/test.ts → dist/test.d.ts} +21 -22
- package/dist/test.js +51 -0
- package/package.json +70 -42
- package/dist/authorization/index.d.ts.map +0 -1
- package/dist/authorization/index.js.map +0 -1
- package/dist/client/core/types.d.ts.map +0 -1
- package/dist/client/index.d.ts.map +0 -1
- package/dist/client/index.js.map +0 -1
- package/dist/component/_generated/api.d.ts +0 -75
- package/dist/component/_generated/api.d.ts.map +0 -1
- package/dist/component/_generated/api.js.map +0 -1
- package/dist/component/_generated/component.d.ts.map +0 -1
- package/dist/component/_generated/dataModel.d.ts +0 -42
- package/dist/component/_generated/dataModel.d.ts.map +0 -1
- package/dist/component/_generated/server.d.ts +0 -117
- package/dist/component/_generated/server.d.ts.map +0 -1
- package/dist/component/_generated/server.js.map +0 -1
- package/dist/component/_virtual/rolldown_runtime.js +0 -18
- package/dist/component/client/core/types.d.ts +0 -2
- package/dist/component/client/index.d.ts +0 -1
- package/dist/component/convex.config.d.ts.map +0 -1
- package/dist/component/convex.config.js.map +0 -1
- package/dist/component/functions.d.ts +0 -25
- package/dist/component/functions.d.ts.map +0 -1
- package/dist/component/functions.js.map +0 -1
- package/dist/component/index.d.ts.map +0 -1
- package/dist/component/model.d.ts.map +0 -1
- package/dist/component/model.js.map +0 -1
- package/dist/component/providers/anonymous.d.ts +0 -54
- package/dist/component/providers/anonymous.d.ts.map +0 -1
- package/dist/component/providers/credentials.d.ts +0 -38
- package/dist/component/providers/credentials.d.ts.map +0 -1
- package/dist/component/providers/device.d.ts +0 -67
- package/dist/component/providers/device.d.ts.map +0 -1
- package/dist/component/providers/email.d.ts +0 -62
- package/dist/component/providers/email.d.ts.map +0 -1
- package/dist/component/providers/oauth.d.ts +0 -25
- package/dist/component/providers/oauth.d.ts.map +0 -1
- package/dist/component/providers/oauth.js +0 -13
- package/dist/component/providers/oauth.js.map +0 -1
- package/dist/component/providers/passkey.d.ts +0 -57
- package/dist/component/providers/passkey.d.ts.map +0 -1
- package/dist/component/providers/password.d.ts +0 -88
- package/dist/component/providers/password.d.ts.map +0 -1
- package/dist/component/providers/phone.d.ts +0 -48
- package/dist/component/providers/phone.d.ts.map +0 -1
- package/dist/component/providers/sso.d.ts +0 -50
- package/dist/component/providers/sso.d.ts.map +0 -1
- package/dist/component/providers/totp.d.ts +0 -45
- package/dist/component/providers/totp.d.ts.map +0 -1
- package/dist/component/public/enterprise/audit.d.ts +0 -73
- package/dist/component/public/enterprise/audit.d.ts.map +0 -1
- package/dist/component/public/enterprise/audit.js.map +0 -1
- package/dist/component/public/enterprise/core.d.ts +0 -176
- package/dist/component/public/enterprise/core.d.ts.map +0 -1
- package/dist/component/public/enterprise/core.js +0 -292
- package/dist/component/public/enterprise/core.js.map +0 -1
- package/dist/component/public/enterprise/domains.d.ts +0 -174
- package/dist/component/public/enterprise/domains.d.ts.map +0 -1
- package/dist/component/public/enterprise/domains.js +0 -271
- package/dist/component/public/enterprise/domains.js.map +0 -1
- package/dist/component/public/enterprise/scim.d.ts +0 -245
- package/dist/component/public/enterprise/scim.d.ts.map +0 -1
- package/dist/component/public/enterprise/scim.js.map +0 -1
- package/dist/component/public/enterprise/secrets.d.ts +0 -78
- package/dist/component/public/enterprise/secrets.d.ts.map +0 -1
- package/dist/component/public/enterprise/secrets.js +0 -118
- package/dist/component/public/enterprise/secrets.js.map +0 -1
- package/dist/component/public/enterprise/webhooks.d.ts +0 -211
- package/dist/component/public/enterprise/webhooks.d.ts.map +0 -1
- package/dist/component/public/enterprise/webhooks.js.map +0 -1
- package/dist/component/public/factors/devices.d.ts +0 -157
- package/dist/component/public/factors/devices.d.ts.map +0 -1
- package/dist/component/public/factors/devices.js.map +0 -1
- package/dist/component/public/factors/passkeys.d.ts +0 -175
- package/dist/component/public/factors/passkeys.d.ts.map +0 -1
- package/dist/component/public/factors/passkeys.js.map +0 -1
- package/dist/component/public/factors/totp.d.ts +0 -189
- package/dist/component/public/factors/totp.d.ts.map +0 -1
- package/dist/component/public/factors/totp.js.map +0 -1
- package/dist/component/public/groups/core.d.ts +0 -137
- package/dist/component/public/groups/core.d.ts.map +0 -1
- package/dist/component/public/groups/core.js.map +0 -1
- package/dist/component/public/groups/invites.d.ts +0 -217
- package/dist/component/public/groups/invites.d.ts.map +0 -1
- package/dist/component/public/groups/invites.js.map +0 -1
- package/dist/component/public/groups/members.d.ts +0 -204
- package/dist/component/public/groups/members.d.ts.map +0 -1
- package/dist/component/public/groups/members.js.map +0 -1
- package/dist/component/public/identity/accounts.d.ts +0 -147
- package/dist/component/public/identity/accounts.d.ts.map +0 -1
- package/dist/component/public/identity/accounts.js.map +0 -1
- package/dist/component/public/identity/codes.d.ts +0 -104
- package/dist/component/public/identity/codes.d.ts.map +0 -1
- package/dist/component/public/identity/codes.js.map +0 -1
- package/dist/component/public/identity/sessions.d.ts +0 -128
- package/dist/component/public/identity/sessions.d.ts.map +0 -1
- package/dist/component/public/identity/sessions.js.map +0 -1
- package/dist/component/public/identity/tokens.d.ts +0 -169
- package/dist/component/public/identity/tokens.d.ts.map +0 -1
- package/dist/component/public/identity/tokens.js.map +0 -1
- package/dist/component/public/identity/users.d.ts +0 -212
- package/dist/component/public/identity/users.d.ts.map +0 -1
- package/dist/component/public/identity/users.js.map +0 -1
- package/dist/component/public/identity/verifiers.d.ts +0 -116
- package/dist/component/public/identity/verifiers.d.ts.map +0 -1
- package/dist/component/public/identity/verifiers.js.map +0 -1
- package/dist/component/public/security/keys.d.ts +0 -209
- package/dist/component/public/security/keys.d.ts.map +0 -1
- package/dist/component/public/security/keys.js.map +0 -1
- package/dist/component/public/security/limits.d.ts +0 -114
- package/dist/component/public/security/limits.d.ts.map +0 -1
- package/dist/component/public/security/limits.js.map +0 -1
- package/dist/component/public.d.ts +0 -28
- package/dist/component/public.d.ts.map +0 -1
- package/dist/component/schema.d.ts.map +0 -1
- package/dist/component/schema.js.map +0 -1
- package/dist/component/server/auth.d.ts +0 -447
- package/dist/component/server/auth.d.ts.map +0 -1
- package/dist/component/server/auth.js +0 -254
- package/dist/component/server/auth.js.map +0 -1
- package/dist/component/server/config.js +0 -121
- package/dist/component/server/config.js.map +0 -1
- package/dist/component/server/context.js +0 -53
- package/dist/component/server/context.js.map +0 -1
- package/dist/component/server/cookies.js +0 -47
- package/dist/component/server/cookies.js.map +0 -1
- package/dist/component/server/core.js +0 -576
- package/dist/component/server/core.js.map +0 -1
- package/dist/component/server/crypto.js +0 -56
- package/dist/component/server/crypto.js.map +0 -1
- package/dist/component/server/db.js +0 -87
- package/dist/component/server/db.js.map +0 -1
- package/dist/component/server/device.js +0 -152
- package/dist/component/server/device.js.map +0 -1
- package/dist/component/server/enterprise/config.js +0 -46
- package/dist/component/server/enterprise/config.js.map +0 -1
- package/dist/component/server/enterprise/domain.js +0 -974
- package/dist/component/server/enterprise/domain.js.map +0 -1
- package/dist/component/server/enterprise/http.js +0 -787
- package/dist/component/server/enterprise/http.js.map +0 -1
- package/dist/component/server/enterprise/oidc.js +0 -248
- package/dist/component/server/enterprise/oidc.js.map +0 -1
- package/dist/component/server/enterprise/policy.js +0 -85
- package/dist/component/server/enterprise/policy.js.map +0 -1
- package/dist/component/server/enterprise/saml.js.map +0 -1
- package/dist/component/server/enterprise/scim.js.map +0 -1
- package/dist/component/server/enterprise/shared.js +0 -51
- package/dist/component/server/enterprise/shared.js.map +0 -1
- package/dist/component/server/http.d.ts +0 -85
- package/dist/component/server/http.d.ts.map +0 -1
- package/dist/component/server/http.js +0 -351
- package/dist/component/server/http.js.map +0 -1
- package/dist/component/server/identity.js +0 -16
- package/dist/component/server/identity.js.map +0 -1
- package/dist/component/server/keys.js +0 -96
- package/dist/component/server/keys.js.map +0 -1
- package/dist/component/server/limits.js +0 -52
- package/dist/component/server/limits.js.map +0 -1
- package/dist/component/server/mutations/account.js +0 -46
- package/dist/component/server/mutations/account.js.map +0 -1
- package/dist/component/server/mutations/code.js +0 -68
- package/dist/component/server/mutations/code.js.map +0 -1
- package/dist/component/server/mutations/invalidate.js +0 -32
- package/dist/component/server/mutations/invalidate.js.map +0 -1
- package/dist/component/server/mutations/oauth.js +0 -116
- package/dist/component/server/mutations/oauth.js.map +0 -1
- package/dist/component/server/mutations/refresh.js +0 -119
- package/dist/component/server/mutations/refresh.js.map +0 -1
- package/dist/component/server/mutations/register.js +0 -87
- package/dist/component/server/mutations/register.js.map +0 -1
- package/dist/component/server/mutations/retrieve.js +0 -61
- package/dist/component/server/mutations/retrieve.js.map +0 -1
- package/dist/component/server/mutations/signature.js +0 -38
- package/dist/component/server/mutations/signature.js.map +0 -1
- package/dist/component/server/mutations/signin.js +0 -27
- package/dist/component/server/mutations/signin.js.map +0 -1
- package/dist/component/server/mutations/signout.js +0 -27
- package/dist/component/server/mutations/signout.js.map +0 -1
- package/dist/component/server/mutations/store/refs.js +0 -15
- package/dist/component/server/mutations/store/refs.js.map +0 -1
- package/dist/component/server/mutations/store.js +0 -70
- package/dist/component/server/mutations/store.js.map +0 -1
- package/dist/component/server/mutations/verifier.js +0 -18
- package/dist/component/server/mutations/verifier.js.map +0 -1
- package/dist/component/server/mutations/verify.js +0 -98
- package/dist/component/server/mutations/verify.js.map +0 -1
- package/dist/component/server/oauth.js +0 -242
- package/dist/component/server/oauth.js.map +0 -1
- package/dist/component/server/passkey.js +0 -415
- package/dist/component/server/passkey.js.map +0 -1
- package/dist/component/server/redirects.js +0 -40
- package/dist/component/server/redirects.js.map +0 -1
- package/dist/component/server/refresh.js +0 -99
- package/dist/component/server/refresh.js.map +0 -1
- package/dist/component/server/runtime.d.ts +0 -136
- package/dist/component/server/runtime.d.ts.map +0 -1
- package/dist/component/server/runtime.js +0 -456
- package/dist/component/server/runtime.js.map +0 -1
- package/dist/component/server/sessions.js +0 -71
- package/dist/component/server/sessions.js.map +0 -1
- package/dist/component/server/signin.js +0 -225
- package/dist/component/server/signin.js.map +0 -1
- package/dist/component/server/tokens.js +0 -17
- package/dist/component/server/tokens.js.map +0 -1
- package/dist/component/server/totp.js +0 -208
- package/dist/component/server/totp.js.map +0 -1
- package/dist/component/server/types.d.ts +0 -949
- package/dist/component/server/types.d.ts.map +0 -1
- package/dist/component/server/types.js +0 -79
- package/dist/component/server/types.js.map +0 -1
- package/dist/component/server/users.js +0 -123
- package/dist/component/server/users.js.map +0 -1
- package/dist/component/server/utils.js +0 -140
- package/dist/component/server/utils.js.map +0 -1
- package/dist/core/types.d.ts +0 -361
- package/dist/core/types.d.ts.map +0 -1
- package/dist/factors/device.js +0 -104
- package/dist/factors/device.js.map +0 -1
- package/dist/factors/passkey.js.map +0 -1
- package/dist/factors/totp.js.map +0 -1
- package/dist/providers/anonymous.d.ts.map +0 -1
- package/dist/providers/anonymous.js.map +0 -1
- package/dist/providers/credentials.d.ts.map +0 -1
- package/dist/providers/credentials.js.map +0 -1
- package/dist/providers/device.d.ts.map +0 -1
- package/dist/providers/device.js.map +0 -1
- package/dist/providers/email.d.ts.map +0 -1
- package/dist/providers/email.js.map +0 -1
- package/dist/providers/oauth.d.ts +0 -69
- package/dist/providers/oauth.d.ts.map +0 -1
- package/dist/providers/oauth.js +0 -43
- package/dist/providers/oauth.js.map +0 -1
- package/dist/providers/passkey.d.ts.map +0 -1
- package/dist/providers/passkey.js.map +0 -1
- package/dist/providers/password.d.ts.map +0 -1
- package/dist/providers/password.js.map +0 -1
- package/dist/providers/phone.d.ts.map +0 -1
- package/dist/providers/phone.js.map +0 -1
- package/dist/providers/sso.d.ts.map +0 -1
- package/dist/providers/sso.js.map +0 -1
- package/dist/providers/totp.d.ts.map +0 -1
- package/dist/providers/totp.js.map +0 -1
- package/dist/runtime/browser.js +0 -68
- package/dist/runtime/browser.js.map +0 -1
- package/dist/runtime/invite.js.map +0 -1
- package/dist/runtime/proxy.js +0 -70
- package/dist/runtime/proxy.js.map +0 -1
- package/dist/runtime/storage.js +0 -37
- package/dist/runtime/storage.js.map +0 -1
- package/dist/server/auth.d.ts.map +0 -1
- package/dist/server/auth.js.map +0 -1
- package/dist/server/config.d.ts +0 -1
- package/dist/server/config.js.map +0 -1
- package/dist/server/context.d.ts +0 -1
- package/dist/server/context.js.map +0 -1
- package/dist/server/cookies.d.ts +0 -1
- package/dist/server/cookies.js.map +0 -1
- package/dist/server/core.d.ts +0 -1315
- package/dist/server/core.d.ts.map +0 -1
- package/dist/server/core.js.map +0 -1
- package/dist/server/crypto.d.ts +0 -8
- package/dist/server/crypto.d.ts.map +0 -1
- package/dist/server/crypto.js.map +0 -1
- package/dist/server/db.d.ts +0 -1
- package/dist/server/db.js.map +0 -1
- package/dist/server/device.d.ts +0 -1
- package/dist/server/device.js.map +0 -1
- package/dist/server/enterprise/config.d.ts +0 -1
- package/dist/server/enterprise/config.js.map +0 -1
- package/dist/server/enterprise/domain.d.ts +0 -401
- package/dist/server/enterprise/domain.d.ts.map +0 -1
- package/dist/server/enterprise/domain.js +0 -974
- package/dist/server/enterprise/domain.js.map +0 -1
- package/dist/server/enterprise/http.d.ts +0 -26
- package/dist/server/enterprise/http.d.ts.map +0 -1
- package/dist/server/enterprise/http.js +0 -787
- package/dist/server/enterprise/http.js.map +0 -1
- package/dist/server/enterprise/oidc.d.ts +0 -1
- package/dist/server/enterprise/oidc.js +0 -248
- package/dist/server/enterprise/oidc.js.map +0 -1
- package/dist/server/enterprise/policy.d.ts +0 -1
- package/dist/server/enterprise/policy.js +0 -85
- package/dist/server/enterprise/policy.js.map +0 -1
- package/dist/server/enterprise/saml.d.ts +0 -1
- package/dist/server/enterprise/saml.js +0 -338
- package/dist/server/enterprise/saml.js.map +0 -1
- package/dist/server/enterprise/scim.d.ts +0 -1
- package/dist/server/enterprise/scim.js +0 -97
- package/dist/server/enterprise/scim.js.map +0 -1
- package/dist/server/enterprise/shared.d.ts +0 -5
- package/dist/server/enterprise/shared.d.ts.map +0 -1
- package/dist/server/enterprise/shared.js +0 -51
- package/dist/server/enterprise/shared.js.map +0 -1
- package/dist/server/enterprise/validators.d.ts +0 -1
- package/dist/server/enterprise/validators.js +0 -60
- package/dist/server/enterprise/validators.js.map +0 -1
- package/dist/server/http.d.ts.map +0 -1
- package/dist/server/http.js.map +0 -1
- package/dist/server/identity.d.ts +0 -1
- package/dist/server/identity.js.map +0 -1
- package/dist/server/keys.d.ts +0 -1
- package/dist/server/keys.js.map +0 -1
- package/dist/server/limits.d.ts +0 -1
- package/dist/server/limits.js.map +0 -1
- package/dist/server/mounts.d.ts.map +0 -1
- package/dist/server/mounts.js.map +0 -1
- package/dist/server/mutations/account.d.ts +0 -29
- package/dist/server/mutations/account.d.ts.map +0 -1
- package/dist/server/mutations/account.js.map +0 -1
- package/dist/server/mutations/code.d.ts +0 -30
- package/dist/server/mutations/code.d.ts.map +0 -1
- package/dist/server/mutations/code.js.map +0 -1
- package/dist/server/mutations/index.d.ts +0 -14
- package/dist/server/mutations/invalidate.d.ts +0 -20
- package/dist/server/mutations/invalidate.d.ts.map +0 -1
- package/dist/server/mutations/invalidate.js.map +0 -1
- package/dist/server/mutations/oauth.d.ts +0 -30
- package/dist/server/mutations/oauth.d.ts.map +0 -1
- package/dist/server/mutations/oauth.js.map +0 -1
- package/dist/server/mutations/refresh.d.ts +0 -21
- package/dist/server/mutations/refresh.d.ts.map +0 -1
- package/dist/server/mutations/refresh.js.map +0 -1
- package/dist/server/mutations/register.d.ts +0 -38
- package/dist/server/mutations/register.d.ts.map +0 -1
- package/dist/server/mutations/register.js.map +0 -1
- package/dist/server/mutations/retrieve.d.ts +0 -33
- package/dist/server/mutations/retrieve.d.ts.map +0 -1
- package/dist/server/mutations/retrieve.js.map +0 -1
- package/dist/server/mutations/signature.d.ts +0 -21
- package/dist/server/mutations/signature.d.ts.map +0 -1
- package/dist/server/mutations/signature.js.map +0 -1
- package/dist/server/mutations/signin.d.ts +0 -22
- package/dist/server/mutations/signin.d.ts.map +0 -1
- package/dist/server/mutations/signin.js.map +0 -1
- package/dist/server/mutations/signout.d.ts +0 -16
- package/dist/server/mutations/signout.d.ts.map +0 -1
- package/dist/server/mutations/signout.js.map +0 -1
- package/dist/server/mutations/store/refs.d.ts +0 -12
- package/dist/server/mutations/store/refs.d.ts.map +0 -1
- package/dist/server/mutations/store/refs.js.map +0 -1
- package/dist/server/mutations/store.d.ts +0 -306
- package/dist/server/mutations/store.d.ts.map +0 -1
- package/dist/server/mutations/store.js.map +0 -1
- package/dist/server/mutations/verifier.d.ts +0 -13
- package/dist/server/mutations/verifier.d.ts.map +0 -1
- package/dist/server/mutations/verifier.js.map +0 -1
- package/dist/server/mutations/verify.d.ts +0 -26
- package/dist/server/mutations/verify.d.ts.map +0 -1
- package/dist/server/mutations/verify.js.map +0 -1
- package/dist/server/oauth.d.ts +0 -1
- package/dist/server/oauth.js +0 -242
- package/dist/server/oauth.js.map +0 -1
- package/dist/server/passkey.d.ts +0 -27
- package/dist/server/passkey.d.ts.map +0 -1
- package/dist/server/passkey.js.map +0 -1
- package/dist/server/redirects.d.ts +0 -1
- package/dist/server/redirects.js.map +0 -1
- package/dist/server/refresh.d.ts +0 -1
- package/dist/server/refresh.js.map +0 -1
- package/dist/server/runtime.d.ts.map +0 -1
- package/dist/server/runtime.js.map +0 -1
- package/dist/server/sessions.d.ts +0 -1
- package/dist/server/sessions.js.map +0 -1
- package/dist/server/signin.d.ts +0 -1
- package/dist/server/signin.js.map +0 -1
- package/dist/server/ssr.d.ts.map +0 -1
- package/dist/server/ssr.js +0 -777
- package/dist/server/ssr.js.map +0 -1
- package/dist/server/templates.d.ts +0 -1
- package/dist/server/templates.js.map +0 -1
- package/dist/server/tokens.d.ts +0 -1
- package/dist/server/tokens.js.map +0 -1
- package/dist/server/totp.d.ts +0 -1
- package/dist/server/totp.js.map +0 -1
- package/dist/server/types.d.ts.map +0 -1
- package/dist/server/types.js.map +0 -1
- package/dist/server/users.d.ts +0 -1
- package/dist/server/users.js.map +0 -1
- package/dist/server/utils.d.ts +0 -1
- package/dist/server/utils.js +0 -140
- package/dist/server/utils.js.map +0 -1
- package/src/authorization/index.ts +0 -83
- package/src/cli/bin.ts +0 -5
- package/src/cli/command.ts +0 -70
- package/src/cli/index.ts +0 -1112
- package/src/cli/keys.ts +0 -23
- package/src/client/core/types.ts +0 -437
- package/src/client/factors/device.ts +0 -158
- package/src/client/factors/passkey.ts +0 -279
- package/src/client/factors/totp.ts +0 -150
- package/src/client/index.ts +0 -1124
- package/src/client/runtime/browser.ts +0 -112
- package/src/client/runtime/invite.ts +0 -63
- package/src/client/runtime/proxy.ts +0 -111
- package/src/client/runtime/storage.ts +0 -79
- package/src/component/_generated/api.ts +0 -96
- package/src/component/_generated/component.ts +0 -3774
- package/src/component/_generated/dataModel.ts +0 -60
- package/src/component/_generated/server.ts +0 -156
- package/src/component/convex.config.ts +0 -5
- package/src/component/functions.ts +0 -104
- package/src/component/index.ts +0 -42
- package/src/component/model.ts +0 -449
- package/src/component/public/enterprise/audit.ts +0 -125
- package/src/component/public/enterprise/core.ts +0 -355
- package/src/component/public/enterprise/domains.ts +0 -327
- package/src/component/public/enterprise/scim.ts +0 -397
- package/src/component/public/enterprise/secrets.ts +0 -133
- package/src/component/public/enterprise/webhooks.ts +0 -307
- package/src/component/public/factors/devices.ts +0 -224
- package/src/component/public/factors/passkeys.ts +0 -243
- package/src/component/public/factors/totp.ts +0 -259
- package/src/component/public/groups/core.ts +0 -481
- package/src/component/public/groups/invites.ts +0 -608
- package/src/component/public/groups/members.ts +0 -410
- package/src/component/public/identity/accounts.ts +0 -207
- package/src/component/public/identity/codes.ts +0 -149
- package/src/component/public/identity/sessions.ts +0 -210
- package/src/component/public/identity/tokens.ts +0 -251
- package/src/component/public/identity/users.ts +0 -355
- package/src/component/public/identity/verifiers.ts +0 -158
- package/src/component/public/security/keys.ts +0 -366
- package/src/component/public/security/limits.ts +0 -174
- package/src/component/public.ts +0 -27
- package/src/component/schema.ts +0 -505
- package/src/providers/anonymous.ts +0 -99
- package/src/providers/credentials.ts +0 -102
- package/src/providers/device.ts +0 -87
- package/src/providers/email.ts +0 -99
- package/src/providers/index.ts +0 -31
- package/src/providers/oauth.ts +0 -117
- package/src/providers/passkey.ts +0 -77
- package/src/providers/password.ts +0 -441
- package/src/providers/phone.ts +0 -93
- package/src/providers/sso.ts +0 -54
- package/src/providers/totp.ts +0 -62
- package/src/samlify.d.ts +0 -53
- package/src/server/auth.ts +0 -949
- package/src/server/config.ts +0 -200
- package/src/server/context.ts +0 -90
- package/src/server/cookies.ts +0 -49
- package/src/server/core.ts +0 -2004
- package/src/server/crypto.ts +0 -90
- package/src/server/db.ts +0 -203
- package/src/server/device.ts +0 -254
- package/src/server/enterprise/config.ts +0 -51
- package/src/server/enterprise/domain.ts +0 -1739
- package/src/server/enterprise/http.ts +0 -1331
- package/src/server/enterprise/oidc.ts +0 -500
- package/src/server/enterprise/policy.ts +0 -128
- package/src/server/enterprise/saml.ts +0 -578
- package/src/server/enterprise/scim.ts +0 -135
- package/src/server/enterprise/shared.ts +0 -134
- package/src/server/enterprise/validators.ts +0 -93
- package/src/server/http.ts +0 -790
- package/src/server/identity.ts +0 -18
- package/src/server/index.ts +0 -40
- package/src/server/keys.ts +0 -158
- package/src/server/limits.ts +0 -107
- package/src/server/mounts.ts +0 -924
- package/src/server/mutations/account.ts +0 -62
- package/src/server/mutations/code.ts +0 -119
- package/src/server/mutations/index.ts +0 -13
- package/src/server/mutations/invalidate.ts +0 -50
- package/src/server/mutations/oauth.ts +0 -243
- package/src/server/mutations/refresh.ts +0 -299
- package/src/server/mutations/register.ts +0 -155
- package/src/server/mutations/retrieve.ts +0 -109
- package/src/server/mutations/signature.ts +0 -57
- package/src/server/mutations/signin.ts +0 -54
- package/src/server/mutations/signout.ts +0 -43
- package/src/server/mutations/store/refs.ts +0 -10
- package/src/server/mutations/store.ts +0 -123
- package/src/server/mutations/verifier.ts +0 -34
- package/src/server/mutations/verify.ts +0 -200
- package/src/server/oauth.ts +0 -418
- package/src/server/passkey.ts +0 -838
- package/src/server/redirects.ts +0 -59
- package/src/server/refresh.ts +0 -218
- package/src/server/runtime.ts +0 -918
- package/src/server/sessions.ts +0 -132
- package/src/server/signin.ts +0 -445
- package/src/server/ssr.ts +0 -1747
- package/src/server/templates.ts +0 -82
- package/src/server/tokens.ts +0 -35
- package/src/server/totp.ts +0 -399
- package/src/server/types.ts +0 -1942
- package/src/server/users.ts +0 -291
- package/src/server/utils.ts +0 -220
- /package/dist/{runtime → client/runtime}/invite.js +0 -0
|
@@ -1,415 +0,0 @@
|
|
|
1
|
-
import { userIdFromIdentitySubject } from "./identity.js";
|
|
2
|
-
import { siteUrlsFromEnv } from "./utils.js";
|
|
3
|
-
import { authDb } from "./db.js";
|
|
4
|
-
import { callVerifierSignature } from "./mutations/signature.js";
|
|
5
|
-
import { callSignIn } from "./mutations/signin.js";
|
|
6
|
-
import { callVerifier } from "./mutations/verifier.js";
|
|
7
|
-
import { mutatePasskeyInsert, mutatePasskeyUpdateCounter, mutateVerifierDelete, queryPasskeyByCredentialId, queryPasskeysByUserId, queryUserById, queryUserByVerifiedEmail, queryVerifierById } from "./types.js";
|
|
8
|
-
import { Cv } from "@robelest/fx/convex";
|
|
9
|
-
import { Fx } from "@robelest/fx";
|
|
10
|
-
import { sha256 } from "@oslojs/crypto/sha2";
|
|
11
|
-
import { decodeBase64urlIgnorePadding, encodeBase64urlNoPadding } from "@oslojs/encoding";
|
|
12
|
-
import { decodePKIXECDSASignature, decodeSEC1PublicKey, p256, verifyECDSASignature } from "@oslojs/crypto/ecdsa";
|
|
13
|
-
import { RSAPublicKey, decodePKCS1RSAPublicKey, sha256ObjectIdentifier, verifyRSASSAPKCS1v15Signature } from "@oslojs/crypto/rsa";
|
|
14
|
-
import { COSEKeyType, ClientDataType, coseAlgorithmES256, coseAlgorithmRS256, createAssertionSignatureMessage, parseAttestationObject, parseAuthenticatorData, parseClientDataJSON } from "@oslojs/webauthn";
|
|
15
|
-
|
|
16
|
-
//#region src/server/passkey.ts
|
|
17
|
-
/**
|
|
18
|
-
* Server-side WebAuthn ceremony logic for passkey authentication.
|
|
19
|
-
*
|
|
20
|
-
* Handles the four phases of the WebAuthn flow:
|
|
21
|
-
* 1. registerOptions — generate PublicKeyCredentialCreationOptions
|
|
22
|
-
* 2. registerVerify — verify attestation and store credential
|
|
23
|
-
* 3. authOptions — generate PublicKeyCredentialRequestOptions
|
|
24
|
-
* 4. authVerify — verify assertion signature and sign in
|
|
25
|
-
*
|
|
26
|
-
* Uses `@oslojs/webauthn` for attestation/assertion parsing and
|
|
27
|
-
* `@oslojs/crypto` for signature verification.
|
|
28
|
-
*
|
|
29
|
-
* All functions return `Fx<A, ConvexError<any>>` composed via `Fx.chain` pipelines.
|
|
30
|
-
*
|
|
31
|
-
* @module
|
|
32
|
-
*/
|
|
33
|
-
/**
|
|
34
|
-
* Resolve passkey relying party options from provider config and environment.
|
|
35
|
-
*
|
|
36
|
-
* Returns `Fx<RpOptions, ConvexError<any>>` — fails if neither SITE_URL nor rpId
|
|
37
|
-
* is configured.
|
|
38
|
-
*/
|
|
39
|
-
const resolveRpOptionsFx = (provider) => {
|
|
40
|
-
const configuredSiteUrls = process.env.SITE_URL === void 0 ? null : siteUrlsFromEnv();
|
|
41
|
-
const siteUrl = configuredSiteUrls?.primaryUrl;
|
|
42
|
-
const hasSiteUrl = siteUrl !== void 0 && siteUrl !== "";
|
|
43
|
-
const hasRpId = provider.options.rpId !== void 0;
|
|
44
|
-
return Fx.succeed({
|
|
45
|
-
siteUrl,
|
|
46
|
-
hasSiteUrl,
|
|
47
|
-
hasRpId
|
|
48
|
-
}).pipe(Fx.chain(({ siteUrl: siteUrl$1, hasSiteUrl: hasSiteUrl$1, hasRpId: hasRpId$1 }) => !hasSiteUrl$1 && !hasRpId$1 ? Cv.fail({
|
|
49
|
-
code: "PASSKEY_MISSING_CONFIG",
|
|
50
|
-
message: "Passkey provider requires SITE_URL env var (your frontend URL) or explicit rpId / origin in the provider config. CONVEX_SITE_URL cannot be used because WebAuthn RP ID must match the frontend domain."
|
|
51
|
-
}) : Fx.succeed(siteUrl$1)), Fx.map((siteUrl$1) => {
|
|
52
|
-
const siteHostname = siteUrl$1 ? new URL(siteUrl$1).hostname : void 0;
|
|
53
|
-
const defaultOrigin = configuredSiteUrls?.allowedUrls ?? siteUrl$1;
|
|
54
|
-
return {
|
|
55
|
-
rpName: provider.options.rpName ?? siteHostname ?? "localhost",
|
|
56
|
-
rpId: provider.options.rpId ?? siteHostname ?? "localhost",
|
|
57
|
-
origin: provider.options.origin ?? defaultOrigin ?? "http://localhost",
|
|
58
|
-
attestation: provider.options.attestation ?? "none",
|
|
59
|
-
userVerification: provider.options.userVerification ?? "required",
|
|
60
|
-
residentKey: provider.options.residentKey ?? "preferred",
|
|
61
|
-
authenticatorAttachment: provider.options.authenticatorAttachment,
|
|
62
|
-
algorithms: provider.options.algorithms ?? [coseAlgorithmES256, coseAlgorithmRS256],
|
|
63
|
-
challengeExpirationMs: provider.options.challengeExpirationMs ?? 3e5
|
|
64
|
-
};
|
|
65
|
-
}));
|
|
66
|
-
};
|
|
67
|
-
/** Verify client data type matches expected WebAuthn ceremony type. */
|
|
68
|
-
const verifyClientDataType = (expectedType, label) => (clientData) => clientData.type === expectedType ? Fx.succeed(clientData) : Cv.fail({
|
|
69
|
-
code: "PASSKEY_INVALID_CLIENT_DATA",
|
|
70
|
-
message: `Invalid client data type: expected ${label}`
|
|
71
|
-
});
|
|
72
|
-
/** Verify origin is in the allowed list. */
|
|
73
|
-
const verifyOrigin = (rp) => (clientData) => {
|
|
74
|
-
const allowed = Array.isArray(rp.origin) ? rp.origin : [rp.origin];
|
|
75
|
-
return allowed.includes(clientData.origin) ? Fx.succeed(clientData) : Cv.fail({
|
|
76
|
-
code: "PASSKEY_INVALID_ORIGIN",
|
|
77
|
-
message: `Invalid origin: ${clientData.origin}, expected one of: ${allowed.join(", ")}`
|
|
78
|
-
});
|
|
79
|
-
};
|
|
80
|
-
/** Verify the challenge hash matches the stored verifier, then delete verifier. */
|
|
81
|
-
const verifyAndConsumeChallenge = (ctx, verifierValue) => (clientData) => {
|
|
82
|
-
const challengeHash = encodeBase64urlNoPadding(new Uint8Array(sha256(clientData.challenge)));
|
|
83
|
-
return Fx.from({
|
|
84
|
-
ok: () => queryVerifierById(ctx, verifierValue),
|
|
85
|
-
err: () => Cv.error({
|
|
86
|
-
code: "PASSKEY_INVALID_CHALLENGE",
|
|
87
|
-
message: "Invalid or expired passkey challenge."
|
|
88
|
-
})
|
|
89
|
-
}).pipe(Fx.chain((doc) => !doc || doc.signature !== challengeHash ? Cv.fail({
|
|
90
|
-
code: "PASSKEY_INVALID_CHALLENGE",
|
|
91
|
-
message: "Invalid or expired passkey challenge."
|
|
92
|
-
}) : Fx.succeed(doc)), Fx.chain(() => Fx.from({
|
|
93
|
-
ok: () => mutateVerifierDelete(ctx, verifierValue),
|
|
94
|
-
err: () => Cv.error({
|
|
95
|
-
code: "PASSKEY_INVALID_CHALLENGE",
|
|
96
|
-
message: "Invalid or expired passkey challenge."
|
|
97
|
-
})
|
|
98
|
-
})), Fx.map(() => clientData));
|
|
99
|
-
};
|
|
100
|
-
/** Verify RP ID hash matches. */
|
|
101
|
-
const verifyRpId = (rpId) => (authData) => authData.verifyRelyingPartyIdHash(rpId) ? Fx.succeed(authData) : Cv.fail({
|
|
102
|
-
code: "PASSKEY_RP_MISMATCH",
|
|
103
|
-
message: "Relying party ID mismatch."
|
|
104
|
-
});
|
|
105
|
-
/** Verify user presence and (optionally) user verification flags. */
|
|
106
|
-
const verifyUserFlags = (rp) => (authData) => !authData.userPresent ? Cv.fail({
|
|
107
|
-
code: "PASSKEY_USER_PRESENCE",
|
|
108
|
-
message: "User presence flag not set."
|
|
109
|
-
}) : rp.userVerification === "required" && !authData.userVerified ? Cv.fail({
|
|
110
|
-
code: "PASSKEY_USER_VERIFICATION",
|
|
111
|
-
message: "User verification required but not performed."
|
|
112
|
-
}) : Fx.succeed(authData);
|
|
113
|
-
const PASSKEY_FLOW = {
|
|
114
|
-
registerOptions: "registerOptions",
|
|
115
|
-
registerVerify: "registerVerify",
|
|
116
|
-
authOptions: "authOptions",
|
|
117
|
-
authVerify: "authVerify"
|
|
118
|
-
};
|
|
119
|
-
const PASSKEY_FLOWS = [
|
|
120
|
-
PASSKEY_FLOW.registerOptions,
|
|
121
|
-
PASSKEY_FLOW.registerVerify,
|
|
122
|
-
PASSKEY_FLOW.authOptions,
|
|
123
|
-
PASSKEY_FLOW.authVerify
|
|
124
|
-
];
|
|
125
|
-
const resolvePasskeyDispatchFx = (params) => {
|
|
126
|
-
const flow = params.flow;
|
|
127
|
-
return typeof flow === "string" && PASSKEY_FLOWS.includes(flow) ? Fx.succeed({ flow }) : Cv.fail({
|
|
128
|
-
code: "PASSKEY_MISSING_FLOW",
|
|
129
|
-
message: "Missing `flow` parameter. Expected one of: registerOptions, registerVerify, authOptions, authVerify"
|
|
130
|
-
});
|
|
131
|
-
};
|
|
132
|
-
const requirePasskeyVerifierFx = (verifier) => verifier != null ? Fx.succeed(verifier) : Cv.fail({
|
|
133
|
-
code: "PASSKEY_MISSING_VERIFIER",
|
|
134
|
-
message: "Missing verifier for passkey operation."
|
|
135
|
-
});
|
|
136
|
-
/**
|
|
137
|
-
* Main passkey handler dispatched from signIn.ts.
|
|
138
|
-
*
|
|
139
|
-
* Routes to the appropriate phase based on `params.flow` via `dispatchFx`.
|
|
140
|
-
*/
|
|
141
|
-
function handlePasskeyFx(ctx, provider, args) {
|
|
142
|
-
const params = args.params ?? {};
|
|
143
|
-
return resolvePasskeyDispatchFx(params).pipe(Fx.chain((dispatch) => {
|
|
144
|
-
return Fx.match(dispatch).on("flow", {
|
|
145
|
-
registerOptions: (_) => Fx.zip(Fx.from({
|
|
146
|
-
ok: () => ctx.auth.getUserIdentity(),
|
|
147
|
-
err: () => Cv.error({
|
|
148
|
-
code: "PASSKEY_AUTH_REQUIRED",
|
|
149
|
-
message: "Sign in first, then add a passkey to your account."
|
|
150
|
-
})
|
|
151
|
-
}).pipe(Fx.chain((id) => id === null ? Cv.fail({
|
|
152
|
-
code: "PASSKEY_AUTH_REQUIRED",
|
|
153
|
-
message: "Sign in first, then add a passkey to your account."
|
|
154
|
-
}) : Fx.succeed(userIdFromIdentitySubject(id.subject)))), resolveRpOptionsFx(provider)).pipe(Fx.chain(([userId, rp]) => {
|
|
155
|
-
const challenge = new Uint8Array(32);
|
|
156
|
-
crypto.getRandomValues(challenge);
|
|
157
|
-
const challengeHash = encodeBase64urlNoPadding(new Uint8Array(sha256(challenge)));
|
|
158
|
-
return Fx.from({
|
|
159
|
-
ok: async () => {
|
|
160
|
-
const verifier = await callVerifier(ctx);
|
|
161
|
-
await callVerifierSignature(ctx, {
|
|
162
|
-
verifier,
|
|
163
|
-
signature: challengeHash
|
|
164
|
-
});
|
|
165
|
-
const user = await queryUserById(ctx, userId);
|
|
166
|
-
const userName = params.userName ?? user?.email ?? "user";
|
|
167
|
-
const userDisplayName = params.userDisplayName ?? user?.name ?? userName;
|
|
168
|
-
const excludeCredentials = (await queryPasskeysByUserId(ctx, userId)).map((pk) => ({
|
|
169
|
-
id: pk.credentialId,
|
|
170
|
-
transports: pk.transports
|
|
171
|
-
}));
|
|
172
|
-
const userHandle = encodeBase64urlNoPadding(new TextEncoder().encode(userId));
|
|
173
|
-
return {
|
|
174
|
-
kind: "passkeyOptions",
|
|
175
|
-
options: {
|
|
176
|
-
rp: {
|
|
177
|
-
name: rp.rpName,
|
|
178
|
-
id: rp.rpId
|
|
179
|
-
},
|
|
180
|
-
user: {
|
|
181
|
-
id: userHandle,
|
|
182
|
-
name: userName,
|
|
183
|
-
displayName: userDisplayName
|
|
184
|
-
},
|
|
185
|
-
challenge: encodeBase64urlNoPadding(challenge),
|
|
186
|
-
pubKeyCredParams: rp.algorithms.map((alg) => ({
|
|
187
|
-
type: "public-key",
|
|
188
|
-
alg
|
|
189
|
-
})),
|
|
190
|
-
timeout: rp.challengeExpirationMs,
|
|
191
|
-
attestation: rp.attestation,
|
|
192
|
-
authenticatorSelection: {
|
|
193
|
-
residentKey: rp.residentKey,
|
|
194
|
-
requireResidentKey: rp.residentKey === "required",
|
|
195
|
-
userVerification: rp.userVerification,
|
|
196
|
-
...rp.authenticatorAttachment ? { authenticatorAttachment: rp.authenticatorAttachment } : {}
|
|
197
|
-
},
|
|
198
|
-
excludeCredentials
|
|
199
|
-
},
|
|
200
|
-
verifier
|
|
201
|
-
};
|
|
202
|
-
},
|
|
203
|
-
err: () => Cv.error({
|
|
204
|
-
code: "INTERNAL_ERROR",
|
|
205
|
-
message: "An unexpected error occurred."
|
|
206
|
-
})
|
|
207
|
-
});
|
|
208
|
-
})),
|
|
209
|
-
registerVerify: (_) => Fx.zip(Fx.from({
|
|
210
|
-
ok: () => ctx.auth.getUserIdentity(),
|
|
211
|
-
err: () => Cv.error({
|
|
212
|
-
code: "PASSKEY_AUTH_REQUIRED",
|
|
213
|
-
message: "Sign in first, then add a passkey to your account."
|
|
214
|
-
})
|
|
215
|
-
}).pipe(Fx.chain((id) => id === null ? Cv.fail({
|
|
216
|
-
code: "PASSKEY_AUTH_REQUIRED",
|
|
217
|
-
message: "Sign in first, then add a passkey to your account."
|
|
218
|
-
}) : Fx.succeed(userIdFromIdentitySubject(id.subject)))), resolveRpOptionsFx(provider)).pipe(Fx.chain(([userId, rp]) => requirePasskeyVerifierFx(args.verifier).pipe(Fx.chain((verifier) => {
|
|
219
|
-
const clientData = parseClientDataJSON(decodeBase64urlIgnorePadding(params.clientDataJSON));
|
|
220
|
-
return Fx.succeed(clientData).pipe(Fx.chain(verifyClientDataType(ClientDataType.Create, "webauthn.create")), Fx.chain(verifyOrigin(rp)), Fx.chain(verifyAndConsumeChallenge(ctx, verifier)), Fx.map(() => {
|
|
221
|
-
return parseAttestationObject(decodeBase64urlIgnorePadding(params.attestationObject)).authenticatorData;
|
|
222
|
-
})).pipe(Fx.chain(verifyRpId(rp.rpId)), Fx.chain(verifyUserFlags(rp)), Fx.chain((authData) => {
|
|
223
|
-
if (authData.credential == null) return Cv.fail({
|
|
224
|
-
code: "PASSKEY_NO_CREDENTIAL",
|
|
225
|
-
message: "No credential in attestation."
|
|
226
|
-
});
|
|
227
|
-
return Fx.succeed({
|
|
228
|
-
authData,
|
|
229
|
-
credential: authData.credential
|
|
230
|
-
});
|
|
231
|
-
}), Fx.chain(({ authData, credential }) => {
|
|
232
|
-
const credentialId = encodeBase64urlNoPadding(credential.id);
|
|
233
|
-
const publicKey = credential.publicKey;
|
|
234
|
-
let algorithm;
|
|
235
|
-
if (publicKey.isAlgorithmDefined()) algorithm = publicKey.algorithm();
|
|
236
|
-
else {
|
|
237
|
-
const keyType = publicKey.type();
|
|
238
|
-
algorithm = keyType === COSEKeyType.EC2 ? coseAlgorithmES256 : keyType === COSEKeyType.RSA ? coseAlgorithmRS256 : coseAlgorithmES256;
|
|
239
|
-
}
|
|
240
|
-
const handler = {
|
|
241
|
-
[coseAlgorithmES256]: () => {
|
|
242
|
-
const ec2 = publicKey.ec2();
|
|
243
|
-
const xBytes = new Uint8Array(32);
|
|
244
|
-
let vx = ec2.x;
|
|
245
|
-
for (let i = 31; i >= 0; i--) {
|
|
246
|
-
xBytes[i] = Number(vx & 255n);
|
|
247
|
-
vx >>= 8n;
|
|
248
|
-
}
|
|
249
|
-
const yBytes = new Uint8Array(32);
|
|
250
|
-
let vy = ec2.y;
|
|
251
|
-
for (let i = 31; i >= 0; i--) {
|
|
252
|
-
yBytes[i] = Number(vy & 255n);
|
|
253
|
-
vy >>= 8n;
|
|
254
|
-
}
|
|
255
|
-
const bytes = new Uint8Array(65);
|
|
256
|
-
bytes[0] = 4;
|
|
257
|
-
bytes.set(xBytes, 1);
|
|
258
|
-
bytes.set(yBytes, 33);
|
|
259
|
-
return Fx.succeed(bytes);
|
|
260
|
-
},
|
|
261
|
-
[coseAlgorithmRS256]: () => {
|
|
262
|
-
const rsa = publicKey.rsa();
|
|
263
|
-
const rsaPubKey = new RSAPublicKey(rsa.n, rsa.e);
|
|
264
|
-
return Fx.succeed(rsaPubKey.encodePKCS1());
|
|
265
|
-
}
|
|
266
|
-
}[algorithm];
|
|
267
|
-
return (handler ? handler() : Cv.fail({
|
|
268
|
-
code: "PASSKEY_UNSUPPORTED_ALGORITHM",
|
|
269
|
-
message: `Unsupported algorithm: ${algorithm}`
|
|
270
|
-
})).pipe(Fx.chain((publicKeyBytes) => Fx.from({
|
|
271
|
-
ok: async () => {
|
|
272
|
-
const deviceType = params.deviceType ?? "single-device";
|
|
273
|
-
const backedUp = params.backedUp ?? false;
|
|
274
|
-
await authDb(ctx, ctx.auth.config).accounts.create({
|
|
275
|
-
userId,
|
|
276
|
-
provider: provider.id,
|
|
277
|
-
providerAccountId: credentialId
|
|
278
|
-
});
|
|
279
|
-
await mutatePasskeyInsert(ctx, {
|
|
280
|
-
userId,
|
|
281
|
-
credentialId,
|
|
282
|
-
publicKey: publicKeyBytes.buffer.slice(publicKeyBytes.byteOffset, publicKeyBytes.byteOffset + publicKeyBytes.byteLength),
|
|
283
|
-
algorithm,
|
|
284
|
-
counter: authData.signatureCounter,
|
|
285
|
-
transports: params.transports,
|
|
286
|
-
deviceType,
|
|
287
|
-
backedUp,
|
|
288
|
-
name: params.passkeyName,
|
|
289
|
-
createdAt: Date.now()
|
|
290
|
-
});
|
|
291
|
-
return {
|
|
292
|
-
kind: "signedIn",
|
|
293
|
-
signedIn: await callSignIn(ctx, {
|
|
294
|
-
userId,
|
|
295
|
-
generateTokens: true
|
|
296
|
-
})
|
|
297
|
-
};
|
|
298
|
-
},
|
|
299
|
-
err: () => Cv.error({
|
|
300
|
-
code: "INTERNAL_ERROR",
|
|
301
|
-
message: "An unexpected error occurred."
|
|
302
|
-
})
|
|
303
|
-
})));
|
|
304
|
-
}));
|
|
305
|
-
})))),
|
|
306
|
-
authOptions: (_) => resolveRpOptionsFx(provider).pipe(Fx.chain((rp) => {
|
|
307
|
-
const challenge = new Uint8Array(32);
|
|
308
|
-
crypto.getRandomValues(challenge);
|
|
309
|
-
const challengeHash = encodeBase64urlNoPadding(new Uint8Array(sha256(challenge)));
|
|
310
|
-
return Fx.from({
|
|
311
|
-
ok: async () => {
|
|
312
|
-
const verifier = await callVerifier(ctx);
|
|
313
|
-
await callVerifierSignature(ctx, {
|
|
314
|
-
verifier,
|
|
315
|
-
signature: challengeHash
|
|
316
|
-
});
|
|
317
|
-
let allowCredentials;
|
|
318
|
-
if (params.email) {
|
|
319
|
-
const user = await queryUserByVerifiedEmail(ctx, params.email);
|
|
320
|
-
if (user) {
|
|
321
|
-
const passkeys = await queryPasskeysByUserId(ctx, user._id);
|
|
322
|
-
if (passkeys.length > 0) allowCredentials = passkeys.map((pk) => ({
|
|
323
|
-
type: "public-key",
|
|
324
|
-
id: pk.credentialId,
|
|
325
|
-
transports: pk.transports
|
|
326
|
-
}));
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
const options = {
|
|
330
|
-
challenge: encodeBase64urlNoPadding(challenge),
|
|
331
|
-
timeout: rp.challengeExpirationMs,
|
|
332
|
-
rpId: rp.rpId,
|
|
333
|
-
userVerification: rp.userVerification
|
|
334
|
-
};
|
|
335
|
-
if (allowCredentials) options.allowCredentials = allowCredentials;
|
|
336
|
-
return {
|
|
337
|
-
kind: "passkeyOptions",
|
|
338
|
-
options,
|
|
339
|
-
verifier
|
|
340
|
-
};
|
|
341
|
-
},
|
|
342
|
-
err: () => Cv.error({
|
|
343
|
-
code: "INTERNAL_ERROR",
|
|
344
|
-
message: "An unexpected error occurred."
|
|
345
|
-
})
|
|
346
|
-
});
|
|
347
|
-
})),
|
|
348
|
-
authVerify: (_) => Fx.zip(resolveRpOptionsFx(provider), requirePasskeyVerifierFx(args.verifier)).pipe(Fx.chain(([rp, verifier]) => {
|
|
349
|
-
const clientDataJSON = decodeBase64urlIgnorePadding(params.clientDataJSON);
|
|
350
|
-
const clientData = parseClientDataJSON(clientDataJSON);
|
|
351
|
-
return Fx.succeed(clientData).pipe(Fx.chain(verifyClientDataType(ClientDataType.Get, "webauthn.get")), Fx.chain(verifyOrigin(rp)), Fx.chain(verifyAndConsumeChallenge(ctx, verifier)), Fx.chain(() => params.credentialId != null ? Fx.succeed(params.credentialId) : Cv.fail({
|
|
352
|
-
code: "PASSKEY_UNKNOWN_CREDENTIAL",
|
|
353
|
-
message: "Missing credential ID"
|
|
354
|
-
}))).pipe(Fx.chain((credentialId) => Fx.from({
|
|
355
|
-
ok: () => queryPasskeyByCredentialId(ctx, credentialId),
|
|
356
|
-
err: () => Cv.error({
|
|
357
|
-
code: "PASSKEY_UNKNOWN_CREDENTIAL",
|
|
358
|
-
message: "Unknown passkey credential."
|
|
359
|
-
})
|
|
360
|
-
}).pipe(Fx.chain((passkey) => passkey ? Fx.succeed(passkey) : Cv.fail({
|
|
361
|
-
code: "PASSKEY_UNKNOWN_CREDENTIAL",
|
|
362
|
-
message: "Unknown credential"
|
|
363
|
-
})))), Fx.chain((passkey) => {
|
|
364
|
-
const authenticatorDataBytes = decodeBase64urlIgnorePadding(params.authenticatorData);
|
|
365
|
-
const authenticatorData = parseAuthenticatorData(authenticatorDataBytes);
|
|
366
|
-
const signature = decodeBase64urlIgnorePadding(params.signature);
|
|
367
|
-
const messageHash = sha256(createAssertionSignatureMessage(authenticatorDataBytes, clientDataJSON));
|
|
368
|
-
return Fx.succeed(authenticatorData).pipe(Fx.chain(verifyRpId(rp.rpId)), Fx.chain(verifyUserFlags(rp))).pipe(Fx.chain(() => {
|
|
369
|
-
const storedPublicKeyBytes = new Uint8Array(passkey.publicKey);
|
|
370
|
-
const handler = {
|
|
371
|
-
[coseAlgorithmES256]: () => {
|
|
372
|
-
return verifyECDSASignature(decodeSEC1PublicKey(p256, storedPublicKeyBytes), messageHash, decodePKIXECDSASignature(signature)) ? Fx.succeed(void 0) : Cv.fail({
|
|
373
|
-
code: "PASSKEY_INVALID_SIGNATURE",
|
|
374
|
-
message: "Invalid passkey signature."
|
|
375
|
-
});
|
|
376
|
-
},
|
|
377
|
-
[coseAlgorithmRS256]: () => {
|
|
378
|
-
return verifyRSASSAPKCS1v15Signature(decodePKCS1RSAPublicKey(storedPublicKeyBytes), sha256ObjectIdentifier, messageHash, signature) ? Fx.succeed(void 0) : Cv.fail({
|
|
379
|
-
code: "PASSKEY_INVALID_SIGNATURE",
|
|
380
|
-
message: "Invalid passkey signature."
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
}[passkey.algorithm];
|
|
384
|
-
return handler ? handler() : Cv.fail({
|
|
385
|
-
code: "PASSKEY_UNSUPPORTED_ALGORITHM",
|
|
386
|
-
message: `Unsupported algorithm: ${passkey.algorithm}`
|
|
387
|
-
});
|
|
388
|
-
})).pipe(Fx.chain(() => passkey.counter !== 0 && authenticatorData.signatureCounter !== 0 && authenticatorData.signatureCounter <= passkey.counter ? Cv.fail({
|
|
389
|
-
code: "PASSKEY_COUNTER_ERROR",
|
|
390
|
-
message: "Authenticator counter did not increase — possible credential cloning detected."
|
|
391
|
-
}) : Fx.succeed(authenticatorData))).pipe(Fx.chain(() => Fx.from({
|
|
392
|
-
ok: async () => {
|
|
393
|
-
await mutatePasskeyUpdateCounter(ctx, passkey._id, authenticatorData.signatureCounter, Date.now());
|
|
394
|
-
return {
|
|
395
|
-
kind: "signedIn",
|
|
396
|
-
signedIn: await callSignIn(ctx, {
|
|
397
|
-
userId: passkey.userId,
|
|
398
|
-
generateTokens: true
|
|
399
|
-
})
|
|
400
|
-
};
|
|
401
|
-
},
|
|
402
|
-
err: () => Cv.error({
|
|
403
|
-
code: "INTERNAL_ERROR",
|
|
404
|
-
message: "An unexpected error occurred."
|
|
405
|
-
})
|
|
406
|
-
})));
|
|
407
|
-
}));
|
|
408
|
-
}))
|
|
409
|
-
});
|
|
410
|
-
}));
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
//#endregion
|
|
414
|
-
export { handlePasskeyFx };
|
|
415
|
-
//# sourceMappingURL=passkey.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"passkey.js","names":["hasSiteUrl","hasRpId","siteUrl"],"sources":["../../../src/server/passkey.ts"],"sourcesContent":["/**\n * Server-side WebAuthn ceremony logic for passkey authentication.\n *\n * Handles the four phases of the WebAuthn flow:\n * 1. registerOptions — generate PublicKeyCredentialCreationOptions\n * 2. registerVerify — verify attestation and store credential\n * 3. authOptions — generate PublicKeyCredentialRequestOptions\n * 4. authVerify — verify assertion signature and sign in\n *\n * Uses `@oslojs/webauthn` for attestation/assertion parsing and\n * `@oslojs/crypto` for signature verification.\n *\n * All functions return `Fx<A, ConvexError<any>>` composed via `Fx.chain` pipelines.\n *\n * @module\n */\n\nimport {\n p256,\n verifyECDSASignature,\n decodeSEC1PublicKey,\n decodePKIXECDSASignature,\n} from \"@oslojs/crypto/ecdsa\";\nimport {\n RSAPublicKey,\n decodePKCS1RSAPublicKey,\n sha256ObjectIdentifier,\n verifyRSASSAPKCS1v15Signature,\n} from \"@oslojs/crypto/rsa\";\nimport { sha256 } from \"@oslojs/crypto/sha2\";\nimport {\n encodeBase64urlNoPadding,\n decodeBase64urlIgnorePadding,\n} from \"@oslojs/encoding\";\nimport {\n parseAttestationObject,\n parseClientDataJSON,\n parseAuthenticatorData,\n createAssertionSignatureMessage,\n ClientDataType,\n coseAlgorithmES256,\n coseAlgorithmRS256,\n COSEKeyType,\n} from \"@oslojs/webauthn\";\nimport type { Fx as FxType } from \"@robelest/fx\";\nimport { Fx } from \"@robelest/fx\";\nimport { Cv } from \"@robelest/fx/convex\";\nimport type { ConvexError } from \"convex/values\";\n\nimport { authDb } from \"./db\";\nimport { userIdFromIdentitySubject } from \"./identity\";\nimport { callSignIn, callVerifier } from \"./mutations/index\";\nimport { callVerifierSignature } from \"./mutations/signature\";\nimport { PasskeyProviderConfig, GenericActionCtxWithAuthConfig } from \"./types\";\nimport {\n AuthDataModel,\n SessionInfo,\n queryUserById,\n queryUserByVerifiedEmail,\n queryPasskeysByUserId,\n queryPasskeyByCredentialId,\n queryVerifierById,\n mutatePasskeyInsert,\n mutatePasskeyUpdateCounter,\n mutateVerifierDelete,\n} from \"./types\";\nimport { siteUrlsFromEnv } from \"./utils\";\n\ntype EnrichedActionCtx = GenericActionCtxWithAuthConfig<AuthDataModel>;\n\n// ============================================================================\n// Resolve RP options — Fx pipeline with validation\n// ============================================================================\n\n/** Resolved relying party configuration. */\ninterface RpOptions {\n rpName: string;\n rpId: string;\n origin: string | string[];\n attestation: string;\n userVerification: string;\n residentKey: string;\n authenticatorAttachment?: string;\n algorithms: number[];\n challengeExpirationMs: number;\n}\n\n/**\n * Resolve passkey relying party options from provider config and environment.\n *\n * Returns `Fx<RpOptions, ConvexError<any>>` — fails if neither SITE_URL nor rpId\n * is configured.\n */\nconst resolveRpOptionsFx = (\n provider: PasskeyProviderConfig,\n): FxType<RpOptions, ConvexError<any>> => {\n const configuredSiteUrls =\n process.env.SITE_URL === undefined ? null : siteUrlsFromEnv();\n const siteUrl = configuredSiteUrls?.primaryUrl;\n const hasSiteUrl = siteUrl !== undefined && siteUrl !== \"\";\n const hasRpId = provider.options.rpId !== undefined;\n\n return Fx.succeed({ siteUrl, hasSiteUrl, hasRpId }).pipe(\n Fx.chain(({ siteUrl, hasSiteUrl, hasRpId }) =>\n !hasSiteUrl && !hasRpId\n ? Cv.fail({\n code: \"PASSKEY_MISSING_CONFIG\",\n message:\n \"Passkey provider requires SITE_URL env var (your frontend URL) \" +\n \"or explicit rpId / origin in the provider config. \" +\n \"CONVEX_SITE_URL cannot be used because WebAuthn RP ID must match the frontend domain.\",\n })\n : Fx.succeed(siteUrl),\n ),\n Fx.map((siteUrl) => {\n const siteHostname = siteUrl ? new URL(siteUrl).hostname : undefined;\n const defaultOrigin = configuredSiteUrls?.allowedUrls ?? siteUrl;\n return {\n rpName: provider.options.rpName ?? siteHostname ?? \"localhost\",\n rpId: provider.options.rpId ?? siteHostname ?? \"localhost\",\n origin: provider.options.origin ?? defaultOrigin ?? \"http://localhost\",\n attestation: provider.options.attestation ?? \"none\",\n userVerification: provider.options.userVerification ?? \"required\",\n residentKey: provider.options.residentKey ?? \"preferred\",\n authenticatorAttachment: provider.options.authenticatorAttachment,\n algorithms: provider.options.algorithms ?? [\n coseAlgorithmES256,\n coseAlgorithmRS256,\n ],\n challengeExpirationMs:\n provider.options.challengeExpirationMs ?? 300_000,\n };\n }),\n );\n};\n\n// ============================================================================\n// Composable validators — small functions (A) => Fx<B, ConvexError<any>>\n// ============================================================================\n\n/** Verify client data type matches expected WebAuthn ceremony type. */\nconst verifyClientDataType =\n <T extends { type: ClientDataType }>(\n expectedType: ClientDataType,\n label: string,\n ) =>\n (clientData: T): FxType<T, ConvexError<any>> =>\n clientData.type === expectedType\n ? Fx.succeed(clientData)\n : Cv.fail({\n code: \"PASSKEY_INVALID_CLIENT_DATA\",\n message: `Invalid client data type: expected ${label}`,\n });\n\n/** Verify origin is in the allowed list. */\nconst verifyOrigin =\n (rp: RpOptions) =>\n <T extends { origin: string }>(\n clientData: T,\n ): FxType<T, ConvexError<any>> => {\n const allowed = Array.isArray(rp.origin) ? rp.origin : [rp.origin];\n return allowed.includes(clientData.origin)\n ? Fx.succeed(clientData)\n : Cv.fail({\n code: \"PASSKEY_INVALID_ORIGIN\",\n message: `Invalid origin: ${clientData.origin}, expected one of: ${allowed.join(\", \")}`,\n });\n };\n\n/** Verify the challenge hash matches the stored verifier, then delete verifier. */\nconst verifyAndConsumeChallenge =\n (ctx: EnrichedActionCtx, verifierValue: string) =>\n <T extends { challenge: Uint8Array }>(\n clientData: T,\n ): FxType<T, ConvexError<any>> => {\n const challengeHash = encodeBase64urlNoPadding(\n new Uint8Array(sha256(clientData.challenge)),\n );\n return Fx.from({\n ok: () => queryVerifierById(ctx, verifierValue),\n err: () =>\n Cv.error({\n code: \"PASSKEY_INVALID_CHALLENGE\",\n message: \"Invalid or expired passkey challenge.\",\n }),\n }).pipe(\n Fx.chain((doc) =>\n !doc || doc.signature !== challengeHash\n ? Cv.fail({\n code: \"PASSKEY_INVALID_CHALLENGE\",\n message: \"Invalid or expired passkey challenge.\",\n })\n : Fx.succeed(doc),\n ),\n Fx.chain(() =>\n Fx.from({\n ok: () => mutateVerifierDelete(ctx, verifierValue),\n err: () =>\n Cv.error({\n code: \"PASSKEY_INVALID_CHALLENGE\",\n message: \"Invalid or expired passkey challenge.\",\n }),\n }),\n ),\n Fx.map(() => clientData),\n );\n };\n\n/** Verify RP ID hash matches. */\nconst verifyRpId =\n (rpId: string) =>\n <T extends { verifyRelyingPartyIdHash: (id: string) => boolean }>(\n authData: T,\n ): FxType<T, ConvexError<any>> =>\n authData.verifyRelyingPartyIdHash(rpId)\n ? Fx.succeed(authData)\n : Cv.fail({\n code: \"PASSKEY_RP_MISMATCH\",\n message: \"Relying party ID mismatch.\",\n });\n\n/** Verify user presence and (optionally) user verification flags. */\nconst verifyUserFlags =\n (rp: RpOptions) =>\n <T extends { userPresent: boolean; userVerified: boolean }>(\n authData: T,\n ): FxType<T, ConvexError<any>> =>\n !authData.userPresent\n ? Cv.fail({\n code: \"PASSKEY_USER_PRESENCE\",\n message: \"User presence flag not set.\",\n })\n : rp.userVerification === \"required\" && !authData.userVerified\n ? Cv.fail({\n code: \"PASSKEY_USER_VERIFICATION\",\n message: \"User verification required but not performed.\",\n })\n : Fx.succeed(authData);\n\n// ============================================================================\n// Registration flow\n// ============================================================================\n\n// ============================================================================\n// Authentication flow\n// ============================================================================\n\n// ============================================================================\n// Main dispatch\n// ============================================================================\n\n/** Result type for all passkey flows. */\ntype PasskeyResult =\n | { kind: \"signedIn\"; signedIn: SessionInfo | null }\n | { kind: \"passkeyOptions\"; options: Record<string, any>; verifier: string };\n\nconst PASSKEY_FLOW = {\n registerOptions: \"registerOptions\",\n registerVerify: \"registerVerify\",\n authOptions: \"authOptions\",\n authVerify: \"authVerify\",\n} as const;\n\nconst PASSKEY_FLOWS = [\n PASSKEY_FLOW.registerOptions,\n PASSKEY_FLOW.registerVerify,\n PASSKEY_FLOW.authOptions,\n PASSKEY_FLOW.authVerify,\n] as const;\n\ntype PasskeyDispatch =\n | { flow: typeof PASSKEY_FLOW.registerOptions }\n | { flow: typeof PASSKEY_FLOW.registerVerify }\n | { flow: typeof PASSKEY_FLOW.authOptions }\n | { flow: typeof PASSKEY_FLOW.authVerify };\n\nconst resolvePasskeyDispatchFx = (\n params: Record<string, unknown>,\n): FxType<PasskeyDispatch, ConvexError<any>> => {\n const flow = params.flow;\n return typeof flow === \"string\" && PASSKEY_FLOWS.includes(flow as never)\n ? Fx.succeed({ flow: flow as (typeof PASSKEY_FLOWS)[number] })\n : Cv.fail({\n code: \"PASSKEY_MISSING_FLOW\",\n message:\n \"Missing `flow` parameter. Expected one of: registerOptions, registerVerify, authOptions, authVerify\",\n });\n};\n\nconst requirePasskeyVerifierFx = (\n verifier: string | undefined,\n): FxType<string, ConvexError<any>> =>\n verifier != null\n ? Fx.succeed(verifier)\n : Cv.fail({\n code: \"PASSKEY_MISSING_VERIFIER\",\n message: \"Missing verifier for passkey operation.\",\n });\n\n/**\n * Main passkey handler dispatched from signIn.ts.\n *\n * Routes to the appropriate phase based on `params.flow` via `dispatchFx`.\n */\nexport function handlePasskeyFx(\n ctx: EnrichedActionCtx,\n provider: PasskeyProviderConfig,\n args: {\n params?: Record<string, any>;\n verifier?: string;\n },\n): FxType<PasskeyResult, ConvexError<any>> {\n const params = (args.params ?? {}) as Record<string, any>;\n\n return resolvePasskeyDispatchFx(params).pipe(\n Fx.chain((dispatch) => {\n const flowFx: FxType<PasskeyResult, ConvexError<any>> = Fx.match(\n dispatch,\n ).on(\"flow\", {\n registerOptions: (_) =>\n Fx.zip(\n Fx.from({\n ok: () => ctx.auth.getUserIdentity(),\n err: () =>\n Cv.error({\n code: \"PASSKEY_AUTH_REQUIRED\",\n message: \"Sign in first, then add a passkey to your account.\",\n }),\n }).pipe(\n Fx.chain((id) =>\n id === null\n ? Cv.fail({\n code: \"PASSKEY_AUTH_REQUIRED\",\n message:\n \"Sign in first, then add a passkey to your account.\",\n })\n : Fx.succeed(userIdFromIdentitySubject(id.subject)),\n ),\n ),\n resolveRpOptionsFx(provider),\n ).pipe(\n Fx.chain(([userId, rp]) => {\n const challenge = new Uint8Array(32);\n crypto.getRandomValues(challenge);\n const challengeHash = encodeBase64urlNoPadding(\n new Uint8Array(sha256(challenge)),\n );\n\n return Fx.from({\n ok: async () => {\n const verifier = await callVerifier(ctx);\n await callVerifierSignature(ctx, {\n verifier,\n signature: challengeHash,\n });\n\n const user = await queryUserById(ctx, userId);\n const userName = params.userName ?? user?.email ?? \"user\";\n const userDisplayName =\n params.userDisplayName ?? user?.name ?? userName;\n\n const existing = await queryPasskeysByUserId(ctx, userId);\n const excludeCredentials = existing.map((pk) => ({\n id: pk.credentialId,\n transports: pk.transports,\n }));\n\n const userHandle = encodeBase64urlNoPadding(\n new TextEncoder().encode(userId),\n );\n\n const options = {\n rp: { name: rp.rpName, id: rp.rpId },\n user: {\n id: userHandle,\n name: userName,\n displayName: userDisplayName,\n },\n challenge: encodeBase64urlNoPadding(challenge),\n pubKeyCredParams: rp.algorithms.map((alg) => ({\n type: \"public-key\" as const,\n alg,\n })),\n timeout: rp.challengeExpirationMs,\n attestation: rp.attestation,\n authenticatorSelection: {\n residentKey: rp.residentKey,\n requireResidentKey: rp.residentKey === \"required\",\n userVerification: rp.userVerification,\n ...(rp.authenticatorAttachment\n ? {\n authenticatorAttachment: rp.authenticatorAttachment,\n }\n : {}),\n },\n excludeCredentials,\n };\n\n return {\n kind: \"passkeyOptions\" as const,\n options,\n verifier,\n };\n },\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"An unexpected error occurred.\",\n }),\n });\n }),\n ),\n registerVerify: (_) =>\n Fx.zip(\n Fx.from({\n ok: () => ctx.auth.getUserIdentity(),\n err: () =>\n Cv.error({\n code: \"PASSKEY_AUTH_REQUIRED\",\n message: \"Sign in first, then add a passkey to your account.\",\n }),\n }).pipe(\n Fx.chain((id) =>\n id === null\n ? Cv.fail({\n code: \"PASSKEY_AUTH_REQUIRED\",\n message:\n \"Sign in first, then add a passkey to your account.\",\n })\n : Fx.succeed(userIdFromIdentitySubject(id.subject)),\n ),\n ),\n resolveRpOptionsFx(provider),\n ).pipe(\n Fx.chain(([userId, rp]) =>\n requirePasskeyVerifierFx(args.verifier).pipe(\n Fx.chain((verifier) => {\n const clientDataJSON = decodeBase64urlIgnorePadding(\n params.clientDataJSON,\n );\n const clientData = parseClientDataJSON(clientDataJSON);\n\n const verifiedClientDataFx = Fx.succeed(clientData).pipe(\n Fx.chain(\n verifyClientDataType(\n ClientDataType.Create,\n \"webauthn.create\",\n ),\n ),\n Fx.chain(verifyOrigin(rp)),\n Fx.chain(verifyAndConsumeChallenge(ctx, verifier)),\n Fx.map(() => {\n const attestationObjectBytes =\n decodeBase64urlIgnorePadding(params.attestationObject);\n const attestation = parseAttestationObject(\n attestationObjectBytes,\n );\n return attestation.authenticatorData;\n }),\n );\n\n return verifiedClientDataFx.pipe(\n Fx.chain(verifyRpId(rp.rpId)),\n Fx.chain(verifyUserFlags(rp)),\n Fx.chain((authData) => {\n if (authData.credential == null) {\n return Cv.fail({\n code: \"PASSKEY_NO_CREDENTIAL\",\n message: \"No credential in attestation.\",\n });\n }\n return Fx.succeed({\n authData,\n credential: authData.credential,\n });\n }),\n Fx.chain(({ authData, credential }) => {\n const credentialId = encodeBase64urlNoPadding(\n credential.id,\n );\n const publicKey = credential.publicKey;\n\n let algorithm: number;\n if (publicKey.isAlgorithmDefined()) {\n algorithm = publicKey.algorithm();\n } else {\n const keyType = publicKey.type();\n algorithm =\n keyType === COSEKeyType.EC2\n ? coseAlgorithmES256\n : keyType === COSEKeyType.RSA\n ? coseAlgorithmRS256\n : coseAlgorithmES256;\n }\n\n const handlers: Record<\n number,\n (() => FxType<Uint8Array, ConvexError<any>>) | undefined\n > = {\n [coseAlgorithmES256]: () => {\n const ec2 = publicKey.ec2();\n const xBytes = new Uint8Array(32);\n let vx = ec2.x;\n for (let i = 31; i >= 0; i--) {\n xBytes[i] = Number(vx & 0xffn);\n vx >>= 8n;\n }\n const yBytes = new Uint8Array(32);\n let vy = ec2.y;\n for (let i = 31; i >= 0; i--) {\n yBytes[i] = Number(vy & 0xffn);\n vy >>= 8n;\n }\n const bytes = new Uint8Array(65);\n bytes[0] = 0x04;\n bytes.set(xBytes, 1);\n bytes.set(yBytes, 33);\n return Fx.succeed(bytes);\n },\n [coseAlgorithmRS256]: () => {\n const rsa = publicKey.rsa();\n const rsaPubKey = new RSAPublicKey(rsa.n, rsa.e);\n return Fx.succeed(rsaPubKey.encodePKCS1());\n },\n };\n\n const handler = handlers[algorithm];\n return (\n handler\n ? handler()\n : Cv.fail({\n code: \"PASSKEY_UNSUPPORTED_ALGORITHM\",\n message: `Unsupported algorithm: ${algorithm}`,\n })\n ).pipe(\n Fx.chain((publicKeyBytes) =>\n Fx.from({\n ok: async () => {\n const deviceType =\n params.deviceType ?? \"single-device\";\n const backedUp = params.backedUp ?? false;\n\n const db = authDb(ctx, ctx.auth.config);\n await db.accounts.create({\n userId,\n provider: provider.id,\n providerAccountId: credentialId,\n });\n\n await mutatePasskeyInsert(ctx, {\n userId,\n credentialId,\n publicKey: publicKeyBytes.buffer.slice(\n publicKeyBytes.byteOffset,\n publicKeyBytes.byteOffset +\n publicKeyBytes.byteLength,\n ),\n algorithm,\n counter: authData.signatureCounter,\n transports: params.transports,\n deviceType,\n backedUp,\n name: params.passkeyName,\n createdAt: Date.now(),\n });\n\n const signInResult = await callSignIn(ctx, {\n userId,\n generateTokens: true,\n });\n\n return {\n kind: \"signedIn\" as const,\n signedIn: signInResult,\n };\n },\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"An unexpected error occurred.\",\n }),\n }),\n ),\n );\n }),\n );\n }),\n ),\n ),\n ),\n authOptions: (_) =>\n resolveRpOptionsFx(provider).pipe(\n Fx.chain((rp) => {\n const challenge = new Uint8Array(32);\n crypto.getRandomValues(challenge);\n const challengeHash = encodeBase64urlNoPadding(\n new Uint8Array(sha256(challenge)),\n );\n\n return Fx.from({\n ok: async () => {\n const verifier = await callVerifier(ctx);\n await callVerifierSignature(ctx, {\n verifier,\n signature: challengeHash,\n });\n\n let allowCredentials:\n | Array<{\n type: string;\n id: string;\n transports?: string[];\n }>\n | undefined;\n if (params.email) {\n const user = await queryUserByVerifiedEmail(\n ctx,\n params.email,\n );\n if (user) {\n const passkeys = await queryPasskeysByUserId(\n ctx,\n user._id,\n );\n if (passkeys.length > 0) {\n allowCredentials = passkeys.map((pk) => ({\n type: \"public-key\",\n id: pk.credentialId,\n transports: pk.transports,\n }));\n }\n }\n }\n\n const options: Record<string, any> = {\n challenge: encodeBase64urlNoPadding(challenge),\n timeout: rp.challengeExpirationMs,\n rpId: rp.rpId,\n userVerification: rp.userVerification,\n };\n\n if (allowCredentials) {\n options.allowCredentials = allowCredentials;\n }\n\n return {\n kind: \"passkeyOptions\" as const,\n options,\n verifier,\n };\n },\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"An unexpected error occurred.\",\n }),\n });\n }),\n ),\n authVerify: (_) =>\n Fx.zip(\n resolveRpOptionsFx(provider),\n requirePasskeyVerifierFx(args.verifier),\n ).pipe(\n Fx.chain(([rp, verifier]) => {\n const clientDataJSON = decodeBase64urlIgnorePadding(\n params.clientDataJSON,\n );\n const clientData = parseClientDataJSON(clientDataJSON);\n\n const verifiedClientDataFx = Fx.succeed(clientData).pipe(\n Fx.chain(\n verifyClientDataType(ClientDataType.Get, \"webauthn.get\"),\n ),\n Fx.chain(verifyOrigin(rp)),\n Fx.chain(verifyAndConsumeChallenge(ctx, verifier)),\n Fx.chain(() =>\n params.credentialId != null\n ? Fx.succeed(params.credentialId as string)\n : Cv.fail({\n code: \"PASSKEY_UNKNOWN_CREDENTIAL\",\n message: \"Missing credential ID\",\n }),\n ),\n );\n\n return verifiedClientDataFx.pipe(\n Fx.chain((credentialId) =>\n Fx.from({\n ok: () => queryPasskeyByCredentialId(ctx, credentialId),\n err: () =>\n Cv.error({\n code: \"PASSKEY_UNKNOWN_CREDENTIAL\",\n message: \"Unknown passkey credential.\",\n }),\n }).pipe(\n Fx.chain((passkey) =>\n passkey\n ? Fx.succeed(passkey)\n : Cv.fail({\n code: \"PASSKEY_UNKNOWN_CREDENTIAL\",\n message: \"Unknown credential\",\n }),\n ),\n ),\n ),\n Fx.chain((passkey) => {\n const authenticatorDataBytes = decodeBase64urlIgnorePadding(\n params.authenticatorData,\n );\n const authenticatorData = parseAuthenticatorData(\n authenticatorDataBytes,\n );\n\n const signature = decodeBase64urlIgnorePadding(\n params.signature,\n );\n const signatureMessage = createAssertionSignatureMessage(\n authenticatorDataBytes,\n clientDataJSON,\n );\n const messageHash = sha256(signatureMessage);\n\n const checkedAuthenticatorFx = Fx.succeed(\n authenticatorData,\n ).pipe(\n Fx.chain(verifyRpId(rp.rpId)),\n Fx.chain(verifyUserFlags(rp)),\n );\n\n const signatureVerifiedFx = checkedAuthenticatorFx.pipe(\n Fx.chain(() => {\n const storedPublicKeyBytes = new Uint8Array(\n passkey.publicKey,\n );\n const algorithmHandlers: Record<\n number,\n (() => FxType<void, ConvexError<any>>) | undefined\n > = {\n [coseAlgorithmES256]: () => {\n const ecPublicKey = decodeSEC1PublicKey(\n p256,\n storedPublicKeyBytes,\n );\n const ecdsaSignature =\n decodePKIXECDSASignature(signature);\n const valid = verifyECDSASignature(\n ecPublicKey,\n messageHash,\n ecdsaSignature,\n );\n return valid\n ? Fx.succeed(undefined as void)\n : Cv.fail({\n code: \"PASSKEY_INVALID_SIGNATURE\",\n message: \"Invalid passkey signature.\",\n });\n },\n [coseAlgorithmRS256]: () => {\n const rsaPublicKey =\n decodePKCS1RSAPublicKey(storedPublicKeyBytes);\n const valid = verifyRSASSAPKCS1v15Signature(\n rsaPublicKey,\n sha256ObjectIdentifier,\n messageHash,\n signature,\n );\n return valid\n ? Fx.succeed(undefined as void)\n : Cv.fail({\n code: \"PASSKEY_INVALID_SIGNATURE\",\n message: \"Invalid passkey signature.\",\n });\n },\n };\n\n const handler = algorithmHandlers[passkey.algorithm];\n return handler\n ? handler()\n : Cv.fail({\n code: \"PASSKEY_UNSUPPORTED_ALGORITHM\",\n message: `Unsupported algorithm: ${passkey.algorithm}`,\n });\n }),\n );\n\n const counterValidatedFx = signatureVerifiedFx.pipe(\n Fx.chain(() =>\n passkey.counter !== 0 &&\n authenticatorData.signatureCounter !== 0 &&\n authenticatorData.signatureCounter <= passkey.counter\n ? Cv.fail({\n code: \"PASSKEY_COUNTER_ERROR\",\n message:\n \"Authenticator counter did not increase — possible credential cloning detected.\",\n })\n : Fx.succeed(authenticatorData),\n ),\n );\n\n return counterValidatedFx.pipe(\n Fx.chain(() =>\n Fx.from({\n ok: async () => {\n await mutatePasskeyUpdateCounter(\n ctx,\n passkey._id,\n authenticatorData.signatureCounter,\n Date.now(),\n );\n\n const signInResult = await callSignIn(ctx, {\n userId: passkey.userId,\n generateTokens: true,\n });\n\n return {\n kind: \"signedIn\" as const,\n signedIn: signInResult,\n };\n },\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"An unexpected error occurred.\",\n }),\n }),\n ),\n );\n }),\n );\n }),\n ),\n });\n return flowFx;\n }),\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { requireEnv } from "./utils.js";
|
|
2
|
-
import { Cv } from "@robelest/fx/convex";
|
|
3
|
-
|
|
4
|
-
//#region src/server/redirects.ts
|
|
5
|
-
/** @internal */
|
|
6
|
-
async function redirectAbsoluteUrl(config, params) {
|
|
7
|
-
if (params.redirectTo === void 0) return requireEnv("SITE_URL").replace(/\/$/, "");
|
|
8
|
-
if (typeof params.redirectTo !== "string") throw Cv.error({
|
|
9
|
-
code: "INVALID_REDIRECT",
|
|
10
|
-
message: `Expected \`redirectTo\` to be a string, got ${params.redirectTo}`
|
|
11
|
-
});
|
|
12
|
-
const redirectCallback = config.callbacks?.redirect ?? defaultRedirectCallback;
|
|
13
|
-
try {
|
|
14
|
-
return await redirectCallback({ redirectTo: params.redirectTo });
|
|
15
|
-
} catch {
|
|
16
|
-
throw Cv.error({
|
|
17
|
-
code: "INTERNAL_ERROR",
|
|
18
|
-
message: "An unexpected error occurred."
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
async function defaultRedirectCallback({ redirectTo }) {
|
|
23
|
-
if (redirectTo.startsWith("?") || redirectTo.startsWith("/")) return `${requireEnv("SITE_URL").replace(/\/$/, "")}${redirectTo}`;
|
|
24
|
-
return redirectTo;
|
|
25
|
-
}
|
|
26
|
-
/** @internal */
|
|
27
|
-
function setURLSearchParam(absoluteUrl, param, value) {
|
|
28
|
-
const pattern = /([^:]+):(.*)/;
|
|
29
|
-
const [, scheme, rest] = absoluteUrl.match(pattern);
|
|
30
|
-
const hasNoDomain = /^\/\/(?:\/|$|\?)/.test(rest);
|
|
31
|
-
const startsWithPath = hasNoDomain && rest.startsWith("///");
|
|
32
|
-
const url = new URL(`http:${hasNoDomain ? "//googblibok" + rest.slice(2) : rest}`);
|
|
33
|
-
url.searchParams.set(param, value);
|
|
34
|
-
const [, , withParam] = url.toString().match(pattern);
|
|
35
|
-
return `${scheme}:${hasNoDomain ? (startsWithPath ? "/" : "") + "//" + withParam.slice(13) : withParam}`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
//#endregion
|
|
39
|
-
export { redirectAbsoluteUrl, setURLSearchParam };
|
|
40
|
-
//# sourceMappingURL=redirects.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"redirects.js","names":[],"sources":["../../../src/server/redirects.ts"],"sourcesContent":["import { Cv } from \"@robelest/fx/convex\";\n\nimport { ConvexAuthMaterializedConfig } from \"./types\";\nimport { requireEnv } from \"./utils\";\n\n/** @internal */\nexport async function redirectAbsoluteUrl(\n config: ConvexAuthMaterializedConfig,\n params: { redirectTo: unknown },\n) {\n if (params.redirectTo === undefined) {\n return requireEnv(\"SITE_URL\").replace(/\\/$/, \"\");\n }\n if (typeof params.redirectTo !== \"string\") {\n throw Cv.error({\n code: \"INVALID_REDIRECT\",\n message: `Expected \\`redirectTo\\` to be a string, got ${params.redirectTo as any}`,\n });\n }\n const redirectCallback =\n config.callbacks?.redirect ?? defaultRedirectCallback;\n try {\n return await redirectCallback({ redirectTo: params.redirectTo });\n } catch {\n throw Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"An unexpected error occurred.\",\n });\n }\n}\n\nasync function defaultRedirectCallback({ redirectTo }: { redirectTo: string }) {\n // Resolve relative paths against SITE_URL; absolute URLs are passed through\n // as-is. The developer is trusted to provide valid redirect targets.\n if (redirectTo.startsWith(\"?\") || redirectTo.startsWith(\"/\")) {\n return `${requireEnv(\"SITE_URL\").replace(/\\/$/, \"\")}${redirectTo}`;\n }\n return redirectTo;\n}\n\n// Temporary work-around because Convex doesn't support\n// schemes other than http and https.\n/** @internal */\nexport function setURLSearchParam(\n absoluteUrl: string,\n param: string,\n value: string,\n) {\n const pattern = /([^:]+):(.*)/;\n const [, scheme, rest] = absoluteUrl.match(pattern)!;\n const hasNoDomain = /^\\/\\/(?:\\/|$|\\?)/.test(rest);\n const startsWithPath = hasNoDomain && rest.startsWith(\"///\");\n const url = new URL(\n `http:${hasNoDomain ? \"//googblibok\" + rest.slice(2) : rest}`,\n );\n url.searchParams.set(param, value);\n const [, , withParam] = url.toString().match(pattern)!;\n return `${scheme}:${hasNoDomain ? (startsWithPath ? \"/\" : \"\") + \"//\" + withParam.slice(13) : withParam}`;\n}\n"],"mappings":";;;;;AAMA,eAAsB,oBACpB,QACA,QACA;AACA,KAAI,OAAO,eAAe,OACxB,QAAO,WAAW,WAAW,CAAC,QAAQ,OAAO,GAAG;AAElD,KAAI,OAAO,OAAO,eAAe,SAC/B,OAAM,GAAG,MAAM;EACb,MAAM;EACN,SAAS,+CAA+C,OAAO;EAChE,CAAC;CAEJ,MAAM,mBACJ,OAAO,WAAW,YAAY;AAChC,KAAI;AACF,SAAO,MAAM,iBAAiB,EAAE,YAAY,OAAO,YAAY,CAAC;SAC1D;AACN,QAAM,GAAG,MAAM;GACb,MAAM;GACN,SAAS;GACV,CAAC;;;AAIN,eAAe,wBAAwB,EAAE,cAAsC;AAG7E,KAAI,WAAW,WAAW,IAAI,IAAI,WAAW,WAAW,IAAI,CAC1D,QAAO,GAAG,WAAW,WAAW,CAAC,QAAQ,OAAO,GAAG,GAAG;AAExD,QAAO;;;AAMT,SAAgB,kBACd,aACA,OACA,OACA;CACA,MAAM,UAAU;CAChB,MAAM,GAAG,QAAQ,QAAQ,YAAY,MAAM,QAAQ;CACnD,MAAM,cAAc,mBAAmB,KAAK,KAAK;CACjD,MAAM,iBAAiB,eAAe,KAAK,WAAW,MAAM;CAC5D,MAAM,MAAM,IAAI,IACd,QAAQ,cAAc,iBAAiB,KAAK,MAAM,EAAE,GAAG,OACxD;AACD,KAAI,aAAa,IAAI,OAAO,MAAM;CAClC,MAAM,KAAK,aAAa,IAAI,UAAU,CAAC,MAAM,QAAQ;AACrD,QAAO,GAAG,OAAO,GAAG,eAAe,iBAAiB,MAAM,MAAM,OAAO,UAAU,MAAM,GAAG,GAAG"}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { LOG_LEVELS, REFRESH_TOKEN_DIVIDER, logWithLevel, maybeRedact } from "./utils.js";
|
|
2
|
-
import { authDb } from "./db.js";
|
|
3
|
-
import { Cv } from "@robelest/fx/convex";
|
|
4
|
-
import { Fx } from "@robelest/fx";
|
|
5
|
-
|
|
6
|
-
//#region src/server/refresh.ts
|
|
7
|
-
const DEFAULT_SESSION_INACTIVE_DURATION_MS = 1e3 * 60 * 60 * 24 * 30;
|
|
8
|
-
/** @internal */
|
|
9
|
-
const REFRESH_TOKEN_REUSE_WINDOW_MS = 10 * 1e3;
|
|
10
|
-
/**
|
|
11
|
-
* Create a new refresh token for the given session.
|
|
12
|
-
*/
|
|
13
|
-
/** @internal */
|
|
14
|
-
async function createRefreshToken(ctx, config, sessionId, parentRefreshTokenId) {
|
|
15
|
-
const expirationTime = Date.now() + (config.session?.inactiveDurationMs ?? (process.env.AUTH_SESSION_INACTIVE_DURATION_MS !== void 0 ? Number(process.env.AUTH_SESSION_INACTIVE_DURATION_MS) : void 0) ?? DEFAULT_SESSION_INACTIVE_DURATION_MS);
|
|
16
|
-
return authDb(ctx, config).refreshTokens.create({
|
|
17
|
-
sessionId,
|
|
18
|
-
expirationTime,
|
|
19
|
-
parentRefreshTokenId: parentRefreshTokenId ?? void 0
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Parse a compound refresh token string into its constituent IDs.
|
|
24
|
-
*/
|
|
25
|
-
/** @internal */
|
|
26
|
-
const parseRefreshToken = (refreshToken) => {
|
|
27
|
-
const [refreshTokenId, sessionId] = refreshToken.split(REFRESH_TOKEN_DIVIDER);
|
|
28
|
-
const msg = `Can't parse refresh token: ${maybeRedact(refreshToken)}`;
|
|
29
|
-
return (refreshTokenId != null ? Fx.succeed(refreshTokenId) : Cv.fail({
|
|
30
|
-
code: "INVALID_REFRESH_TOKEN",
|
|
31
|
-
message: msg
|
|
32
|
-
})).pipe(Fx.chain((rtId) => {
|
|
33
|
-
return (sessionId != null ? Fx.succeed(sessionId) : Cv.fail({
|
|
34
|
-
code: "INVALID_REFRESH_TOKEN",
|
|
35
|
-
message: msg
|
|
36
|
-
})).pipe(Fx.map((sId) => ({
|
|
37
|
-
refreshTokenId: rtId,
|
|
38
|
-
sessionId: sId
|
|
39
|
-
})));
|
|
40
|
-
}));
|
|
41
|
-
};
|
|
42
|
-
/**
|
|
43
|
-
* Mark all refresh tokens descending from the given refresh token as invalid
|
|
44
|
-
* immediately. Used when we detect token reuse — revoke the entire tree.
|
|
45
|
-
*/
|
|
46
|
-
/** @internal */
|
|
47
|
-
async function invalidateRefreshTokensInSubtree(ctx, refreshToken, config) {
|
|
48
|
-
const db = authDb(ctx, config);
|
|
49
|
-
const tokensToInvalidate = [refreshToken];
|
|
50
|
-
const visited = new Set([refreshToken._id]);
|
|
51
|
-
let frontier = [refreshToken._id];
|
|
52
|
-
while (frontier.length > 0) {
|
|
53
|
-
const nextFrontier = [];
|
|
54
|
-
for (const currentTokenId of frontier) {
|
|
55
|
-
const children = await db.refreshTokens.getChildren(refreshToken.sessionId, currentTokenId);
|
|
56
|
-
for (const child of children) {
|
|
57
|
-
if (visited.has(child._id)) continue;
|
|
58
|
-
visited.add(child._id);
|
|
59
|
-
tokensToInvalidate.push(child);
|
|
60
|
-
nextFrontier.push(child._id);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
frontier = nextFrontier;
|
|
64
|
-
}
|
|
65
|
-
await Fx.run(Fx.each(tokensToInvalidate, (token) => token.firstUsedTime === void 0 || token.firstUsedTime > Date.now() - REFRESH_TOKEN_REUSE_WINDOW_MS ? Fx.promise(() => db.refreshTokens.patch(token._id, { firstUsedTime: Date.now() - REFRESH_TOKEN_REUSE_WINDOW_MS })) : Fx.unit));
|
|
66
|
-
return tokensToInvalidate;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Validate a refresh token and its associated session.
|
|
70
|
-
*
|
|
71
|
-
* Returns `null` on any validation failure (matching original semantics).
|
|
72
|
-
* Each validation step is a small composable function chained with `Fx.chain`.
|
|
73
|
-
* On failure, the error message is logged and the pipeline folds to `null`.
|
|
74
|
-
*/
|
|
75
|
-
/** @internal */
|
|
76
|
-
const refreshTokenIfValid = (ctx, refreshTokenId, tokenSessionId, config) => {
|
|
77
|
-
const db = authDb(ctx, config);
|
|
78
|
-
const fetchDoc = (promise, failMsg) => Fx.from({
|
|
79
|
-
ok: promise,
|
|
80
|
-
err: () => failMsg
|
|
81
|
-
}).pipe(Fx.recover((msg) => {
|
|
82
|
-
logWithLevel(LOG_LEVELS.ERROR, msg);
|
|
83
|
-
return Fx.succeed(null);
|
|
84
|
-
}));
|
|
85
|
-
return fetchDoc(() => db.refreshTokens.getById(refreshTokenId), "Invalid refresh token format").pipe(Fx.chain((doc) => doc !== null ? Fx.succeed(doc) : Fx.fail("Invalid refresh token")), Fx.chain((doc) => doc.expirationTime >= Date.now() ? Fx.succeed(doc) : Fx.fail("Expired refresh token")), Fx.chain((doc) => doc.sessionId === tokenSessionId ? Fx.succeed(doc) : Fx.fail("Invalid refresh token session ID"))).pipe(Fx.chain((doc) => fetchDoc(() => db.sessions.getById(doc.sessionId), "Invalid refresh token session format").pipe(Fx.chain((session) => session !== null ? Fx.succeed(session) : Fx.fail("Invalid refresh token session")), Fx.chain((session) => session.expirationTime >= Date.now() ? Fx.succeed(session) : Fx.fail("Expired refresh token session")), Fx.map((session) => ({
|
|
86
|
-
session,
|
|
87
|
-
refreshTokenDoc: doc
|
|
88
|
-
})))), Fx.fold({
|
|
89
|
-
ok: (result) => result,
|
|
90
|
-
err: (msg) => {
|
|
91
|
-
logWithLevel(LOG_LEVELS.ERROR, msg);
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
}));
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
//#endregion
|
|
98
|
-
export { REFRESH_TOKEN_REUSE_WINDOW_MS, createRefreshToken, invalidateRefreshTokensInSubtree, parseRefreshToken, refreshTokenIfValid };
|
|
99
|
-
//# sourceMappingURL=refresh.js.map
|