@estifanos-sh/convex-auth 0.0.2 → 0.0.3
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 +5 -5
- package/dist/_generated/component.d.ts +130 -61
- package/dist/_generated/component.d.ts.map +1 -1
- package/dist/_generated/dataModel.d.ts +28 -0
- package/dist/_generated/dataModel.d.ts.map +1 -0
- package/dist/bin.js +1 -2
- package/dist/browser/index.d.ts +2 -2
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js.map +1 -1
- package/dist/client/core/types.d.ts +40 -79
- package/dist/client/core/types.d.ts.map +1 -1
- package/dist/client/core/types.js.map +1 -1
- package/dist/client/errors.d.ts +15 -0
- package/dist/client/errors.d.ts.map +1 -0
- package/dist/client/errors.js +8 -1
- package/dist/client/errors.js.map +1 -1
- package/dist/client/index.d.ts +6 -3
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +3 -2
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/component.d.ts +130 -61
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/_generated/dataModel.d.ts +1 -1
- package/dist/component/_generated/server.d.ts +1 -1
- package/dist/component/account.d.ts +43 -37
- package/dist/component/account.d.ts.map +1 -1
- package/dist/component/account.js +14 -16
- package/dist/component/account.js.map +1 -1
- package/dist/component/connection/audit.d.ts +29 -29
- package/dist/component/connection/audit.d.ts.map +1 -1
- package/dist/component/connection/audit.js +2 -1
- package/dist/component/connection/audit.js.map +1 -1
- package/dist/component/connection/cache.d.ts +6 -6
- package/dist/component/connection/domain/verification.d.ts +19 -19
- package/dist/component/connection/domain.d.ts +20 -20
- package/dist/component/connection/saml/assertion.d.ts +4 -4
- package/dist/component/connection/saml/request.d.ts +9 -9
- package/dist/component/connection/saml/request.d.ts.map +1 -1
- package/dist/component/connection/scim/config.d.ts +13 -13
- package/dist/component/connection/scim/identity.d.ts +102 -59
- package/dist/component/connection/scim/identity.d.ts.map +1 -1
- package/dist/component/connection/scim/identity.js +357 -56
- package/dist/component/connection/scim/identity.js.map +1 -1
- package/dist/component/connection/secret.d.ts +15 -15
- package/dist/component/connection/secret.d.ts.map +1 -1
- package/dist/component/connection/webhook/delivery.d.ts +69 -53
- package/dist/component/connection/webhook/delivery.d.ts.map +1 -1
- package/dist/component/connection/webhook/delivery.js +127 -170
- package/dist/component/connection/webhook/delivery.js.map +1 -1
- package/dist/component/connection/webhook/endpoint.d.ts +21 -21
- package/dist/component/connection/webhook/events.d.ts +26 -0
- package/dist/component/connection/webhook/events.d.ts.map +1 -0
- package/dist/component/connection/webhook/events.js +39 -0
- package/dist/component/connection/webhook/events.js.map +1 -0
- package/dist/component/connection.d.ts +38 -38
- package/dist/component/connection.d.ts.map +1 -1
- package/dist/component/context.d.ts +115 -0
- package/dist/component/context.d.ts.map +1 -0
- package/dist/component/context.js +50 -0
- package/dist/component/context.js.map +1 -0
- package/dist/component/convex.config.d.ts +2 -2
- package/dist/component/crons.d.ts +2 -2
- package/dist/component/event.d.ts +134 -138
- package/dist/component/event.d.ts.map +1 -1
- package/dist/component/event.js +88 -117
- package/dist/component/event.js.map +1 -1
- package/dist/component/eventstream.d.ts +30 -0
- package/dist/component/eventstream.d.ts.map +1 -0
- package/dist/component/eventstream.js +51 -0
- package/dist/component/eventstream.js.map +1 -0
- package/dist/component/factor/device.d.ts +25 -24
- package/dist/component/factor/device.d.ts.map +1 -1
- package/dist/component/factor/device.js +5 -2
- package/dist/component/factor/device.js.map +1 -1
- package/dist/component/factor/passkey.d.ts +84 -75
- package/dist/component/factor/passkey.d.ts.map +1 -1
- package/dist/component/factor/passkey.js +6 -6
- package/dist/component/factor/passkey.js.map +1 -1
- package/dist/component/factor/totp.d.ts +54 -49
- package/dist/component/factor/totp.d.ts.map +1 -1
- package/dist/component/factor/totp.js +3 -3
- package/dist/component/factor/totp.js.map +1 -1
- package/dist/component/group/active.d.ts +326 -22
- package/dist/component/group/active.d.ts.map +1 -1
- package/dist/component/group/active.js +21 -11
- package/dist/component/group/active.js.map +1 -1
- package/dist/component/group/invite.d.ts +28 -28
- package/dist/component/group/invite.d.ts.map +1 -1
- package/dist/component/group/member.d.ts +34 -34
- package/dist/component/group.d.ts +47 -62
- package/dist/component/group.d.ts.map +1 -1
- package/dist/component/http.d.ts +2 -2
- package/dist/component/limits.d.ts +4 -4
- package/dist/component/maintenance.d.ts +2 -2
- package/dist/component/model.d.ts +1362 -1027
- package/dist/component/model.d.ts.map +1 -1
- package/dist/component/model.js +23 -6
- package/dist/component/model.js.map +1 -1
- package/dist/component/modules.d.ts +8 -2
- package/dist/component/modules.d.ts.map +1 -1
- package/dist/component/modules.js +3 -0
- package/dist/component/modules.js.map +1 -1
- package/dist/component/oauth/client.d.ts +20 -20
- package/dist/component/oauth/code.d.ts +14 -14
- package/dist/component/oauth/refresh.d.ts +19 -19
- package/dist/component/schema.d.ts +1100 -611
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +17 -4
- package/dist/component/schema.js.map +1 -1
- package/dist/component/session.d.ts +72 -32
- package/dist/component/session.d.ts.map +1 -1
- package/dist/component/session.js +120 -32
- package/dist/component/session.js.map +1 -1
- package/dist/component/token/continuation.d.ts +40 -18
- package/dist/component/token/continuation.d.ts.map +1 -1
- package/dist/component/token/continuation.js +57 -11
- package/dist/component/token/continuation.js.map +1 -1
- package/dist/component/token/pkce.d.ts +22 -22
- package/dist/component/token/pkce.d.ts.map +1 -1
- package/dist/component/token/refresh.d.ts +39 -35
- package/dist/component/token/refresh.d.ts.map +1 -1
- package/dist/component/token/refresh.js +11 -6
- package/dist/component/token/refresh.js.map +1 -1
- package/dist/component/token/verification.d.ts +36 -32
- package/dist/component/token/verification.d.ts.map +1 -1
- package/dist/component/token/verification.js +3 -3
- package/dist/component/token/verification.js.map +1 -1
- package/dist/component/user/email.d.ts +21 -21
- package/dist/component/user/key.d.ts +35 -35
- package/dist/component/user/key.d.ts.map +1 -1
- package/dist/component/user.d.ts +51 -45
- package/dist/component/user.d.ts.map +1 -1
- package/dist/core/index.d.ts +54 -90
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -4
- package/dist/core/index.js.map +1 -1
- package/dist/expo/index.d.ts +3 -3
- package/dist/expo/index.d.ts.map +1 -1
- package/dist/expo/index.js.map +1 -1
- package/dist/model.js.map +1 -1
- package/dist/providers/anonymous.d.ts +11 -5
- package/dist/providers/anonymous.d.ts.map +1 -1
- package/dist/providers/anonymous.js +4 -1
- package/dist/providers/anonymous.js.map +1 -1
- package/dist/providers/apple.d.ts +1 -1
- package/dist/providers/credentials.d.ts +21 -13
- package/dist/providers/credentials.d.ts.map +1 -1
- package/dist/providers/credentials.js +2 -0
- package/dist/providers/credentials.js.map +1 -1
- package/dist/providers/custom.d.ts +3 -3
- package/dist/providers/custom.d.ts.map +1 -1
- package/dist/providers/custom.js.map +1 -1
- package/dist/providers/email.d.ts +3 -3
- package/dist/providers/email.d.ts.map +1 -1
- package/dist/providers/email.js.map +1 -1
- package/dist/providers/github.d.ts +1 -1
- package/dist/providers/google.d.ts +1 -1
- package/dist/providers/microsoft.d.ts +1 -1
- package/dist/providers/password.d.ts +103 -8
- package/dist/providers/password.d.ts.map +1 -1
- package/dist/providers/password.js +71 -26
- package/dist/providers/password.js.map +1 -1
- package/dist/providers/phone.d.ts +4 -3
- package/dist/providers/phone.d.ts.map +1 -1
- package/dist/providers/phone.js.map +1 -1
- package/dist/react/index.d.ts +40 -41
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +27 -52
- package/dist/react/index.js.map +1 -1
- package/dist/server/auth.d.ts +157 -117
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +41 -22
- package/dist/server/auth.js.map +1 -1
- package/dist/server/component/factor/db.js +4 -4
- package/dist/server/component/factor/db.js.map +1 -1
- package/dist/server/config.d.ts +2 -1
- package/dist/server/connection/domain.d.ts +4 -66
- package/dist/server/connection/domain.d.ts.map +1 -1
- package/dist/server/connection/domain.js +22 -4
- package/dist/server/connection/domain.js.map +1 -1
- package/dist/server/connection/group/service.js +6 -4
- package/dist/server/connection/group/service.js.map +1 -1
- package/dist/server/connection/http.js +94 -261
- package/dist/server/connection/http.js.map +1 -1
- package/dist/server/connection/oidc.js +8 -17
- package/dist/server/connection/oidc.js.map +1 -1
- package/dist/server/connection/policy/domain.js +4 -1
- package/dist/server/connection/policy/domain.js.map +1 -1
- package/dist/server/connection/policy.js +14 -13
- package/dist/server/connection/policy.js.map +1 -1
- package/dist/server/connection/provision.js +5 -19
- package/dist/server/connection/provision.js.map +1 -1
- package/dist/server/connection/webhook.js +4 -36
- package/dist/server/connection/webhook.js.map +1 -1
- package/dist/server/context.d.ts +2 -2
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/context.js +35 -24
- package/dist/server/context.js.map +1 -1
- package/dist/server/contract.d.ts.map +1 -1
- package/dist/server/contract.js +6 -14
- package/dist/server/contract.js.map +1 -1
- package/dist/server/core.js +6 -0
- package/dist/server/core.js.map +1 -1
- package/dist/server/cors.d.ts +2 -2
- package/dist/server/cors.d.ts.map +1 -1
- package/dist/server/crypto.js.map +1 -1
- package/dist/server/db.js +2 -1
- package/dist/server/db.js.map +1 -1
- package/dist/server/domains/context.js +34 -0
- package/dist/server/domains/context.js.map +1 -0
- package/dist/server/domains/factor.d.ts +3 -2
- package/dist/server/domains/factor.d.ts.map +1 -1
- package/dist/server/domains/factor.js.map +1 -1
- package/dist/server/domains/group.js.map +1 -1
- package/dist/server/domains/invite.js.map +1 -1
- package/dist/server/domains/key.js.map +1 -1
- package/dist/server/domains/member.js.map +1 -1
- package/dist/server/domains/session.js.map +1 -1
- package/dist/server/domains/user.js.map +1 -1
- package/dist/server/env.js +2 -22
- package/dist/server/env.js.map +1 -1
- package/dist/server/events.d.ts +0 -1
- package/dist/server/events.d.ts.map +1 -1
- package/dist/server/events.js +6 -6
- package/dist/server/events.js.map +1 -1
- package/dist/server/http.d.ts +1 -0
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +4 -4
- package/dist/server/http.js.map +1 -1
- package/dist/server/identity/claims.js +1 -1
- package/dist/server/identity/convex.d.ts +2 -0
- package/dist/server/identity/convex.d.ts.map +1 -1
- package/dist/server/index.d.ts +2 -3
- package/dist/server/index.js +1 -2
- package/dist/server/limits.js +21 -1
- package/dist/server/limits.js.map +1 -1
- package/dist/server/mutations/credentials/signin.js +6 -3
- package/dist/server/mutations/credentials/signin.js.map +1 -1
- package/dist/server/mutations/invalidate.js +5 -10
- package/dist/server/mutations/invalidate.js.map +1 -1
- package/dist/server/mutations/oauth.js +1 -1
- package/dist/server/mutations/oauth.js.map +1 -1
- package/dist/server/mutations/retrieve.js +4 -1
- package/dist/server/mutations/retrieve.js.map +1 -1
- package/dist/server/mutations/signin.js +2 -2
- package/dist/server/mutations/signin.js.map +1 -1
- package/dist/server/mutations/verify.js +4 -3
- package/dist/server/mutations/verify.js.map +1 -1
- package/dist/server/oauth/factory.js.map +1 -1
- package/dist/server/runtime.d.ts +107 -127
- package/dist/server/runtime.d.ts.map +1 -1
- package/dist/server/runtime.js +43 -28
- package/dist/server/runtime.js.map +1 -1
- package/dist/server/session/lifecycle.d.ts.map +1 -1
- package/dist/server/session/lifecycle.js +18 -3
- package/dist/server/session/lifecycle.js.map +1 -1
- package/dist/server/signin/flow.js +1 -1
- package/dist/server/signin/flow.js.map +1 -1
- package/dist/server/tokens.js +1 -0
- package/dist/server/tokens.js.map +1 -1
- package/dist/server/totp.js +3 -3
- package/dist/server/totp.js.map +1 -1
- package/dist/server/types.d.ts +63 -43
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/user/account.js +0 -1
- package/dist/server/user/account.js.map +1 -1
- package/dist/server/validators.d.ts +874 -917
- package/dist/server/validators.d.ts.map +1 -1
- package/dist/server/validators.js +33 -32
- package/dist/server/validators.js.map +1 -1
- package/dist/server/webauthn.js +4 -4
- package/dist/server/webauthn.js.map +1 -1
- package/dist/shared/brand.d.ts +3 -7
- package/dist/shared/brand.d.ts.map +1 -1
- package/dist/shared/params.d.ts +78 -0
- package/dist/shared/params.d.ts.map +1 -0
- package/dist/shared/results.d.ts +7 -1
- package/dist/shared/results.d.ts.map +1 -1
- package/dist/svelte/context.svelte.d.ts +23 -12
- package/dist/svelte/context.svelte.d.ts.map +1 -1
- package/dist/svelte/context.svelte.js +20 -13
- package/dist/svelte/index.d.ts +1 -4
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +1 -4
- package/dist/test.d.ts +1105 -611
- package/dist/test.d.ts.map +1 -1
- package/package.json +2 -1
- package/dist/server/env.d.ts +0 -28
- package/dist/server/env.d.ts.map +0 -1
- package/dist/svelte/AuthLoading.svelte +0 -13
- package/dist/svelte/AuthLoading.svelte.d.ts +0 -8
- package/dist/svelte/AuthLoading.svelte.d.ts.map +0 -1
- package/dist/svelte/SignedIn.svelte +0 -14
- package/dist/svelte/SignedIn.svelte.d.ts +0 -8
- package/dist/svelte/SignedIn.svelte.d.ts.map +0 -1
- package/dist/svelte/SignedOut.svelte +0 -13
- package/dist/svelte/SignedOut.svelte.d.ts +0 -8
- package/dist/svelte/SignedOut.svelte.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webauthn.js","names":[],"sources":["../../src/server/webauthn.ts"],"sourcesContent":["/**\n * Server-side WebAuthn ceremony logic.\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 * @module\n */\n\nimport {\n decodePKIXECDSASignature,\n decodeSEC1PublicKey,\n p256,\n verifyECDSASignature,\n} from \"@oslojs/crypto/ecdsa\";\nimport {\n decodePKCS1RSAPublicKey,\n RSAPublicKey,\n sha256ObjectIdentifier,\n verifyRSASSAPKCS1v15Signature,\n} from \"@oslojs/crypto/rsa\";\nimport { decodeCBORToNativeValueNoLeftoverBytes } from \"@oslojs/cbor\";\nimport { sha256 } from \"@oslojs/crypto/sha2\";\nimport { decodeBase64urlIgnorePadding, encodeBase64urlNoPadding } from \"@oslojs/encoding\";\nimport {\n ClientDataType,\n coseAlgorithmES256,\n coseAlgorithmRS256,\n COSEKeyType,\n createAssertionSignatureMessage,\n parseAttestationObject,\n parseAuthenticatorData,\n parseClientDataJSON,\n} from \"@oslojs/webauthn\";\n\nimport type {\n AuthTokens,\n SignInWebAuthnOptionsResult,\n SignInSessionResult,\n} from \"../shared/results\";\nimport { ConvexError, type GenericId } from \"convex/values\";\n\nimport { ErrorCode } from \"../shared/codes\";\nimport {\n MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH,\n MAX_WEBAUTHN_CREDENTIALS_PER_USER,\n} from \"../shared/webauthn\";\nimport { authFlowError } from \"../shared/errors\";\nimport { requireAuthKey } from \"./env\";\nimport type { AuthErrorData } from \"./errors\";\nimport { toConvexError } from \"./errors\";\nimport { queueAuthEvent } from \"./events\";\nimport { getAuthenticatedUserIdOrNull } from \"./identity/claims\";\nimport { LOG_LEVELS, log } from \"./log\";\nimport {\n consumeVerifierById,\n mutatePasskeyBeginRotation,\n mutatePasskeyBeginAssertion,\n mutatePasskeyBeginRegistration,\n mutatePasskeyBeginSignIn,\n mutatePasskeyCompleteAssertion,\n mutatePasskeyCompleteRegistration,\n mutatePasskeyCompleteRotation,\n queryContinuation,\n} from \"./component/factor/db\";\nimport {\n buildSessionIdentity,\n finalizeSessionIssuance,\n getAuthSessionId,\n sessionExpirationTime,\n} from \"./session/lifecycle\";\nimport { encodeRefreshToken, refreshTokenExpirationTime } from \"./token/refresh\";\nimport { buildKnownSignInIdentityAttributes } from \"./telemetry\";\nimport {\n AuthDataModel,\n GenericActionCtxWithAuthConfig,\n WebAuthnAttestationEvidence,\n WebAuthnAttestationPolicy,\n WebAuthnProviderConfig,\n SessionInfo,\n} from \"./types\";\nimport { appUrlFromEnv } from \"./url\";\nimport { setActiveSpanAttributes } from \"./utils/span\";\n\ntype EnrichedActionCtx = GenericActionCtxWithAuthConfig<AuthDataModel>;\n\ninterface RpOptions {\n rpName: string;\n rpId: string;\n origin: string | string[];\n challengeExpirationMs: number;\n registration: WebAuthnProviderConfig[\"options\"][\"registration\"];\n authentication: WebAuthnProviderConfig[\"options\"][\"authentication\"];\n}\n\ntype WebAuthnResult =\n | SignInSessionResult<SessionInfo<AuthTokens | null> | null>\n | SignInWebAuthnOptionsResult;\n\ntype PasskeyRegistrationData = Omit<\n Parameters<typeof mutatePasskeyCompleteRegistration>[1],\n \"replaceSessionId\"\n>;\n\ntype PasskeySessionCompletion = Pick<\n Awaited<ReturnType<typeof mutatePasskeyCompleteRegistration>>,\n \"user\" | \"sessionId\" | \"refreshTokenId\" | \"replacedSessionId\"\n>;\n\n/**\n * The WebAuthn provider has three single-word flows:\n *\n * - `register` — issue a WebAuthn registration challenge (phase 1).\n * - `signIn` — issue a WebAuthn authentication challenge (phase 1).\n * - `verify` — consume the WebAuthn response (phase 2). The server\n * auto-detects whether this completes a registration\n * (`attestationObject` present) or a sign-in\n * (`signature` + `credentialId` present).\n */\nconst WEBAUTHN_FLOWS = [\"register\", \"signIn\", \"verify\"] as const;\ntype WebAuthnFlow = (typeof WEBAUTHN_FLOWS)[number];\ntype WebAuthnDispatch = { flow: WebAuthnFlow };\n\nfunction isWebAuthnFlow(value: string): value is WebAuthnFlow {\n return WEBAUTHN_FLOWS.some((flow) => flow === value);\n}\n\ntype PasskeyParams = {\n flow?: string;\n userName?: string;\n userDisplayName?: string;\n email?: string;\n clientDataJSON?: string;\n attestationObject?: string;\n transports?: string[];\n passkeyName?: string;\n credentialId?: string;\n authenticatorData?: string;\n signature?: string;\n};\n\ntype PasskeyParamCandidate = {\n flow?: unknown;\n userName?: unknown;\n userDisplayName?: unknown;\n email?: unknown;\n clientDataJSON?: unknown;\n attestationObject?: unknown;\n transports?: unknown;\n passkeyName?: unknown;\n credentialId?: unknown;\n authenticatorData?: unknown;\n signature?: unknown;\n};\n\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((entry) => typeof entry === \"string\");\n}\n\nfunction parsePasskeyParams(value: unknown): PasskeyParams {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return {};\n const input = value as PasskeyParamCandidate;\n const string = (key: keyof PasskeyParams) => {\n const candidate = input[key];\n return typeof candidate === \"string\" ? candidate : undefined;\n };\n const transports = isStringArray(input.transports) ? input.transports : undefined;\n return {\n flow: string(\"flow\"),\n userName: string(\"userName\"),\n userDisplayName: string(\"userDisplayName\"),\n email: string(\"email\"),\n clientDataJSON: string(\"clientDataJSON\"),\n attestationObject: string(\"attestationObject\"),\n transports,\n passkeyName: string(\"passkeyName\"),\n credentialId: string(\"credentialId\"),\n authenticatorData: string(\"authenticatorData\"),\n signature: string(\"signature\"),\n };\n}\n\nconst requireStringParam = (value: unknown, name: string) => {\n if (typeof value !== \"string\") {\n throw convexError(ErrorCode.INVALID_PARAMETERS, `Missing \\`${name}\\` parameter.`);\n }\n return value;\n};\n\nconst convexError = (code: ErrorCode, message: string) =>\n toConvexError(authFlowError(code, message));\n\nconst MAX_ENCODED_WEBAUTHN_CREDENTIAL_ID_LENGTH = Math.ceil(\n (MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH * 4) / 3,\n);\n\n/** @internal */\nexport function validateCredentialId(value: unknown): string {\n const credentialId = requireStringParam(value, \"credentialId\");\n if (\n credentialId.length === 0 ||\n credentialId.length > MAX_ENCODED_WEBAUTHN_CREDENTIAL_ID_LENGTH\n ) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n `Credential ID must be between 1 and ${MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH} bytes.`,\n );\n }\n if (!/^[A-Za-z0-9_-]+$/.test(credentialId)) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n \"Credential ID must be valid unpadded base64url.\",\n );\n }\n\n let decoded: Uint8Array;\n try {\n decoded = decodeBase64urlIgnorePadding(credentialId);\n } catch {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n \"Credential ID must be valid unpadded base64url.\",\n );\n }\n if (decoded.byteLength < 1 || decoded.byteLength > MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n `Credential ID must be between 1 and ${MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH} bytes.`,\n );\n }\n return credentialId;\n}\n\nconst asConvexError = (\n error: unknown,\n code: ErrorCode,\n message: string,\n): ConvexError<AuthErrorData> =>\n error instanceof ConvexError\n ? error\n : error instanceof Error\n ? toConvexError(authFlowError(code, error.message || message))\n : convexError(code, message);\n\nconst logPasskeyError = (err: unknown) =>\n log(LOG_LEVELS.ERROR, \"passkey error:\", err instanceof Error ? err.message : String(err));\n\nfunction resolveRpOptions(provider: WebAuthnProviderConfig): RpOptions {\n try {\n let appUrl: string | undefined;\n if (provider.options.rpId === undefined || provider.options.origin === undefined) {\n try {\n appUrl = appUrlFromEnv();\n } catch {\n throw convexError(\n ErrorCode.PASSKEY_MISSING_CONFIG,\n \"Passkey provider requires APP_URL env var (your frontend URL) or explicit rpId and origin in the provider config. CONVEX_SITE_URL cannot be used because WebAuthn RP ID must match the frontend domain.\",\n );\n }\n }\n\n const appHostname = appUrl ? new URL(appUrl).hostname : undefined;\n const registration: RpOptions[\"registration\"] = {\n userVerification: provider.options.registration.userVerification ?? \"required\",\n residentKey: provider.options.registration.residentKey ?? \"preferred\",\n algorithms: provider.options.registration.algorithms ?? [\n coseAlgorithmES256,\n coseAlgorithmRS256,\n ],\n };\n const authentication: RpOptions[\"authentication\"] = {\n userVerification: provider.options.authentication.userVerification ?? \"required\",\n };\n if (provider.options.registration.authenticatorAttachment !== undefined) {\n registration.authenticatorAttachment = provider.options.registration.authenticatorAttachment;\n }\n if (provider.options.registration.hints !== undefined) {\n registration.hints = provider.options.registration.hints;\n }\n if (provider.options.registration.attestation !== undefined) {\n registration.attestation = provider.options.registration.attestation;\n }\n if (provider.options.authentication.hints !== undefined) {\n authentication.hints = provider.options.authentication.hints;\n }\n return {\n rpName: provider.options.rpName ?? appHostname ?? provider.options.rpId ?? \"localhost\",\n rpId: provider.options.rpId ?? appHostname ?? \"localhost\",\n origin: provider.options.origin ?? appUrl ?? \"http://localhost\",\n challengeExpirationMs: provider.options.challengeExpirationMs ?? 300_000,\n registration,\n authentication,\n };\n } catch (error) {\n throw asConvexError(\n error,\n ErrorCode.PASSKEY_MISSING_CONFIG,\n \"Passkey relying party configuration is invalid.\",\n );\n }\n}\n\nfunction verifySameOrigin<T extends { crossOrigin: boolean | null }>(clientData: T): T {\n if (clientData.crossOrigin === true) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n \"Cross-origin WebAuthn ceremonies are not supported.\",\n );\n }\n return clientData;\n}\n\nfunction verifyClientDataType<T extends { type: ClientDataType }>(\n clientData: T,\n expectedType: ClientDataType,\n label: string,\n): T {\n if (clientData.type !== expectedType) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n `Invalid client data type: expected ${label}`,\n );\n }\n return clientData;\n}\n\nfunction verifyOrigin<T extends { origin: string }>(clientData: T, rp: RpOptions): T {\n const allowed = Array.isArray(rp.origin) ? rp.origin : [rp.origin];\n if (!allowed.includes(clientData.origin)) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_ORIGIN,\n `Invalid origin: ${clientData.origin}, expected one of: ${allowed.join(\", \")}`,\n );\n }\n return clientData;\n}\n\nasync function verifyAndConsumeChallenge<T extends { challenge: Uint8Array }>(\n clientData: T,\n ctx: EnrichedActionCtx,\n verifierValue: string,\n) {\n const challengeHash = encodeBase64urlNoPadding(new Uint8Array(sha256(clientData.challenge)));\n // Read-check-delete the challenge verifier in ONE component mutation: this runs\n // in an action, so a separate `get` then `remove` let two concurrent\n // assertions carrying the same challenge both pass before either delete landed,\n // each minting its own session. `consume` matches the expected challenge hash\n // and deletes atomically, so only the single winner gets the doc back.\n let doc;\n try {\n doc = await consumeVerifierById(ctx, verifierValue, challengeHash);\n } catch (err) {\n console.error(\"[auth] passkey error:\", err);\n throw convexError(ErrorCode.PASSKEY_INVALID_CHALLENGE, \"Invalid or expired passkey challenge.\");\n }\n if (!doc) {\n throw convexError(ErrorCode.PASSKEY_INVALID_CHALLENGE, \"Invalid or expired passkey challenge.\");\n }\n return doc;\n}\n\nfunction verifyRpId<T extends { verifyRelyingPartyIdHash: (id: string) => boolean }>(\n authData: T,\n rpId: string,\n): T {\n if (!authData.verifyRelyingPartyIdHash(rpId)) {\n throw convexError(ErrorCode.PASSKEY_RP_MISMATCH, \"Relying party ID mismatch.\");\n }\n return authData;\n}\n\nfunction verifyUserFlags<T extends { userPresent: boolean; userVerified: boolean }>(\n authData: T,\n userVerification: \"required\" | \"preferred\" | \"discouraged\",\n): T {\n if (!authData.userPresent) {\n throw convexError(ErrorCode.PASSKEY_USER_PRESENCE, \"User presence flag not set.\");\n }\n if (userVerification === \"required\" && !authData.userVerified) {\n throw convexError(\n ErrorCode.PASSKEY_USER_VERIFICATION,\n \"User verification required but not performed.\",\n );\n }\n return authData;\n}\n\ntype BackupState = {\n deviceType: \"singleDevice\" | \"multiDevice\";\n backedUp: boolean;\n};\n\n/** @internal */\nexport function parseBackupState(authenticatorData: Uint8Array): BackupState {\n if (authenticatorData.byteLength < 33) {\n throw convexError(ErrorCode.PASSKEY_INVALID_CLIENT_DATA, \"Authenticator data is too short.\");\n }\n const flags = authenticatorData[32];\n const backupEligible = (flags & 0x08) !== 0;\n const backedUp = (flags & 0x10) !== 0;\n if (backedUp && !backupEligible) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n \"Authenticator backup state is invalid.\",\n );\n }\n return {\n deviceType: backupEligible ? \"multiDevice\" : \"singleDevice\",\n backedUp,\n };\n}\n\nfunction extractAttestationAuthenticatorData(attestationObject: Uint8Array): Uint8Array {\n try {\n const decoded = decodeCBORToNativeValueNoLeftoverBytes(attestationObject, 4);\n if (\n typeof decoded !== \"object\" ||\n decoded === null ||\n !(\"authData\" in decoded) ||\n !(decoded.authData instanceof Uint8Array)\n ) {\n throw new Error(\"Invalid or missing authData\");\n }\n return decoded.authData;\n } catch (error) {\n throw asConvexError(\n error,\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n \"Invalid attestation authenticator data.\",\n );\n }\n}\n\n/** @internal */\nexport function validateCredentialAlgorithm(\n algorithm: number,\n configuredAlgorithms: readonly number[],\n): void {\n if (!configuredAlgorithms.includes(algorithm)) {\n throw convexError(\n ErrorCode.PASSKEY_UNSUPPORTED_ALGORITHM,\n `Credential algorithm ${algorithm} was not offered by the server.`,\n );\n }\n}\n\nfunction resolveWebAuthnDispatch(params: PasskeyParams): WebAuthnDispatch {\n const flow = params.flow;\n if (typeof flow === \"string\" && isWebAuthnFlow(flow)) {\n return { flow };\n }\n throw convexError(\n ErrorCode.PASSKEY_MISSING_FLOW,\n \"Missing `flow` parameter. Expected one of: \" + WEBAUTHN_FLOWS.join(\", \"),\n );\n}\n\nfunction requirePasskeyVerifier(verifier: string | undefined): string {\n if (verifier != null) {\n return verifier;\n }\n throw convexError(ErrorCode.PASSKEY_MISSING_VERIFIER, \"Missing verifier for passkey operation.\");\n}\n\nasync function requireAuthenticatedUserId(ctx: EnrichedActionCtx): Promise<string> {\n try {\n const userId = await getAuthenticatedUserIdOrNull(ctx);\n if (userId === null) {\n throw convexError(\n ErrorCode.PASSKEY_AUTH_REQUIRED,\n \"Sign in first, then add a passkey to your account.\",\n );\n }\n return userId;\n } catch (err) {\n console.error(\"[auth] passkey error:\", err);\n throw convexError(\n ErrorCode.PASSKEY_AUTH_REQUIRED,\n \"Sign in first, then add a passkey to your account.\",\n );\n }\n}\n\nfunction resolveRegistrationPublicKeyBytes(\n publicKey: NonNullable<\n ReturnType<typeof parseAttestationObject>[\"authenticatorData\"][\"credential\"]\n >[\"publicKey\"],\n algorithm: number,\n): Uint8Array {\n if (algorithm === 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 bytes;\n }\n if (algorithm === coseAlgorithmRS256) {\n const rsa = publicKey.rsa();\n const rsaPubKey = new RSAPublicKey(rsa.n, rsa.e);\n return rsaPubKey.encodePKCS1();\n }\n throw convexError(ErrorCode.PASSKEY_UNSUPPORTED_ALGORITHM, `Unsupported algorithm: ${algorithm}`);\n}\n\nfunction verifyAssertionSignature(\n passkey: { algorithm: number; publicKey: ArrayBuffer },\n signature: Uint8Array,\n messageHash: Uint8Array,\n): void {\n try {\n if (passkey.algorithm === coseAlgorithmES256) {\n const ecPublicKey = decodeSEC1PublicKey(p256, new Uint8Array(passkey.publicKey));\n const ecdsaSignature = decodePKIXECDSASignature(signature);\n if (!verifyECDSASignature(ecPublicKey, messageHash, ecdsaSignature)) {\n throw convexError(ErrorCode.PASSKEY_INVALID_SIGNATURE, \"Invalid passkey signature.\");\n }\n return;\n }\n if (passkey.algorithm === coseAlgorithmRS256) {\n const rsaPublicKey = decodePKCS1RSAPublicKey(new Uint8Array(passkey.publicKey));\n if (\n !verifyRSASSAPKCS1v15Signature(rsaPublicKey, sha256ObjectIdentifier, messageHash, signature)\n ) {\n throw convexError(ErrorCode.PASSKEY_INVALID_SIGNATURE, \"Invalid passkey signature.\");\n }\n return;\n }\n throw convexError(\n ErrorCode.PASSKEY_UNSUPPORTED_ALGORITHM,\n `Unsupported algorithm: ${passkey.algorithm}`,\n );\n } catch (error) {\n if (error instanceof ConvexError) throw error;\n throw convexError(ErrorCode.PASSKEY_INVALID_SIGNATURE, \"Invalid passkey signature.\");\n }\n}\n\nconst DUMMY_ES256_PUBLIC_KEY = decodeBase64urlIgnorePadding(\n \"BGsX0fLhLEJH-Lzm5WOkQPJ3A32BLeszoPShOUXYmMKWT-NC4v4af5uO5-tKfA-eFivOM1drMV7Oy7ZAaDe_UfU\",\n);\nconst DUMMY_RS256_PUBLIC_KEY = decodeBase64urlIgnorePadding(\n \"MIIBCgKCAQEAxumpcTeR9zlJNbNZinnjj_eb75YcIeNHf92QdRZ86g_wGzz6WW4VmpRSrkMJC1u3EYdN7rRSw1tveNaw7g6NTfaLjJdyrxkpNdx6niAXq8dMHGe5-Y9MyoGNwP0T_c6PrBdu_M-xGEtpDIG9GwNCM9RHsOxcf7PxVaHBZqR_BoWWLTHrVw6weXBYALsp3iz-jFDl5mG7JW9G_sKVr9dV0BAzeNMAKJEAoEO4swJB4qWGhHaSzCyy8YMngsuQrO9L5L_m8dG0GjYjjkV1G2aoRIl8VR7nnJdGFaAlSgqO_icJN6QY3WS2aalDEr1yRx2mjJPRUIr6rZMCfsuyjq1NZwIDAQAB\",\n);\n\ntype AssertionCredential = { algorithm: number; publicKey: ArrayBuffer };\n\nfunction copyArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;\n}\n\nfunction registrationData(\n userId: string,\n credentialId: string,\n publicKey: Uint8Array,\n algorithm: number,\n counter: number,\n backupState: BackupState,\n params: PasskeyParams,\n attestation: WebAuthnAttestationEvidence | undefined,\n ctx: EnrichedActionCtx,\n): PasskeyRegistrationData {\n return {\n userId,\n credentialId,\n publicKey: copyArrayBuffer(publicKey),\n algorithm,\n counter,\n ...(params.transports === undefined ? {} : { transports: params.transports }),\n deviceType: backupState.deviceType,\n backedUp: backupState.backedUp,\n ...(params.passkeyName === undefined ? {} : { name: params.passkeyName }),\n ...(attestation === undefined ? {} : { attestation }),\n createdAt: Date.now(),\n sessionExpirationTime: sessionExpirationTime(ctx.auth.config),\n refreshTokenExpirationTime: refreshTokenExpirationTime(ctx.auth.config),\n };\n}\n\nasync function finalizePasskeySession(\n ctx: EnrichedActionCtx,\n completed: PasskeySessionCompletion,\n): Promise<SignInSessionResult<SessionInfo<AuthTokens | null> | null>> {\n const userId = completed.user._id as GenericId<\"User\">;\n const sessionId = completed.sessionId as GenericId<\"Session\">;\n setActiveSpanAttributes({\n \"auth.signin.result\": \"success\",\n ...buildKnownSignInIdentityAttributes(\n ctx.auth.config,\n { userId, sessionId },\n completed.user.email,\n ),\n });\n if (completed.replacedSessionId !== undefined) {\n const replacedSessionId = completed.replacedSessionId as GenericId<\"Session\">;\n await queueAuthEvent(ctx, ctx.auth.config, {\n kind: \"session.invalidated\",\n actor: { type: \"system\" },\n subject: { type: \"session\", id: replacedSessionId },\n targets: [\n { kind: \"user\", id: userId },\n { kind: \"session\", id: replacedSessionId },\n ],\n outcome: \"success\",\n data: { userId, reason: \"replaced\" },\n });\n }\n await queueAuthEvent(ctx, ctx.auth.config, {\n kind: \"session.signed_in\",\n actor: { type: \"user\", id: userId },\n subject: { type: \"session\", id: sessionId },\n targets: [\n { kind: \"user\", id: userId },\n { kind: \"session\", id: sessionId },\n ],\n outcome: \"success\",\n data: { provider: \"session\" },\n });\n const session = await finalizeSessionIssuance(ctx.auth.config, {\n userId,\n sessionId,\n identity: buildSessionIdentity(userId, sessionId, completed.user),\n refreshToken: encodeRefreshToken(\n completed.refreshTokenId as GenericId<\"RefreshToken\">,\n sessionId,\n ),\n });\n return { kind: \"signedIn\", session };\n}\n\nfunction signatureIsValid(\n credential: AssertionCredential,\n signature: Uint8Array,\n messageHash: Uint8Array,\n): boolean {\n try {\n verifyAssertionSignature(credential, signature, messageHash);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Perform both supported verification algorithms for real and unknown IDs so\n * the selected algorithm is not a credential-existence timing oracle.\n */\nfunction verifyAssertionSignatureUniformly(\n passkey: AssertionCredential | null,\n signature: Uint8Array,\n messageHash: Uint8Array,\n): asserts passkey is AssertionCredential {\n const es256Valid = signatureIsValid(\n passkey?.algorithm === coseAlgorithmES256\n ? passkey\n : {\n algorithm: coseAlgorithmES256,\n publicKey: copyArrayBuffer(DUMMY_ES256_PUBLIC_KEY),\n },\n signature,\n messageHash,\n );\n const rs256Valid = signatureIsValid(\n passkey?.algorithm === coseAlgorithmRS256\n ? passkey\n : {\n algorithm: coseAlgorithmRS256,\n publicKey: copyArrayBuffer(DUMMY_RS256_PUBLIC_KEY),\n },\n signature,\n messageHash,\n );\n const valid =\n passkey?.algorithm === coseAlgorithmES256\n ? es256Valid\n : passkey?.algorithm === coseAlgorithmRS256\n ? rs256Valid\n : false;\n if (!valid) {\n throw convexError(ErrorCode.PASSKEY_INVALID_SIGNATURE, \"Invalid passkey signature.\");\n }\n}\n\n/** @internal */\nexport function validateBackupEligibility(\n storedDeviceType: string,\n assertionDeviceType: BackupState[\"deviceType\"],\n): void {\n if (storedDeviceType !== assertionDeviceType) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n \"Authenticator backup eligibility changed for an existing credential.\",\n );\n }\n}\n\n/** @internal */\nexport async function assertStoredAttestationTrusted(\n policy: WebAuthnAttestationPolicy,\n evidence: WebAuthnAttestationEvidence | undefined,\n): Promise<void> {\n if (!evidence) {\n throw convexError(\n ErrorCode.PASSKEY_UNTRUSTED_ATTESTATION,\n \"This credential predates the current attestation policy and must be registered again.\",\n );\n }\n try {\n await policy.verifier.assertTrusted(evidence);\n } catch (error) {\n throw asConvexError(\n error,\n ErrorCode.PASSKEY_UNTRUSTED_ATTESTATION,\n \"Authenticator attestation is no longer trusted.\",\n );\n }\n}\n\n/**\n * Build a constant-size, secret-keyed `allowCredentials` list for an email-first\n * sign-in. Real credential IDs are mixed with deterministic decoys, then are\n * ordered by a secret-keyed digest.\n *\n * A known email with passkeys returns a populated `allowCredentials`, so an\n * absent/empty list for every other email would be an immediate\n * account-enumeration oracle. Padding keeps the top-level shape and pair\n * multiplicities equivalent and prevents public prediction of decoy IDs.\n *\n * Credential IDs are authenticator-generated and variable-length. A bounded\n * allow-list cannot make every possible real length statistically identical to\n * an unknown account, so this is defense in depth rather than a claim of\n * identifier privacy. Prefer discoverable credentials and email-less sign-in\n * when account-enumeration resistance is a hard requirement.\n *\n * Decoy IDs reference no stored credential: the ceremony simply fails to produce\n * an assertion (as it would for a real account whose authenticator is absent),\n * and a forged assertion can never pass {@link verifyAssertionSignature} because\n * no matching credential is ever looked up on `verify`.\n */\nconst EMAIL_ALLOW_CREDENTIALS_SIZE = 32;\nconst EMAIL_ALLOW_REAL_CREDENTIALS = EMAIL_ALLOW_CREDENTIALS_SIZE / 2;\nconst MAX_EMAIL_LENGTH = 254;\n\ntype AllowCredential = { type: \"public-key\"; id: string; transports?: string[] };\n\ntype StoredCredential = { id: string; transports?: string[] };\n\nconst ROAMING_AUTHENTICATOR_TRANSPORTS = new Set([\"usb\", \"nfc\", \"ble\"]);\nconst ROAMING_CEREMONY_TRANSPORTS = [\"ble\", \"nfc\", \"usb\"];\n\ntype SecurityKeyCredential = {\n transports?: readonly string[];\n deviceType?: string;\n backedUp?: boolean;\n};\n\nfunction roamingTransports(transports: readonly string[] | undefined): string[] | undefined {\n if (\n !transports?.length ||\n !transports.every((value) => ROAMING_AUTHENTICATOR_TRANSPORTS.has(value))\n ) {\n return undefined;\n }\n return [...new Set(transports)].sort();\n}\n\n/** @internal */\nexport function isSecurityKeyCredential(credential: SecurityKeyCredential): boolean {\n return (\n roamingTransports(credential.transports) !== undefined &&\n (credential.deviceType === undefined || credential.deviceType === \"singleDevice\") &&\n (credential.backedUp === undefined || credential.backedUp === false)\n );\n}\n\n/** @internal */\nexport function selectAuthenticationCredentials<T extends StoredCredential>(\n credentials: readonly T[],\n securityKeysOnly: boolean | undefined,\n): T[] {\n return securityKeysOnly ? credentials.filter(isSecurityKeyCredential) : [...credentials];\n}\n\nfunction requireSecurityKeyCredential(credential: SecurityKeyCredential): void {\n if (!isSecurityKeyCredential(credential)) {\n throw convexError(\n ErrorCode.PASSKEY_SECURITY_KEY_REQUIRED,\n \"A roaming hardware security key is required.\",\n );\n }\n}\n\nfunction credentialAuthenticationHints(\n credentials: readonly StoredCredential[],\n): string[] | undefined {\n return credentials.length > 0 &&\n credentials.every((credential) => roamingTransports(credential.transports) !== undefined)\n ? [\"security-key\"]\n : undefined;\n}\n\nasync function keyedDigest(key: CryptoKey, value: string): Promise<Uint8Array> {\n const encoder = new TextEncoder();\n return new Uint8Array(await crypto.subtle.sign(\"HMAC\", key, encoder.encode(value)));\n}\n\nasync function keyedBytes(key: CryptoKey, domain: string, length: number): Promise<Uint8Array> {\n const blocks = await Promise.all(\n Array.from({ length: Math.ceil(length / 32) }, (_, block) =>\n keyedDigest(key, `${domain}:block:${block}`),\n ),\n );\n const bytes = new Uint8Array(length);\n for (let offset = 0; offset < length; offset += 32) {\n bytes.set(blocks[offset / 32].subarray(0, Math.min(32, length - offset)), offset);\n }\n return bytes;\n}\n\nfunction normalizeEmail(value: unknown): string {\n const email = requireStringParam(value, \"email\").trim().toLowerCase();\n if (email.length === 0 || email.length > MAX_EMAIL_LENGTH) {\n throw convexError(ErrorCode.INVALID_PARAMETERS, \"Email must be between 1 and 254 characters.\");\n }\n return email;\n}\n\nasync function deriveEmailAllowCredentials(\n normalizedEmail: string,\n rpId: string,\n realCredentials: readonly StoredCredential[],\n forceRoamingCeremony = false,\n): Promise<AllowCredential[]> {\n const secret = requireAuthKey(\"webauthnMaskingKey\");\n const emailSeed = encodeBase64urlNoPadding(\n new Uint8Array(sha256(new TextEncoder().encode(normalizedEmail))),\n );\n const realIds = realCredentials\n .flatMap((credential) => {\n try {\n const byteLength = decodeBase64urlIgnorePadding(credential.id).byteLength;\n return byteLength >= 1 && byteLength <= MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH\n ? [\n {\n id: credential.id,\n byteLength,\n transports: roamingTransports(credential.transports),\n },\n ]\n : [];\n } catch {\n return [];\n }\n })\n .slice(0, EMAIL_ALLOW_REAL_CREDENTIALS);\n const ceremonyTransports =\n forceRoamingCeremony || credentialAuthenticationHints(realCredentials)\n ? ROAMING_CEREMONY_TRANSPORTS\n : undefined;\n const key = await crypto.subtle.importKey(\n \"raw\",\n new TextEncoder().encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const makeCredential = async (\n id: string,\n discriminator: string,\n transports?: string[],\n ): Promise<AllowCredential & { order: string }> => ({\n type: \"public-key\",\n id,\n ...(transports === undefined ? {} : { transports }),\n order: encodeBase64urlNoPadding(\n await keyedDigest(\n key,\n `convex-auth:webauthn-order:v2:${rpId}:${emailSeed}:${discriminator}:${id}`,\n ),\n ),\n });\n\n const lengthSeed = await keyedDigest(key, `convex-auth:webauthn-lengths:v2:${rpId}:${emailSeed}`);\n const credentialPairs = await Promise.all(\n Array.from({ length: EMAIL_ALLOW_REAL_CREDENTIALS }, async (_, pairIndex) => {\n const real = realIds[pairIndex];\n const derivedLength =\n 1 +\n (((lengthSeed[pairIndex * 2] << 8) | lengthSeed[pairIndex * 2 + 1]) %\n MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH);\n const byteLength = real?.byteLength ?? derivedLength;\n const pairTransports = ceremonyTransports ?? real?.transports;\n const first = real\n ? await makeCredential(real.id, `real:${pairIndex}`, pairTransports)\n : await keyedBytes(\n key,\n `convex-auth:webauthn-decoy:v2:${rpId}:${emailSeed}:pair:${pairIndex}:first`,\n byteLength,\n ).then((bytes) =>\n makeCredential(\n encodeBase64urlNoPadding(bytes),\n `pair:${pairIndex}:first`,\n pairTransports,\n ),\n );\n const companion = await keyedBytes(\n key,\n `convex-auth:webauthn-decoy:v2:${rpId}:${emailSeed}:pair:${pairIndex}:companion`,\n byteLength,\n ).then((bytes) =>\n makeCredential(\n encodeBase64urlNoPadding(bytes),\n `pair:${pairIndex}:companion`,\n pairTransports,\n ),\n );\n return [first, companion];\n }),\n );\n const credentials = credentialPairs.flat();\n credentials.sort((a, b) => a.order.localeCompare(b.order));\n return credentials.map(({ type, id, transports }) => ({\n type,\n id,\n ...(transports === undefined ? {} : { transports }),\n }));\n}\n\n/**\n * Drive the passkey provider's `register` / `signIn` / `verify` flow.\n *\n * Dispatches on `args.params.flow`; for `verify` it auto-detects whether the\n * response completes a registration or a sign-in (see {@link WEBAUTHN_FLOWS}).\n *\n * @param ctx - Auth-enriched action context.\n * @param provider - The resolved WebAuthn provider config.\n * @param args - The flow params and, for `verify`, the issued challenge verifier.\n * @returns A WebAuthn challenge (`webauthnOptions`) or a signed-in session.\n */\nexport async function handleWebAuthn(\n ctx: EnrichedActionCtx,\n provider: WebAuthnProviderConfig,\n args: {\n params?: unknown;\n verifier?: string;\n continuation?: string;\n },\n): Promise<WebAuthnResult> {\n const params = parsePasskeyParams(args.params);\n const dispatch = resolveWebAuthnDispatch(params);\n\n const handleRegisterVerify = async (): Promise<WebAuthnResult> => {\n const rp = resolveRpOptions(provider);\n const verifier = requirePasskeyVerifier(args.verifier);\n\n const encodedClientDataJSON = requireStringParam(params.clientDataJSON, \"clientDataJSON\");\n const clientDataJSON = decodeBase64urlIgnorePadding(encodedClientDataJSON);\n const clientData = parseClientDataJSON(clientDataJSON);\n verifyClientDataType(clientData, ClientDataType.Create, \"webauthn.create\");\n verifySameOrigin(clientData);\n verifyOrigin(clientData, rp);\n const challenge = await verifyAndConsumeChallenge(clientData, ctx, verifier);\n if (challenge.continuationId !== args.continuation) {\n throw convexError(ErrorCode.CONTINUATION_INVALID, \"Invalid or expired continuation.\");\n }\n let continuationId: string | undefined;\n let userId: string;\n if (challenge.continuationId !== undefined) {\n const continuation = await queryContinuation(ctx, challenge.continuationId);\n if (\n continuation === null ||\n continuation.provider !== provider.id ||\n continuation.operation !== \"rotate\"\n ) {\n throw convexError(ErrorCode.CONTINUATION_INVALID, \"Invalid or expired continuation.\");\n }\n continuationId = continuation._id;\n userId = continuation.userId;\n } else {\n userId = await requireAuthenticatedUserId(ctx);\n }\n\n const encodedAttestationObject = requireStringParam(\n params.attestationObject,\n \"attestationObject\",\n );\n const attestationObjectBytes = decodeBase64urlIgnorePadding(encodedAttestationObject);\n const rawAuthenticatorData = extractAttestationAuthenticatorData(attestationObjectBytes);\n const attestation = parseAttestationObject(attestationObjectBytes);\n const authData = attestation.authenticatorData;\n verifyRpId(authData, rp.rpId);\n verifyUserFlags(authData, rp.registration.userVerification);\n\n if (authData.credential == null) {\n throw convexError(ErrorCode.PASSKEY_NO_CREDENTIAL, \"No credential in attestation.\");\n }\n\n const credential = authData.credential;\n if (\n credential.id.byteLength < 1 ||\n credential.id.byteLength > MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH\n ) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n `Credential ID must be between 1 and ${MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH} bytes.`,\n );\n }\n const credentialId = encodeBase64urlNoPadding(credential.id);\n const publicKey = credential.publicKey;\n const algorithm = publicKey.isAlgorithmDefined()\n ? publicKey.algorithm()\n : publicKey.type() === COSEKeyType.EC2\n ? coseAlgorithmES256\n : publicKey.type() === COSEKeyType.RSA\n ? coseAlgorithmRS256\n : coseAlgorithmES256;\n validateCredentialAlgorithm(algorithm, rp.registration.algorithms);\n const publicKeyBytes = resolveRegistrationPublicKeyBytes(publicKey, algorithm);\n const backupState = parseBackupState(rawAuthenticatorData);\n if (provider.options.securityKeysOnly) {\n requireSecurityKeyCredential({\n transports: params.transports,\n deviceType: backupState.deviceType,\n backedUp: backupState.backedUp,\n });\n }\n let attestationEvidence: WebAuthnAttestationEvidence | undefined;\n if (rp.registration.attestation) {\n const { verifier: attestationVerifier } = rp.registration.attestation;\n try {\n const evidence = await attestationVerifier.verify({\n clientDataJSON: encodedClientDataJSON,\n attestationObject: encodedAttestationObject,\n credentialId,\n transports: params.transports,\n expectedChallenge: encodeBase64urlNoPadding(clientData.challenge),\n expectedOrigin: rp.origin,\n expectedRpId: rp.rpId,\n requireUserVerification: rp.registration.userVerification === \"required\",\n supportedAlgorithms: rp.registration.algorithms,\n });\n attestationEvidence = {\n ...evidence,\n verifier: attestationVerifier.id,\n verifiedAt: Date.now(),\n status: \"trusted\" as const,\n };\n } catch (error) {\n throw asConvexError(\n error,\n ErrorCode.PASSKEY_UNTRUSTED_ATTESTATION,\n \"Authenticator attestation is not trusted.\",\n );\n }\n }\n\n let completed:\n | Awaited<ReturnType<typeof mutatePasskeyCompleteRegistration>>\n | Exclude<Awaited<ReturnType<typeof mutatePasskeyCompleteRotation>>, { status: \"rejected\" }>;\n try {\n const data = registrationData(\n userId,\n credentialId,\n publicKeyBytes,\n algorithm,\n authData.signatureCounter,\n backupState,\n params,\n attestationEvidence,\n ctx,\n );\n if (continuationId === undefined) {\n completed = await mutatePasskeyCompleteRegistration(ctx, {\n ...data,\n replaceSessionId: (await getAuthSessionId(ctx)) ?? undefined,\n });\n } else {\n const rotation = await mutatePasskeyCompleteRotation(ctx, {\n ...data,\n continuationId,\n provider: provider.id,\n });\n if (rotation.status === \"rejected\") {\n throw convexError(ErrorCode.CONTINUATION_INVALID, \"Invalid or expired continuation.\");\n }\n completed = rotation;\n }\n const passkeyId = completed.passkeyId as GenericId<\"Passkey\">;\n if (\"removedPasskeyIds\" in completed) {\n for (const removedPasskeyId of completed.removedPasskeyIds) {\n await queueAuthEvent(ctx, ctx.auth.config, {\n kind: \"passkey.removed\",\n actor: { type: \"user\", id: userId },\n subject: { type: \"passkey\", id: removedPasskeyId },\n targets: [{ kind: \"user\", id: userId }],\n outcome: \"success\",\n data: { passkeyId: removedPasskeyId },\n });\n }\n }\n await queueAuthEvent(ctx, ctx.auth.config, {\n kind: \"passkey.added\",\n actor: { type: \"user\", id: userId },\n subject: { type: \"passkey\", id: passkeyId },\n targets: [{ kind: \"user\", id: userId }],\n outcome: \"success\",\n data: { passkeyId, credentialId },\n });\n if (\"passwordChanged\" in completed && completed.passwordChanged) {\n await queueAuthEvent(ctx, ctx.auth.config, {\n kind: \"password.changed\",\n actor: { type: \"user\", id: userId },\n subject: { type: \"user\", id: userId },\n targets: [{ kind: \"user\", id: userId }],\n outcome: \"success\",\n data: { flow: \"reset\" },\n });\n }\n } catch (err) {\n logPasskeyError(err);\n // Preserve structured failures (e.g. the credentialId-uniqueness guard in\n // `factor.passkey.create` firing when two registrations race the same\n // credential) instead of masking them as a generic internal error.\n if (err instanceof ConvexError) {\n throw err;\n }\n throw convexError(ErrorCode.INTERNAL_ERROR, \"An unexpected error occurred.\");\n }\n\n return await finalizePasskeySession(ctx, completed);\n };\n\n const handleAuthVerify = async (): Promise<WebAuthnResult> => {\n const rp = resolveRpOptions(provider);\n const verifier = requirePasskeyVerifier(args.verifier);\n\n const clientDataJSON = decodeBase64urlIgnorePadding(\n requireStringParam(params.clientDataJSON, \"clientDataJSON\"),\n );\n const clientData = parseClientDataJSON(clientDataJSON);\n verifyClientDataType(clientData, ClientDataType.Get, \"webauthn.get\");\n verifySameOrigin(clientData);\n verifyOrigin(clientData, rp);\n const credentialId = validateCredentialId(params.credentialId);\n const challengeHash = encodeBase64urlNoPadding(new Uint8Array(sha256(clientData.challenge)));\n let assertion;\n try {\n assertion = await mutatePasskeyBeginAssertion(ctx, {\n verifierId: verifier,\n expectedChallenge: challengeHash,\n credentialId,\n });\n } catch (error) {\n logPasskeyError(error);\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CHALLENGE,\n \"Invalid or expired passkey challenge.\",\n );\n }\n if (!assertion.verifierAccepted) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CHALLENGE,\n \"Invalid or expired passkey challenge.\",\n );\n }\n\n const authenticatorDataBytes = decodeBase64urlIgnorePadding(\n requireStringParam(params.authenticatorData, \"authenticatorData\"),\n );\n const authenticatorData = parseAuthenticatorData(authenticatorDataBytes);\n const backupState = parseBackupState(authenticatorDataBytes);\n const signatureBytes = decodeBase64urlIgnorePadding(\n requireStringParam(params.signature, \"signature\"),\n );\n const signatureMessage = createAssertionSignatureMessage(\n authenticatorDataBytes,\n clientDataJSON,\n );\n const messageHash = sha256(signatureMessage);\n\n verifyRpId(authenticatorData, rp.rpId);\n verifyUserFlags(authenticatorData, rp.authentication.userVerification);\n\n const passkey = assertion.passkey;\n\n verifyAssertionSignatureUniformly(passkey, signatureBytes, messageHash);\n if (provider.options.securityKeysOnly) {\n requireSecurityKeyCredential(passkey);\n requireSecurityKeyCredential({\n transports: passkey.transports,\n deviceType: backupState.deviceType,\n backedUp: backupState.backedUp,\n });\n }\n validateBackupEligibility(passkey.deviceType, backupState.deviceType);\n\n if (rp.registration.attestation) {\n await assertStoredAttestationTrusted(rp.registration.attestation, passkey.attestation);\n }\n\n if (\n (passkey.counter !== 0 || authenticatorData.signatureCounter !== 0) &&\n authenticatorData.signatureCounter <= passkey.counter\n ) {\n throw convexError(\n ErrorCode.PASSKEY_COUNTER_ERROR,\n \"Authenticator counter did not increase — possible credential cloning detected.\",\n );\n }\n\n const replaceSessionId = (await getAuthSessionId(ctx)) ?? undefined;\n let completed;\n try {\n completed = await mutatePasskeyCompleteAssertion(ctx, {\n id: passkey._id,\n counter: authenticatorData.signatureCounter,\n lastUsedAt: Date.now(),\n backedUp: backupState.backedUp,\n replaceSessionId,\n sessionExpirationTime: sessionExpirationTime(ctx.auth.config),\n refreshTokenExpirationTime: refreshTokenExpirationTime(ctx.auth.config),\n });\n } catch (error) {\n throw asConvexError(error, ErrorCode.INTERNAL_ERROR, \"Failed to finalize passkey sign-in.\");\n }\n if (completed.status === \"rejected\") {\n throw convexError(\n ErrorCode.PASSKEY_COUNTER_ERROR,\n \"Authenticator counter did not increase — possible credential cloning detected.\",\n );\n }\n\n return await finalizePasskeySession(ctx, completed);\n };\n\n const flowHandlers = {\n register: async () => {\n const rp = resolveRpOptions(provider);\n\n const challenge = new Uint8Array(32);\n crypto.getRandomValues(challenge);\n const challengeHash = encodeBase64urlNoPadding(new Uint8Array(sha256(challenge)));\n\n let registration;\n let userId: string;\n try {\n if (args.continuation === undefined) {\n userId = await requireAuthenticatedUserId(ctx);\n registration = await mutatePasskeyBeginRegistration(ctx, {\n userId,\n sessionId: (await getAuthSessionId(ctx)) ?? undefined,\n signature: challengeHash,\n expirationTime: Date.now() + rp.challengeExpirationMs,\n });\n } else {\n const rotation = await mutatePasskeyBeginRotation(ctx, {\n continuationId: args.continuation,\n provider: provider.id,\n signature: challengeHash,\n expirationTime: Date.now() + rp.challengeExpirationMs,\n });\n if (rotation.status === \"rejected\") {\n throw convexError(ErrorCode.CONTINUATION_INVALID, \"Invalid or expired continuation.\");\n }\n userId = rotation.userId;\n registration = rotation;\n }\n } catch (err) {\n logPasskeyError(err);\n throw convexError(ErrorCode.INTERNAL_ERROR, \"An unexpected error occurred.\");\n }\n const user = registration.user;\n const userName = params.userName ?? user?.email ?? \"user\";\n const userDisplayName = params.userDisplayName ?? user?.name ?? userName;\n const existing = registration.credentials;\n if (args.continuation === undefined && existing.length >= MAX_WEBAUTHN_CREDENTIALS_PER_USER) {\n throw convexError(\n ErrorCode.INVALID_PARAMETERS,\n `A user can register at most ${MAX_WEBAUTHN_CREDENTIALS_PER_USER} WebAuthn credentials.`,\n );\n }\n const excludeCredentials = existing.map((credential) => ({\n id: credential.id,\n transports: credential.transports,\n }));\n\n const userHandle = encodeBase64urlNoPadding(new TextEncoder().encode(userId));\n\n return {\n kind: \"webauthnOptions\" as const,\n options: {\n rp: { name: rp.rpName, id: rp.rpId },\n user: { id: userHandle, name: userName, displayName: userDisplayName },\n challenge: encodeBase64urlNoPadding(challenge),\n pubKeyCredParams: rp.registration.algorithms.map((alg) => ({\n type: \"public-key\" as const,\n alg,\n })),\n timeout: rp.challengeExpirationMs,\n ...(rp.registration.hints === undefined ? {} : { hints: rp.registration.hints }),\n attestation: rp.registration.attestation?.conveyance ?? \"none\",\n authenticatorSelection: {\n residentKey: rp.registration.residentKey,\n requireResidentKey: rp.registration.residentKey === \"required\",\n userVerification: rp.registration.userVerification,\n ...(rp.registration.authenticatorAttachment === undefined\n ? {}\n : { authenticatorAttachment: rp.registration.authenticatorAttachment }),\n },\n excludeCredentials,\n },\n verifier: registration.verifierId,\n ...(args.continuation === undefined\n ? {}\n : { continuation: args.continuation, operation: \"rotate\" as const }),\n };\n },\n\n signIn: async () => {\n const rp = resolveRpOptions(provider);\n const challenge = new Uint8Array(32);\n crypto.getRandomValues(challenge);\n const challengeHash = encodeBase64urlNoPadding(new Uint8Array(sha256(challenge)));\n\n let allowCredentials: AllowCredential[] | undefined;\n let credentialHints: string[] | undefined;\n const email = params.email === undefined ? undefined : normalizeEmail(params.email);\n const sessionId = (await getAuthSessionId(ctx)) ?? undefined;\n let signIn;\n try {\n signIn = await mutatePasskeyBeginSignIn(ctx, {\n sessionId,\n signature: challengeHash,\n expirationTime: Date.now() + rp.challengeExpirationMs,\n ...(email === undefined ? {} : { verifiedEmail: email }),\n });\n } catch (err) {\n logPasskeyError(err);\n throw convexError(ErrorCode.INTERNAL_ERROR, \"An unexpected error occurred.\");\n }\n\n if (email !== undefined) {\n const securityKeysOnly = provider.options.securityKeysOnly === true;\n const eligibleCredentials = selectAuthenticationCredentials(\n signIn.credentials,\n securityKeysOnly,\n );\n allowCredentials = await deriveEmailAllowCredentials(\n email,\n rp.rpId,\n eligibleCredentials,\n securityKeysOnly,\n );\n credentialHints = securityKeysOnly\n ? [\"security-key\"]\n : credentialAuthenticationHints(eligibleCredentials);\n }\n\n const hints = rp.authentication.hints ?? credentialHints;\n return {\n kind: \"webauthnOptions\" as const,\n options: {\n challenge: encodeBase64urlNoPadding(challenge),\n timeout: rp.challengeExpirationMs,\n rpId: rp.rpId,\n userVerification: rp.authentication.userVerification,\n ...(hints === undefined ? {} : { hints }),\n ...(allowCredentials === undefined ? {} : { allowCredentials }),\n },\n verifier: signIn.verifierId,\n };\n },\n\n verify: async () => {\n const isRegistration =\n typeof params.attestationObject === \"string\" && params.attestationObject.length > 0;\n const isAuthentication = typeof params.signature === \"string\" && params.signature.length > 0;\n\n if (isRegistration && !isAuthentication) {\n return await handleRegisterVerify();\n }\n if (isAuthentication && !isRegistration) {\n return await handleAuthVerify();\n }\n throw convexError(\n ErrorCode.PASSKEY_INVALID_VERIFY,\n \"`verify` flow requires either `attestationObject` (to complete a `register`) \" +\n \"or `signature` + `credentialId` (to complete a `signIn`).\",\n );\n },\n } satisfies Record<WebAuthnFlow, () => Promise<WebAuthnResult>>;\n\n const handler = flowHandlers[dispatch.flow];\n if (!handler) {\n throw convexError(ErrorCode.PASSKEY_MISSING_FLOW, `Unknown passkey flow: ${dispatch.flow}`);\n }\n return handler();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"webauthn.js","names":[],"sources":["../../src/server/webauthn.ts"],"sourcesContent":["/**\n * Server-side WebAuthn ceremony logic.\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 * @module\n */\n\nimport {\n decodePKIXECDSASignature,\n decodeSEC1PublicKey,\n p256,\n verifyECDSASignature,\n} from \"@oslojs/crypto/ecdsa\";\nimport {\n decodePKCS1RSAPublicKey,\n RSAPublicKey,\n sha256ObjectIdentifier,\n verifyRSASSAPKCS1v15Signature,\n} from \"@oslojs/crypto/rsa\";\nimport { decodeCBORToNativeValueNoLeftoverBytes } from \"@oslojs/cbor\";\nimport { sha256 } from \"@oslojs/crypto/sha2\";\nimport { decodeBase64urlIgnorePadding, encodeBase64urlNoPadding } from \"@oslojs/encoding\";\nimport {\n ClientDataType,\n coseAlgorithmES256,\n coseAlgorithmRS256,\n COSEKeyType,\n createAssertionSignatureMessage,\n parseAttestationObject,\n parseAuthenticatorData,\n parseClientDataJSON,\n} from \"@oslojs/webauthn\";\n\nimport type {\n AuthTokens,\n SignInWebAuthnOptionsResult,\n SignInSessionResult,\n} from \"../shared/results\";\nimport { ConvexError, type GenericId } from \"convex/values\";\n\nimport { ErrorCode } from \"../shared/codes\";\nimport {\n MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH,\n MAX_WEBAUTHN_CREDENTIALS_PER_USER,\n} from \"../shared/webauthn\";\nimport { authFlowError } from \"../shared/errors\";\nimport { requireAuthKey } from \"./env\";\nimport type { AuthErrorData } from \"./errors\";\nimport { toConvexError } from \"./errors\";\nimport { queueAuthEvent } from \"./events\";\nimport { getAuthenticatedUserIdOrNull } from \"./identity/claims\";\nimport { LOG_LEVELS, log } from \"./log\";\nimport {\n consumeVerifierById,\n mutatePasskeyBeginRotation,\n mutatePasskeyBeginAssertion,\n mutatePasskeyBeginRegistration,\n mutatePasskeyBeginSignIn,\n mutatePasskeyCompleteAssertion,\n mutatePasskeyCompleteRegistration,\n mutatePasskeyCompleteRotation,\n queryContinuation,\n} from \"./component/factor/db\";\nimport {\n buildSessionIdentity,\n finalizeSessionIssuance,\n getAuthSessionId,\n getAuthSessionReplacement,\n sessionExpirationTime,\n} from \"./session/lifecycle\";\nimport { encodeRefreshToken, refreshTokenExpirationTime } from \"./token/refresh\";\nimport { buildKnownSignInIdentityAttributes } from \"./telemetry\";\nimport {\n AuthDataModel,\n GenericActionCtxWithAuthConfig,\n WebAuthnAttestationEvidence,\n WebAuthnAttestationPolicy,\n WebAuthnProviderConfig,\n SessionInfo,\n} from \"./types\";\nimport { appUrlFromEnv } from \"./url\";\nimport { setActiveSpanAttributes } from \"./utils/span\";\n\ntype EnrichedActionCtx = GenericActionCtxWithAuthConfig<AuthDataModel>;\n\ninterface RpOptions {\n rpName: string;\n rpId: string;\n origin: string | string[];\n challengeExpirationMs: number;\n registration: WebAuthnProviderConfig[\"options\"][\"registration\"];\n authentication: WebAuthnProviderConfig[\"options\"][\"authentication\"];\n}\n\ntype WebAuthnResult =\n | SignInSessionResult<SessionInfo<AuthTokens | null> | null>\n | SignInWebAuthnOptionsResult;\n\ntype PasskeyRegistrationData = Omit<\n Parameters<typeof mutatePasskeyCompleteRegistration>[1],\n \"replaceSession\"\n>;\n\ntype PasskeySessionCompletion = Pick<\n Awaited<ReturnType<typeof mutatePasskeyCompleteRegistration>>,\n \"user\" | \"sessionId\" | \"refreshTokenId\" | \"replacedSessionId\"\n>;\n\n/**\n * The WebAuthn provider has three single-word flows:\n *\n * - `register` — issue a WebAuthn registration challenge (phase 1).\n * - `signIn` — issue a WebAuthn authentication challenge (phase 1).\n * - `verify` — consume the WebAuthn response (phase 2). The server\n * auto-detects whether this completes a registration\n * (`attestationObject` present) or a sign-in\n * (`signature` + `credentialId` present).\n */\nconst WEBAUTHN_FLOWS = [\"register\", \"signIn\", \"verify\"] as const;\ntype WebAuthnFlow = (typeof WEBAUTHN_FLOWS)[number];\ntype WebAuthnDispatch = { flow: WebAuthnFlow };\n\nfunction isWebAuthnFlow(value: string): value is WebAuthnFlow {\n return WEBAUTHN_FLOWS.some((flow) => flow === value);\n}\n\ntype PasskeyParams = {\n flow?: string;\n userName?: string;\n userDisplayName?: string;\n email?: string;\n clientDataJSON?: string;\n attestationObject?: string;\n transports?: string[];\n passkeyName?: string;\n credentialId?: string;\n authenticatorData?: string;\n signature?: string;\n};\n\ntype PasskeyParamCandidate = {\n flow?: unknown;\n userName?: unknown;\n userDisplayName?: unknown;\n email?: unknown;\n clientDataJSON?: unknown;\n attestationObject?: unknown;\n transports?: unknown;\n passkeyName?: unknown;\n credentialId?: unknown;\n authenticatorData?: unknown;\n signature?: unknown;\n};\n\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((entry) => typeof entry === \"string\");\n}\n\nfunction parsePasskeyParams(value: unknown): PasskeyParams {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return {};\n const input = value as PasskeyParamCandidate;\n const string = (key: keyof PasskeyParams) => {\n const candidate = input[key];\n return typeof candidate === \"string\" ? candidate : undefined;\n };\n const transports = isStringArray(input.transports) ? input.transports : undefined;\n return {\n flow: string(\"flow\"),\n userName: string(\"userName\"),\n userDisplayName: string(\"userDisplayName\"),\n email: string(\"email\"),\n clientDataJSON: string(\"clientDataJSON\"),\n attestationObject: string(\"attestationObject\"),\n transports,\n passkeyName: string(\"passkeyName\"),\n credentialId: string(\"credentialId\"),\n authenticatorData: string(\"authenticatorData\"),\n signature: string(\"signature\"),\n };\n}\n\nconst requireStringParam = (value: unknown, name: string) => {\n if (typeof value !== \"string\") {\n throw convexError(ErrorCode.INVALID_PARAMETERS, `Missing \\`${name}\\` parameter.`);\n }\n return value;\n};\n\nconst convexError = (code: ErrorCode, message: string) =>\n toConvexError(authFlowError(code, message));\n\nconst MAX_ENCODED_WEBAUTHN_CREDENTIAL_ID_LENGTH = Math.ceil(\n (MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH * 4) / 3,\n);\n\n/** @internal */\nexport function validateCredentialId(value: unknown): string {\n const credentialId = requireStringParam(value, \"credentialId\");\n if (\n credentialId.length === 0 ||\n credentialId.length > MAX_ENCODED_WEBAUTHN_CREDENTIAL_ID_LENGTH\n ) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n `Credential ID must be between 1 and ${MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH} bytes.`,\n );\n }\n if (!/^[A-Za-z0-9_-]+$/.test(credentialId)) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n \"Credential ID must be valid unpadded base64url.\",\n );\n }\n\n let decoded: Uint8Array;\n try {\n decoded = decodeBase64urlIgnorePadding(credentialId);\n } catch {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n \"Credential ID must be valid unpadded base64url.\",\n );\n }\n if (decoded.byteLength < 1 || decoded.byteLength > MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n `Credential ID must be between 1 and ${MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH} bytes.`,\n );\n }\n return credentialId;\n}\n\nconst asConvexError = (\n error: unknown,\n code: ErrorCode,\n message: string,\n): ConvexError<AuthErrorData> =>\n error instanceof ConvexError\n ? error\n : error instanceof Error\n ? toConvexError(authFlowError(code, error.message || message))\n : convexError(code, message);\n\nconst logPasskeyError = (err: unknown) =>\n log(LOG_LEVELS.ERROR, \"passkey error:\", err instanceof Error ? err.message : String(err));\n\nfunction resolveRpOptions(provider: WebAuthnProviderConfig): RpOptions {\n try {\n let appUrl: string | undefined;\n if (provider.options.rpId === undefined || provider.options.origin === undefined) {\n try {\n appUrl = appUrlFromEnv();\n } catch {\n throw convexError(\n ErrorCode.PASSKEY_MISSING_CONFIG,\n \"Passkey provider requires APP_URL env var (your frontend URL) or explicit rpId and origin in the provider config. CONVEX_SITE_URL cannot be used because WebAuthn RP ID must match the frontend domain.\",\n );\n }\n }\n\n const appHostname = appUrl ? new URL(appUrl).hostname : undefined;\n const registration: RpOptions[\"registration\"] = {\n userVerification: provider.options.registration.userVerification ?? \"required\",\n residentKey: provider.options.registration.residentKey ?? \"preferred\",\n algorithms: provider.options.registration.algorithms ?? [\n coseAlgorithmES256,\n coseAlgorithmRS256,\n ],\n };\n const authentication: RpOptions[\"authentication\"] = {\n userVerification: provider.options.authentication.userVerification ?? \"required\",\n };\n if (provider.options.registration.authenticatorAttachment !== undefined) {\n registration.authenticatorAttachment = provider.options.registration.authenticatorAttachment;\n }\n if (provider.options.registration.hints !== undefined) {\n registration.hints = provider.options.registration.hints;\n }\n if (provider.options.registration.attestation !== undefined) {\n registration.attestation = provider.options.registration.attestation;\n }\n if (provider.options.authentication.hints !== undefined) {\n authentication.hints = provider.options.authentication.hints;\n }\n return {\n rpName: provider.options.rpName ?? appHostname ?? provider.options.rpId ?? \"localhost\",\n rpId: provider.options.rpId ?? appHostname ?? \"localhost\",\n origin: provider.options.origin ?? appUrl ?? \"http://localhost\",\n challengeExpirationMs: provider.options.challengeExpirationMs ?? 300_000,\n registration,\n authentication,\n };\n } catch (error) {\n throw asConvexError(\n error,\n ErrorCode.PASSKEY_MISSING_CONFIG,\n \"Passkey relying party configuration is invalid.\",\n );\n }\n}\n\nfunction verifySameOrigin<T extends { crossOrigin: boolean | null }>(clientData: T): T {\n if (clientData.crossOrigin === true) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n \"Cross-origin WebAuthn ceremonies are not supported.\",\n );\n }\n return clientData;\n}\n\nfunction verifyClientDataType<T extends { type: ClientDataType }>(\n clientData: T,\n expectedType: ClientDataType,\n label: string,\n): T {\n if (clientData.type !== expectedType) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n `Invalid client data type: expected ${label}`,\n );\n }\n return clientData;\n}\n\nfunction verifyOrigin<T extends { origin: string }>(clientData: T, rp: RpOptions): T {\n const allowed = Array.isArray(rp.origin) ? rp.origin : [rp.origin];\n if (!allowed.includes(clientData.origin)) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_ORIGIN,\n `Invalid origin: ${clientData.origin}, expected one of: ${allowed.join(\", \")}`,\n );\n }\n return clientData;\n}\n\nasync function verifyAndConsumeChallenge<T extends { challenge: Uint8Array }>(\n clientData: T,\n ctx: EnrichedActionCtx,\n verifierValue: string,\n) {\n const challengeHash = encodeBase64urlNoPadding(new Uint8Array(sha256(clientData.challenge)));\n // Read-check-delete the challenge verifier in ONE component mutation: this runs\n // in an action, so a separate `get` then `remove` let two concurrent\n // assertions carrying the same challenge both pass before either delete landed,\n // each minting its own session. `consume` matches the expected challenge hash\n // and deletes atomically, so only the single winner gets the doc back.\n let doc;\n try {\n doc = await consumeVerifierById(ctx, verifierValue, challengeHash);\n } catch (err) {\n console.error(\"[auth] passkey error:\", err);\n throw convexError(ErrorCode.PASSKEY_INVALID_CHALLENGE, \"Invalid or expired passkey challenge.\");\n }\n if (!doc) {\n throw convexError(ErrorCode.PASSKEY_INVALID_CHALLENGE, \"Invalid or expired passkey challenge.\");\n }\n return doc;\n}\n\nfunction verifyRpId<T extends { verifyRelyingPartyIdHash: (id: string) => boolean }>(\n authData: T,\n rpId: string,\n): T {\n if (!authData.verifyRelyingPartyIdHash(rpId)) {\n throw convexError(ErrorCode.PASSKEY_RP_MISMATCH, \"Relying party ID mismatch.\");\n }\n return authData;\n}\n\nfunction verifyUserFlags<T extends { userPresent: boolean; userVerified: boolean }>(\n authData: T,\n userVerification: \"required\" | \"preferred\" | \"discouraged\",\n): T {\n if (!authData.userPresent) {\n throw convexError(ErrorCode.PASSKEY_USER_PRESENCE, \"User presence flag not set.\");\n }\n if (userVerification === \"required\" && !authData.userVerified) {\n throw convexError(\n ErrorCode.PASSKEY_USER_VERIFICATION,\n \"User verification required but not performed.\",\n );\n }\n return authData;\n}\n\ntype BackupState = {\n deviceType: \"singleDevice\" | \"multiDevice\";\n backedUp: boolean;\n};\n\n/** @internal */\nexport function parseBackupState(authenticatorData: Uint8Array): BackupState {\n if (authenticatorData.byteLength < 33) {\n throw convexError(ErrorCode.PASSKEY_INVALID_CLIENT_DATA, \"Authenticator data is too short.\");\n }\n const flags = authenticatorData[32];\n const backupEligible = (flags & 0x08) !== 0;\n const backedUp = (flags & 0x10) !== 0;\n if (backedUp && !backupEligible) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n \"Authenticator backup state is invalid.\",\n );\n }\n return {\n deviceType: backupEligible ? \"multiDevice\" : \"singleDevice\",\n backedUp,\n };\n}\n\nfunction extractAttestationAuthenticatorData(attestationObject: Uint8Array): Uint8Array {\n try {\n const decoded = decodeCBORToNativeValueNoLeftoverBytes(attestationObject, 4);\n if (\n typeof decoded !== \"object\" ||\n decoded === null ||\n !(\"authData\" in decoded) ||\n !(decoded.authData instanceof Uint8Array)\n ) {\n throw new Error(\"Invalid or missing authData\");\n }\n return decoded.authData;\n } catch (error) {\n throw asConvexError(\n error,\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n \"Invalid attestation authenticator data.\",\n );\n }\n}\n\n/** @internal */\nexport function validateCredentialAlgorithm(\n algorithm: number,\n configuredAlgorithms: readonly number[],\n): void {\n if (!configuredAlgorithms.includes(algorithm)) {\n throw convexError(\n ErrorCode.PASSKEY_UNSUPPORTED_ALGORITHM,\n `Credential algorithm ${algorithm} was not offered by the server.`,\n );\n }\n}\n\nfunction resolveWebAuthnDispatch(params: PasskeyParams): WebAuthnDispatch {\n const flow = params.flow;\n if (typeof flow === \"string\" && isWebAuthnFlow(flow)) {\n return { flow };\n }\n throw convexError(\n ErrorCode.PASSKEY_MISSING_FLOW,\n \"Missing `flow` parameter. Expected one of: \" + WEBAUTHN_FLOWS.join(\", \"),\n );\n}\n\nfunction requirePasskeyVerifier(verifier: string | undefined): string {\n if (verifier != null) {\n return verifier;\n }\n throw convexError(ErrorCode.PASSKEY_MISSING_VERIFIER, \"Missing verifier for passkey operation.\");\n}\n\nasync function requireAuthenticatedUserId(ctx: EnrichedActionCtx): Promise<string> {\n try {\n const userId = await getAuthenticatedUserIdOrNull(ctx);\n if (userId === null) {\n throw convexError(\n ErrorCode.PASSKEY_AUTH_REQUIRED,\n \"Sign in first, then add a passkey to your account.\",\n );\n }\n return userId;\n } catch (err) {\n console.error(\"[auth] passkey error:\", err);\n throw convexError(\n ErrorCode.PASSKEY_AUTH_REQUIRED,\n \"Sign in first, then add a passkey to your account.\",\n );\n }\n}\n\nfunction resolveRegistrationPublicKeyBytes(\n publicKey: NonNullable<\n ReturnType<typeof parseAttestationObject>[\"authenticatorData\"][\"credential\"]\n >[\"publicKey\"],\n algorithm: number,\n): Uint8Array {\n if (algorithm === 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 bytes;\n }\n if (algorithm === coseAlgorithmRS256) {\n const rsa = publicKey.rsa();\n const rsaPubKey = new RSAPublicKey(rsa.n, rsa.e);\n return rsaPubKey.encodePKCS1();\n }\n throw convexError(ErrorCode.PASSKEY_UNSUPPORTED_ALGORITHM, `Unsupported algorithm: ${algorithm}`);\n}\n\nfunction verifyAssertionSignature(\n passkey: { algorithm: number; publicKey: ArrayBuffer },\n signature: Uint8Array,\n messageHash: Uint8Array,\n): void {\n try {\n if (passkey.algorithm === coseAlgorithmES256) {\n const ecPublicKey = decodeSEC1PublicKey(p256, new Uint8Array(passkey.publicKey));\n const ecdsaSignature = decodePKIXECDSASignature(signature);\n if (!verifyECDSASignature(ecPublicKey, messageHash, ecdsaSignature)) {\n throw convexError(ErrorCode.PASSKEY_INVALID_SIGNATURE, \"Invalid passkey signature.\");\n }\n return;\n }\n if (passkey.algorithm === coseAlgorithmRS256) {\n const rsaPublicKey = decodePKCS1RSAPublicKey(new Uint8Array(passkey.publicKey));\n if (\n !verifyRSASSAPKCS1v15Signature(rsaPublicKey, sha256ObjectIdentifier, messageHash, signature)\n ) {\n throw convexError(ErrorCode.PASSKEY_INVALID_SIGNATURE, \"Invalid passkey signature.\");\n }\n return;\n }\n throw convexError(\n ErrorCode.PASSKEY_UNSUPPORTED_ALGORITHM,\n `Unsupported algorithm: ${passkey.algorithm}`,\n );\n } catch (error) {\n if (error instanceof ConvexError) throw error;\n throw convexError(ErrorCode.PASSKEY_INVALID_SIGNATURE, \"Invalid passkey signature.\");\n }\n}\n\nconst DUMMY_ES256_PUBLIC_KEY = decodeBase64urlIgnorePadding(\n \"BGsX0fLhLEJH-Lzm5WOkQPJ3A32BLeszoPShOUXYmMKWT-NC4v4af5uO5-tKfA-eFivOM1drMV7Oy7ZAaDe_UfU\",\n);\nconst DUMMY_RS256_PUBLIC_KEY = decodeBase64urlIgnorePadding(\n \"MIIBCgKCAQEAxumpcTeR9zlJNbNZinnjj_eb75YcIeNHf92QdRZ86g_wGzz6WW4VmpRSrkMJC1u3EYdN7rRSw1tveNaw7g6NTfaLjJdyrxkpNdx6niAXq8dMHGe5-Y9MyoGNwP0T_c6PrBdu_M-xGEtpDIG9GwNCM9RHsOxcf7PxVaHBZqR_BoWWLTHrVw6weXBYALsp3iz-jFDl5mG7JW9G_sKVr9dV0BAzeNMAKJEAoEO4swJB4qWGhHaSzCyy8YMngsuQrO9L5L_m8dG0GjYjjkV1G2aoRIl8VR7nnJdGFaAlSgqO_icJN6QY3WS2aalDEr1yRx2mjJPRUIr6rZMCfsuyjq1NZwIDAQAB\",\n);\n\ntype AssertionCredential = { algorithm: number; publicKey: ArrayBuffer };\n\nfunction copyArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;\n}\n\nfunction registrationData(\n userId: string,\n credentialId: string,\n publicKey: Uint8Array,\n algorithm: number,\n counter: number,\n backupState: BackupState,\n params: PasskeyParams,\n attestation: WebAuthnAttestationEvidence | undefined,\n ctx: EnrichedActionCtx,\n): PasskeyRegistrationData {\n return {\n userId,\n credentialId,\n publicKey: copyArrayBuffer(publicKey),\n algorithm,\n counter,\n ...(params.transports === undefined ? {} : { transports: params.transports }),\n deviceType: backupState.deviceType,\n backedUp: backupState.backedUp,\n ...(params.passkeyName === undefined ? {} : { name: params.passkeyName }),\n ...(attestation === undefined ? {} : { attestation }),\n createdAt: Date.now(),\n sessionExpirationTime: sessionExpirationTime(ctx.auth.config),\n refreshTokenExpirationTime: refreshTokenExpirationTime(ctx.auth.config),\n };\n}\n\nasync function finalizePasskeySession(\n ctx: EnrichedActionCtx,\n completed: PasskeySessionCompletion,\n): Promise<SignInSessionResult<SessionInfo<AuthTokens | null> | null>> {\n const userId = completed.user._id as GenericId<\"User\">;\n const sessionId = completed.sessionId as GenericId<\"Session\">;\n setActiveSpanAttributes({\n \"auth.signin.result\": \"success\",\n ...buildKnownSignInIdentityAttributes(\n ctx.auth.config,\n { userId, sessionId },\n completed.user.email,\n ),\n });\n if (completed.replacedSessionId !== undefined) {\n const replacedSessionId = completed.replacedSessionId as GenericId<\"Session\">;\n await queueAuthEvent(ctx, ctx.auth.config, {\n kind: \"session.invalidated\",\n actor: { type: \"system\" },\n subject: { type: \"session\", id: replacedSessionId },\n targets: [\n { kind: \"user\", id: userId },\n { kind: \"session\", id: replacedSessionId },\n ],\n outcome: \"success\",\n data: { userId, reason: \"replaced\" },\n });\n }\n await queueAuthEvent(ctx, ctx.auth.config, {\n kind: \"session.signed_in\",\n actor: { type: \"user\", id: userId },\n subject: { type: \"session\", id: sessionId },\n targets: [\n { kind: \"user\", id: userId },\n { kind: \"session\", id: sessionId },\n ],\n outcome: \"success\",\n data: { provider: \"session\" },\n });\n const session = await finalizeSessionIssuance(ctx.auth.config, {\n userId,\n sessionId,\n identity: buildSessionIdentity(userId, sessionId, completed.user),\n refreshToken: encodeRefreshToken(\n completed.refreshTokenId as GenericId<\"RefreshToken\">,\n sessionId,\n ),\n });\n return { kind: \"signedIn\", session };\n}\n\nfunction signatureIsValid(\n credential: AssertionCredential,\n signature: Uint8Array,\n messageHash: Uint8Array,\n): boolean {\n try {\n verifyAssertionSignature(credential, signature, messageHash);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Perform both supported verification algorithms for real and unknown IDs so\n * the selected algorithm is not a credential-existence timing oracle.\n */\nfunction verifyAssertionSignatureUniformly(\n passkey: AssertionCredential | null,\n signature: Uint8Array,\n messageHash: Uint8Array,\n): asserts passkey is AssertionCredential {\n const es256Valid = signatureIsValid(\n passkey?.algorithm === coseAlgorithmES256\n ? passkey\n : {\n algorithm: coseAlgorithmES256,\n publicKey: copyArrayBuffer(DUMMY_ES256_PUBLIC_KEY),\n },\n signature,\n messageHash,\n );\n const rs256Valid = signatureIsValid(\n passkey?.algorithm === coseAlgorithmRS256\n ? passkey\n : {\n algorithm: coseAlgorithmRS256,\n publicKey: copyArrayBuffer(DUMMY_RS256_PUBLIC_KEY),\n },\n signature,\n messageHash,\n );\n const valid =\n passkey?.algorithm === coseAlgorithmES256\n ? es256Valid\n : passkey?.algorithm === coseAlgorithmRS256\n ? rs256Valid\n : false;\n if (!valid) {\n throw convexError(ErrorCode.PASSKEY_INVALID_SIGNATURE, \"Invalid passkey signature.\");\n }\n}\n\n/** @internal */\nexport function validateBackupEligibility(\n storedDeviceType: string,\n assertionDeviceType: BackupState[\"deviceType\"],\n): void {\n if (storedDeviceType !== assertionDeviceType) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n \"Authenticator backup eligibility changed for an existing credential.\",\n );\n }\n}\n\n/** @internal */\nexport async function assertStoredAttestationTrusted(\n policy: WebAuthnAttestationPolicy,\n evidence: WebAuthnAttestationEvidence | undefined,\n): Promise<void> {\n if (!evidence) {\n throw convexError(\n ErrorCode.PASSKEY_UNTRUSTED_ATTESTATION,\n \"This credential predates the current attestation policy and must be registered again.\",\n );\n }\n try {\n await policy.verifier.assertTrusted(evidence);\n } catch (error) {\n throw asConvexError(\n error,\n ErrorCode.PASSKEY_UNTRUSTED_ATTESTATION,\n \"Authenticator attestation is no longer trusted.\",\n );\n }\n}\n\n/**\n * Build a constant-size, secret-keyed `allowCredentials` list for an email-first\n * sign-in. Real credential IDs are mixed with deterministic decoys, then are\n * ordered by a secret-keyed digest.\n *\n * A known email with passkeys returns a populated `allowCredentials`, so an\n * absent/empty list for every other email would be an immediate\n * account-enumeration oracle. Padding keeps the top-level shape and pair\n * multiplicities equivalent and prevents public prediction of decoy IDs.\n *\n * Credential IDs are authenticator-generated and variable-length. A bounded\n * allow-list cannot make every possible real length statistically identical to\n * an unknown account, so this is defense in depth rather than a claim of\n * identifier privacy. Prefer discoverable credentials and email-less sign-in\n * when account-enumeration resistance is a hard requirement.\n *\n * Decoy IDs reference no stored credential: the ceremony simply fails to produce\n * an assertion (as it would for a real account whose authenticator is absent),\n * and a forged assertion can never pass {@link verifyAssertionSignature} because\n * no matching credential is ever looked up on `verify`.\n */\nconst EMAIL_ALLOW_CREDENTIALS_SIZE = 32;\nconst EMAIL_ALLOW_REAL_CREDENTIALS = EMAIL_ALLOW_CREDENTIALS_SIZE / 2;\nconst MAX_EMAIL_LENGTH = 254;\n\ntype AllowCredential = { type: \"public-key\"; id: string; transports?: string[] };\n\ntype StoredCredential = { id: string; transports?: string[] };\n\nconst ROAMING_AUTHENTICATOR_TRANSPORTS = new Set([\"usb\", \"nfc\", \"ble\"]);\nconst ROAMING_CEREMONY_TRANSPORTS = [\"ble\", \"nfc\", \"usb\"];\n\ntype SecurityKeyCredential = {\n transports?: readonly string[];\n deviceType?: string;\n backedUp?: boolean;\n};\n\nfunction roamingTransports(transports: readonly string[] | undefined): string[] | undefined {\n if (\n !transports?.length ||\n !transports.every((value) => ROAMING_AUTHENTICATOR_TRANSPORTS.has(value))\n ) {\n return undefined;\n }\n return [...new Set(transports)].sort();\n}\n\n/** @internal */\nexport function isSecurityKeyCredential(credential: SecurityKeyCredential): boolean {\n return (\n roamingTransports(credential.transports) !== undefined &&\n (credential.deviceType === undefined || credential.deviceType === \"singleDevice\") &&\n (credential.backedUp === undefined || credential.backedUp === false)\n );\n}\n\n/** @internal */\nexport function selectAuthenticationCredentials<T extends StoredCredential>(\n credentials: readonly T[],\n securityKeysOnly: boolean | undefined,\n): T[] {\n return securityKeysOnly ? credentials.filter(isSecurityKeyCredential) : [...credentials];\n}\n\nfunction requireSecurityKeyCredential(credential: SecurityKeyCredential): void {\n if (!isSecurityKeyCredential(credential)) {\n throw convexError(\n ErrorCode.PASSKEY_SECURITY_KEY_REQUIRED,\n \"A roaming hardware security key is required.\",\n );\n }\n}\n\nfunction credentialAuthenticationHints(\n credentials: readonly StoredCredential[],\n): string[] | undefined {\n return credentials.length > 0 &&\n credentials.every((credential) => roamingTransports(credential.transports) !== undefined)\n ? [\"security-key\"]\n : undefined;\n}\n\nasync function keyedDigest(key: CryptoKey, value: string): Promise<Uint8Array> {\n const encoder = new TextEncoder();\n return new Uint8Array(await crypto.subtle.sign(\"HMAC\", key, encoder.encode(value)));\n}\n\nasync function keyedBytes(key: CryptoKey, domain: string, length: number): Promise<Uint8Array> {\n const blocks = await Promise.all(\n Array.from({ length: Math.ceil(length / 32) }, (_, block) =>\n keyedDigest(key, `${domain}:block:${block}`),\n ),\n );\n const bytes = new Uint8Array(length);\n for (let offset = 0; offset < length; offset += 32) {\n bytes.set(blocks[offset / 32].subarray(0, Math.min(32, length - offset)), offset);\n }\n return bytes;\n}\n\nfunction normalizeEmail(value: unknown): string {\n const email = requireStringParam(value, \"email\").trim().toLowerCase();\n if (email.length === 0 || email.length > MAX_EMAIL_LENGTH) {\n throw convexError(ErrorCode.INVALID_PARAMETERS, \"Email must be between 1 and 254 characters.\");\n }\n return email;\n}\n\nasync function deriveEmailAllowCredentials(\n normalizedEmail: string,\n rpId: string,\n realCredentials: readonly StoredCredential[],\n forceRoamingCeremony = false,\n): Promise<AllowCredential[]> {\n const secret = requireAuthKey(\"webauthnMaskingKey\");\n const emailSeed = encodeBase64urlNoPadding(\n new Uint8Array(sha256(new TextEncoder().encode(normalizedEmail))),\n );\n const realIds = realCredentials\n .flatMap((credential) => {\n try {\n const byteLength = decodeBase64urlIgnorePadding(credential.id).byteLength;\n return byteLength >= 1 && byteLength <= MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH\n ? [\n {\n id: credential.id,\n byteLength,\n transports: roamingTransports(credential.transports),\n },\n ]\n : [];\n } catch {\n return [];\n }\n })\n .slice(0, EMAIL_ALLOW_REAL_CREDENTIALS);\n const ceremonyTransports =\n forceRoamingCeremony || credentialAuthenticationHints(realCredentials)\n ? ROAMING_CEREMONY_TRANSPORTS\n : undefined;\n const key = await crypto.subtle.importKey(\n \"raw\",\n new TextEncoder().encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const makeCredential = async (\n id: string,\n discriminator: string,\n transports?: string[],\n ): Promise<AllowCredential & { order: string }> => ({\n type: \"public-key\",\n id,\n ...(transports === undefined ? {} : { transports }),\n order: encodeBase64urlNoPadding(\n await keyedDigest(\n key,\n `convex-auth:webauthn-order:v2:${rpId}:${emailSeed}:${discriminator}:${id}`,\n ),\n ),\n });\n\n const lengthSeed = await keyedDigest(key, `convex-auth:webauthn-lengths:v2:${rpId}:${emailSeed}`);\n const credentialPairs = await Promise.all(\n Array.from({ length: EMAIL_ALLOW_REAL_CREDENTIALS }, async (_, pairIndex) => {\n const real = realIds[pairIndex];\n const derivedLength =\n 1 +\n (((lengthSeed[pairIndex * 2] << 8) | lengthSeed[pairIndex * 2 + 1]) %\n MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH);\n const byteLength = real?.byteLength ?? derivedLength;\n const pairTransports = ceremonyTransports ?? real?.transports;\n const first = real\n ? await makeCredential(real.id, `real:${pairIndex}`, pairTransports)\n : await keyedBytes(\n key,\n `convex-auth:webauthn-decoy:v2:${rpId}:${emailSeed}:pair:${pairIndex}:first`,\n byteLength,\n ).then((bytes) =>\n makeCredential(\n encodeBase64urlNoPadding(bytes),\n `pair:${pairIndex}:first`,\n pairTransports,\n ),\n );\n const companion = await keyedBytes(\n key,\n `convex-auth:webauthn-decoy:v2:${rpId}:${emailSeed}:pair:${pairIndex}:companion`,\n byteLength,\n ).then((bytes) =>\n makeCredential(\n encodeBase64urlNoPadding(bytes),\n `pair:${pairIndex}:companion`,\n pairTransports,\n ),\n );\n return [first, companion];\n }),\n );\n const credentials = credentialPairs.flat();\n credentials.sort((a, b) => a.order.localeCompare(b.order));\n return credentials.map(({ type, id, transports }) => ({\n type,\n id,\n ...(transports === undefined ? {} : { transports }),\n }));\n}\n\n/**\n * Drive the passkey provider's `register` / `signIn` / `verify` flow.\n *\n * Dispatches on `args.params.flow`; for `verify` it auto-detects whether the\n * response completes a registration or a sign-in (see {@link WEBAUTHN_FLOWS}).\n *\n * @param ctx - Auth-enriched action context.\n * @param provider - The resolved WebAuthn provider config.\n * @param args - The flow params and, for `verify`, the issued challenge verifier.\n * @returns A WebAuthn challenge (`webauthnOptions`) or a signed-in session.\n */\nexport async function handleWebAuthn(\n ctx: EnrichedActionCtx,\n provider: WebAuthnProviderConfig,\n args: {\n params?: unknown;\n verifier?: string;\n continuation?: string;\n },\n): Promise<WebAuthnResult> {\n const params = parsePasskeyParams(args.params);\n const dispatch = resolveWebAuthnDispatch(params);\n\n const handleRegisterVerify = async (): Promise<WebAuthnResult> => {\n const rp = resolveRpOptions(provider);\n const verifier = requirePasskeyVerifier(args.verifier);\n\n const encodedClientDataJSON = requireStringParam(params.clientDataJSON, \"clientDataJSON\");\n const clientDataJSON = decodeBase64urlIgnorePadding(encodedClientDataJSON);\n const clientData = parseClientDataJSON(clientDataJSON);\n verifyClientDataType(clientData, ClientDataType.Create, \"webauthn.create\");\n verifySameOrigin(clientData);\n verifyOrigin(clientData, rp);\n const challenge = await verifyAndConsumeChallenge(clientData, ctx, verifier);\n if (challenge.continuationId !== args.continuation) {\n throw convexError(ErrorCode.CONTINUATION_INVALID, \"Invalid or expired continuation.\");\n }\n let continuationId: string | undefined;\n let userId: string;\n if (challenge.continuationId !== undefined) {\n const continuation = await queryContinuation(ctx, challenge.continuationId);\n if (\n continuation === null ||\n continuation.provider !== provider.id ||\n continuation.operation !== \"rotate\"\n ) {\n throw convexError(ErrorCode.CONTINUATION_INVALID, \"Invalid or expired continuation.\");\n }\n continuationId = continuation._id;\n userId = continuation.userId;\n } else {\n userId = await requireAuthenticatedUserId(ctx);\n }\n\n const encodedAttestationObject = requireStringParam(\n params.attestationObject,\n \"attestationObject\",\n );\n const attestationObjectBytes = decodeBase64urlIgnorePadding(encodedAttestationObject);\n const rawAuthenticatorData = extractAttestationAuthenticatorData(attestationObjectBytes);\n const attestation = parseAttestationObject(attestationObjectBytes);\n const authData = attestation.authenticatorData;\n verifyRpId(authData, rp.rpId);\n verifyUserFlags(authData, rp.registration.userVerification);\n\n if (authData.credential == null) {\n throw convexError(ErrorCode.PASSKEY_NO_CREDENTIAL, \"No credential in attestation.\");\n }\n\n const credential = authData.credential;\n if (\n credential.id.byteLength < 1 ||\n credential.id.byteLength > MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH\n ) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CLIENT_DATA,\n `Credential ID must be between 1 and ${MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH} bytes.`,\n );\n }\n const credentialId = encodeBase64urlNoPadding(credential.id);\n const publicKey = credential.publicKey;\n const algorithm = publicKey.isAlgorithmDefined()\n ? publicKey.algorithm()\n : publicKey.type() === COSEKeyType.EC2\n ? coseAlgorithmES256\n : publicKey.type() === COSEKeyType.RSA\n ? coseAlgorithmRS256\n : coseAlgorithmES256;\n validateCredentialAlgorithm(algorithm, rp.registration.algorithms);\n const publicKeyBytes = resolveRegistrationPublicKeyBytes(publicKey, algorithm);\n const backupState = parseBackupState(rawAuthenticatorData);\n if (provider.options.securityKeysOnly) {\n requireSecurityKeyCredential({\n transports: params.transports,\n deviceType: backupState.deviceType,\n backedUp: backupState.backedUp,\n });\n }\n let attestationEvidence: WebAuthnAttestationEvidence | undefined;\n if (rp.registration.attestation) {\n const { verifier: attestationVerifier } = rp.registration.attestation;\n try {\n const evidence = await attestationVerifier.verify({\n clientDataJSON: encodedClientDataJSON,\n attestationObject: encodedAttestationObject,\n credentialId,\n transports: params.transports,\n expectedChallenge: encodeBase64urlNoPadding(clientData.challenge),\n expectedOrigin: rp.origin,\n expectedRpId: rp.rpId,\n requireUserVerification: rp.registration.userVerification === \"required\",\n supportedAlgorithms: rp.registration.algorithms,\n });\n attestationEvidence = {\n ...evidence,\n verifier: attestationVerifier.id,\n verifiedAt: Date.now(),\n status: \"trusted\" as const,\n };\n } catch (error) {\n throw asConvexError(\n error,\n ErrorCode.PASSKEY_UNTRUSTED_ATTESTATION,\n \"Authenticator attestation is not trusted.\",\n );\n }\n }\n\n let completed:\n | Awaited<ReturnType<typeof mutatePasskeyCompleteRegistration>>\n | Exclude<Awaited<ReturnType<typeof mutatePasskeyCompleteRotation>>, { status: \"rejected\" }>;\n try {\n const data = registrationData(\n userId,\n credentialId,\n publicKeyBytes,\n algorithm,\n authData.signatureCounter,\n backupState,\n params,\n attestationEvidence,\n ctx,\n );\n if (continuationId === undefined) {\n completed = await mutatePasskeyCompleteRegistration(ctx, {\n ...data,\n replaceSession: await getAuthSessionReplacement(ctx),\n });\n } else {\n const rotation = await mutatePasskeyCompleteRotation(ctx, {\n ...data,\n continuationId,\n provider: provider.id,\n });\n if (rotation.status === \"rejected\") {\n throw convexError(ErrorCode.CONTINUATION_INVALID, \"Invalid or expired continuation.\");\n }\n completed = rotation;\n }\n const passkeyId = completed.passkeyId as GenericId<\"Passkey\">;\n if (\"removedPasskeyIds\" in completed) {\n for (const removedPasskeyId of completed.removedPasskeyIds) {\n await queueAuthEvent(ctx, ctx.auth.config, {\n kind: \"passkey.removed\",\n actor: { type: \"user\", id: userId },\n subject: { type: \"passkey\", id: removedPasskeyId },\n targets: [{ kind: \"user\", id: userId }],\n outcome: \"success\",\n data: { passkeyId: removedPasskeyId },\n });\n }\n }\n await queueAuthEvent(ctx, ctx.auth.config, {\n kind: \"passkey.added\",\n actor: { type: \"user\", id: userId },\n subject: { type: \"passkey\", id: passkeyId },\n targets: [{ kind: \"user\", id: userId }],\n outcome: \"success\",\n data: { passkeyId, credentialId },\n });\n if (\"passwordChanged\" in completed && completed.passwordChanged) {\n await queueAuthEvent(ctx, ctx.auth.config, {\n kind: \"password.changed\",\n actor: { type: \"user\", id: userId },\n subject: { type: \"user\", id: userId },\n targets: [{ kind: \"user\", id: userId }],\n outcome: \"success\",\n data: { flow: \"reset\" },\n });\n }\n } catch (err) {\n logPasskeyError(err);\n // Preserve structured failures (e.g. the credentialId-uniqueness guard in\n // `factor.passkey.create` firing when two registrations race the same\n // credential) instead of masking them as a generic internal error.\n if (err instanceof ConvexError) {\n throw err;\n }\n throw convexError(ErrorCode.INTERNAL_ERROR, \"An unexpected error occurred.\");\n }\n\n return await finalizePasskeySession(ctx, completed);\n };\n\n const handleAuthVerify = async (): Promise<WebAuthnResult> => {\n const rp = resolveRpOptions(provider);\n const verifier = requirePasskeyVerifier(args.verifier);\n\n const clientDataJSON = decodeBase64urlIgnorePadding(\n requireStringParam(params.clientDataJSON, \"clientDataJSON\"),\n );\n const clientData = parseClientDataJSON(clientDataJSON);\n verifyClientDataType(clientData, ClientDataType.Get, \"webauthn.get\");\n verifySameOrigin(clientData);\n verifyOrigin(clientData, rp);\n const credentialId = validateCredentialId(params.credentialId);\n const challengeHash = encodeBase64urlNoPadding(new Uint8Array(sha256(clientData.challenge)));\n let assertion;\n try {\n assertion = await mutatePasskeyBeginAssertion(ctx, {\n verifierId: verifier,\n expectedChallenge: challengeHash,\n credentialId,\n });\n } catch (error) {\n logPasskeyError(error);\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CHALLENGE,\n \"Invalid or expired passkey challenge.\",\n );\n }\n if (!assertion.verifierAccepted) {\n throw convexError(\n ErrorCode.PASSKEY_INVALID_CHALLENGE,\n \"Invalid or expired passkey challenge.\",\n );\n }\n\n const authenticatorDataBytes = decodeBase64urlIgnorePadding(\n requireStringParam(params.authenticatorData, \"authenticatorData\"),\n );\n const authenticatorData = parseAuthenticatorData(authenticatorDataBytes);\n const backupState = parseBackupState(authenticatorDataBytes);\n const signatureBytes = decodeBase64urlIgnorePadding(\n requireStringParam(params.signature, \"signature\"),\n );\n const signatureMessage = createAssertionSignatureMessage(\n authenticatorDataBytes,\n clientDataJSON,\n );\n const messageHash = sha256(signatureMessage);\n\n verifyRpId(authenticatorData, rp.rpId);\n verifyUserFlags(authenticatorData, rp.authentication.userVerification);\n\n const passkey = assertion.passkey;\n\n verifyAssertionSignatureUniformly(passkey, signatureBytes, messageHash);\n if (provider.options.securityKeysOnly) {\n requireSecurityKeyCredential(passkey);\n requireSecurityKeyCredential({\n transports: passkey.transports,\n deviceType: backupState.deviceType,\n backedUp: backupState.backedUp,\n });\n }\n validateBackupEligibility(passkey.deviceType, backupState.deviceType);\n\n if (rp.registration.attestation) {\n await assertStoredAttestationTrusted(rp.registration.attestation, passkey.attestation);\n }\n\n if (\n (passkey.counter !== 0 || authenticatorData.signatureCounter !== 0) &&\n authenticatorData.signatureCounter <= passkey.counter\n ) {\n throw convexError(\n ErrorCode.PASSKEY_COUNTER_ERROR,\n \"Authenticator counter did not increase — possible credential cloning detected.\",\n );\n }\n\n const replaceSession = await getAuthSessionReplacement(ctx);\n let completed;\n try {\n completed = await mutatePasskeyCompleteAssertion(ctx, {\n id: passkey._id,\n counter: authenticatorData.signatureCounter,\n lastUsedAt: Date.now(),\n backedUp: backupState.backedUp,\n replaceSession,\n sessionExpirationTime: sessionExpirationTime(ctx.auth.config),\n refreshTokenExpirationTime: refreshTokenExpirationTime(ctx.auth.config),\n });\n } catch (error) {\n throw asConvexError(error, ErrorCode.INTERNAL_ERROR, \"Failed to finalize passkey sign-in.\");\n }\n if (completed.status === \"rejected\") {\n throw convexError(\n ErrorCode.PASSKEY_COUNTER_ERROR,\n \"Authenticator counter did not increase — possible credential cloning detected.\",\n );\n }\n\n return await finalizePasskeySession(ctx, completed);\n };\n\n const flowHandlers = {\n register: async () => {\n const rp = resolveRpOptions(provider);\n\n const challenge = new Uint8Array(32);\n crypto.getRandomValues(challenge);\n const challengeHash = encodeBase64urlNoPadding(new Uint8Array(sha256(challenge)));\n\n let registration;\n let userId: string;\n try {\n if (args.continuation === undefined) {\n userId = await requireAuthenticatedUserId(ctx);\n registration = await mutatePasskeyBeginRegistration(ctx, {\n userId,\n sessionId: (await getAuthSessionId(ctx)) ?? undefined,\n signature: challengeHash,\n expirationTime: Date.now() + rp.challengeExpirationMs,\n });\n } else {\n const rotation = await mutatePasskeyBeginRotation(ctx, {\n continuationId: args.continuation,\n provider: provider.id,\n signature: challengeHash,\n expirationTime: Date.now() + rp.challengeExpirationMs,\n });\n if (rotation.status === \"rejected\") {\n throw convexError(ErrorCode.CONTINUATION_INVALID, \"Invalid or expired continuation.\");\n }\n userId = rotation.userId;\n registration = rotation;\n }\n } catch (err) {\n logPasskeyError(err);\n throw convexError(ErrorCode.INTERNAL_ERROR, \"An unexpected error occurred.\");\n }\n const user = registration.user;\n const userName = params.userName ?? user?.email ?? \"user\";\n const userDisplayName = params.userDisplayName ?? user?.name ?? userName;\n const existing = registration.credentials;\n if (args.continuation === undefined && existing.length >= MAX_WEBAUTHN_CREDENTIALS_PER_USER) {\n throw convexError(\n ErrorCode.INVALID_PARAMETERS,\n `A user can register at most ${MAX_WEBAUTHN_CREDENTIALS_PER_USER} WebAuthn credentials.`,\n );\n }\n const excludeCredentials = existing.map((credential) => ({\n id: credential.id,\n transports: credential.transports,\n }));\n\n const userHandle = encodeBase64urlNoPadding(new TextEncoder().encode(userId));\n\n return {\n kind: \"webauthnOptions\" as const,\n options: {\n rp: { name: rp.rpName, id: rp.rpId },\n user: { id: userHandle, name: userName, displayName: userDisplayName },\n challenge: encodeBase64urlNoPadding(challenge),\n pubKeyCredParams: rp.registration.algorithms.map((alg) => ({\n type: \"public-key\" as const,\n alg,\n })),\n timeout: rp.challengeExpirationMs,\n ...(rp.registration.hints === undefined ? {} : { hints: rp.registration.hints }),\n attestation: rp.registration.attestation?.conveyance ?? \"none\",\n authenticatorSelection: {\n residentKey: rp.registration.residentKey,\n requireResidentKey: rp.registration.residentKey === \"required\",\n userVerification: rp.registration.userVerification,\n ...(rp.registration.authenticatorAttachment === undefined\n ? {}\n : { authenticatorAttachment: rp.registration.authenticatorAttachment }),\n },\n excludeCredentials,\n },\n verifier: registration.verifierId,\n ...(args.continuation === undefined\n ? {}\n : { continuation: args.continuation, operation: \"rotate\" as const }),\n };\n },\n\n signIn: async () => {\n const rp = resolveRpOptions(provider);\n const challenge = new Uint8Array(32);\n crypto.getRandomValues(challenge);\n const challengeHash = encodeBase64urlNoPadding(new Uint8Array(sha256(challenge)));\n\n let allowCredentials: AllowCredential[] | undefined;\n let credentialHints: string[] | undefined;\n const email = params.email === undefined ? undefined : normalizeEmail(params.email);\n const sessionId = (await getAuthSessionId(ctx)) ?? undefined;\n let signIn;\n try {\n signIn = await mutatePasskeyBeginSignIn(ctx, {\n sessionId,\n signature: challengeHash,\n expirationTime: Date.now() + rp.challengeExpirationMs,\n ...(email === undefined ? {} : { verifiedEmail: email }),\n });\n } catch (err) {\n logPasskeyError(err);\n throw convexError(ErrorCode.INTERNAL_ERROR, \"An unexpected error occurred.\");\n }\n\n if (email !== undefined) {\n const securityKeysOnly = provider.options.securityKeysOnly === true;\n const eligibleCredentials = selectAuthenticationCredentials(\n signIn.credentials,\n securityKeysOnly,\n );\n allowCredentials = await deriveEmailAllowCredentials(\n email,\n rp.rpId,\n eligibleCredentials,\n securityKeysOnly,\n );\n credentialHints = securityKeysOnly\n ? [\"security-key\"]\n : credentialAuthenticationHints(eligibleCredentials);\n }\n\n const hints = rp.authentication.hints ?? credentialHints;\n return {\n kind: \"webauthnOptions\" as const,\n options: {\n challenge: encodeBase64urlNoPadding(challenge),\n timeout: rp.challengeExpirationMs,\n rpId: rp.rpId,\n userVerification: rp.authentication.userVerification,\n ...(hints === undefined ? {} : { hints }),\n ...(allowCredentials === undefined ? {} : { allowCredentials }),\n },\n verifier: signIn.verifierId,\n };\n },\n\n verify: async () => {\n const isRegistration =\n typeof params.attestationObject === \"string\" && params.attestationObject.length > 0;\n const isAuthentication = typeof params.signature === \"string\" && params.signature.length > 0;\n\n if (isRegistration && !isAuthentication) {\n return await handleRegisterVerify();\n }\n if (isAuthentication && !isRegistration) {\n return await handleAuthVerify();\n }\n throw convexError(\n ErrorCode.PASSKEY_INVALID_VERIFY,\n \"`verify` flow requires either `attestationObject` (to complete a `register`) \" +\n \"or `signature` + `credentialId` (to complete a `signIn`).\",\n );\n },\n } satisfies Record<WebAuthnFlow, () => Promise<WebAuthnResult>>;\n\n const handler = flowHandlers[dispatch.flow];\n if (!handler) {\n throw convexError(ErrorCode.PASSKEY_MISSING_FLOW, `Unknown passkey flow: ${dispatch.flow}`);\n }\n return handler();\n}\n"],"mappings":"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"}
|
package/dist/shared/brand.d.ts
CHANGED
|
@@ -13,16 +13,12 @@ declare const brand: unique symbol;
|
|
|
13
13
|
type Brand<T, Tag extends string> = T & {
|
|
14
14
|
readonly [brand]: Tag;
|
|
15
15
|
};
|
|
16
|
+
/** Opaque JWT access token. */
|
|
17
|
+
type AccessToken = Brand<string, "AccessToken">;
|
|
16
18
|
/** Opaque encoded refresh token — `${refreshTokenId}|${sessionId}`. */
|
|
17
19
|
type RefreshToken = Brand<string, "RefreshToken">;
|
|
18
|
-
/**
|
|
19
|
-
* A one-way hash of some plaintext `TOf` (e.g. `Hashed<"Password">`,
|
|
20
|
-
* `Hashed<"ApiKeySecret">`, `Hashed<"VerificationCode">`). Distinct from the
|
|
21
|
-
* plaintext it was derived from so the two can't be swapped at a boundary.
|
|
22
|
-
*/
|
|
23
|
-
type Hashed<TOf extends string> = Brand<string, `Hashed:${TOf}`>;
|
|
24
20
|
/** AES-GCM ciphertext produced by `encryptSecret` (`${iv}.${payload}`). */
|
|
25
21
|
type EncryptedSecret = Brand<string, "EncryptedSecret">;
|
|
26
22
|
//#endregion
|
|
27
|
-
export {
|
|
23
|
+
export { AccessToken, EncryptedSecret, RefreshToken };
|
|
28
24
|
//# sourceMappingURL=brand.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brand.d.ts","names":[],"sources":["../../src/shared/brand.ts"],"mappings":";;;;;;;AAaA;;;cAHc,KAAA;;KAGF,KAAA,0BAA+B,CAAA;EAAA,UAAgB,KAAA,GAAQ,GAAA;AAAA;;
|
|
1
|
+
{"version":3,"file":"brand.d.ts","names":[],"sources":["../../src/shared/brand.ts"],"mappings":";;;;;;;AAaA;;;cAHc,KAAA;;KAGF,KAAA,0BAA+B,CAAA;EAAA,UAAgB,KAAA,GAAQ,GAAA;AAAA;;KAGvD,WAAA,GAAc,KAAA;;KAGd,YAAA,GAAe,KAAA;AAA3B;AAAA,KAaY,eAAA,GAAkB,KAAA"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Value } from "convex/values";
|
|
2
|
+
|
|
3
|
+
//#region src/shared/params.d.ts
|
|
4
|
+
/** Values accepted by provider sign-in parameter objects. */
|
|
5
|
+
type ProviderParams = Record<string, Value | undefined>;
|
|
6
|
+
/** Password-provider operations accepted by the stock client. */
|
|
7
|
+
type PasswordParams = {
|
|
8
|
+
flow: "signUp";
|
|
9
|
+
email: string;
|
|
10
|
+
password: string;
|
|
11
|
+
redirectTo?: string;
|
|
12
|
+
} | {
|
|
13
|
+
flow: "signIn";
|
|
14
|
+
email: string;
|
|
15
|
+
password: string;
|
|
16
|
+
redirectTo?: string;
|
|
17
|
+
} | {
|
|
18
|
+
flow: "reset";
|
|
19
|
+
email: string;
|
|
20
|
+
redirectTo?: string;
|
|
21
|
+
} | {
|
|
22
|
+
flow: "recover";
|
|
23
|
+
email: string;
|
|
24
|
+
code: string;
|
|
25
|
+
newPassword: string;
|
|
26
|
+
redirectTo?: string;
|
|
27
|
+
} | {
|
|
28
|
+
flow: "verify";
|
|
29
|
+
email: string;
|
|
30
|
+
code?: string;
|
|
31
|
+
redirectTo?: string;
|
|
32
|
+
} | {
|
|
33
|
+
flow: "change";
|
|
34
|
+
email: string;
|
|
35
|
+
currentPassword: string;
|
|
36
|
+
newPassword: string;
|
|
37
|
+
redirectTo?: string;
|
|
38
|
+
};
|
|
39
|
+
/** Email-provider initiation parameters. */
|
|
40
|
+
type EmailParams = {
|
|
41
|
+
email: string;
|
|
42
|
+
redirectTo?: string;
|
|
43
|
+
};
|
|
44
|
+
/** Phone-provider initiation parameters. */
|
|
45
|
+
type PhoneParams = {
|
|
46
|
+
phone: string;
|
|
47
|
+
redirectTo?: string;
|
|
48
|
+
};
|
|
49
|
+
/** Completion parameters for provider-owned verification codes. */
|
|
50
|
+
type CodeParams = {
|
|
51
|
+
code: string;
|
|
52
|
+
redirectTo?: string;
|
|
53
|
+
};
|
|
54
|
+
/** Group-connection discovery and selection parameters. */
|
|
55
|
+
type ConnectionParams = {
|
|
56
|
+
connectionId: string;
|
|
57
|
+
redirectTo?: string;
|
|
58
|
+
loginHint?: string;
|
|
59
|
+
} | {
|
|
60
|
+
email: string;
|
|
61
|
+
redirectTo?: string;
|
|
62
|
+
loginHint?: string;
|
|
63
|
+
} | {
|
|
64
|
+
domain: string;
|
|
65
|
+
redirectTo?: string;
|
|
66
|
+
loginHint?: string;
|
|
67
|
+
};
|
|
68
|
+
/** Anonymous-provider parameters. */
|
|
69
|
+
type AnonymousParams = {
|
|
70
|
+
redirectTo?: string;
|
|
71
|
+
};
|
|
72
|
+
/** OAuth-provider parameters. */
|
|
73
|
+
type OAuthParams = {
|
|
74
|
+
redirectTo?: string;
|
|
75
|
+
};
|
|
76
|
+
//#endregion
|
|
77
|
+
export { AnonymousParams, CodeParams, ConnectionParams, EmailParams, OAuthParams, PasswordParams, PhoneParams, ProviderParams };
|
|
78
|
+
//# sourceMappingURL=params.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"params.d.ts","names":[],"sources":["../../src/shared/params.ts"],"mappings":";;;;KAGY,cAAA,GAAiB,MAAA,SAAe,KAAA;AAA5C;AAAA,KAGY,cAAA;EACN,IAAA;EAAgB,KAAA;EAAe,QAAA;EAAkB,UAAA;AAAA;EACjD,IAAA;EAAgB,KAAA;EAAe,QAAA;EAAkB,UAAA;AAAA;EACjD,IAAA;EAAe,KAAA;EAAe,UAAA;AAAA;EAC9B,IAAA;EAAiB,KAAA;EAAe,IAAA;EAAc,WAAA;EAAqB,UAAA;AAAA;EACnE,IAAA;EAAgB,KAAA;EAAe,IAAA;EAAe,UAAA;AAAA;EAE9C,IAAA;EACA,KAAA;EACA,eAAA;EACA,WAAA;EACA,UAAA;AAAA;;KAIM,WAAA;EAAgB,KAAA;EAAe,UAAA;AAAA;;KAG/B,WAAA;EAAgB,KAAA;EAAe,UAAA;AAAA;;KAG/B,UAAA;EAAe,IAAA;EAAc,UAAA;AAAA;;KAG7B,gBAAA;EACN,YAAA;EAAsB,UAAA;EAAqB,SAAA;AAAA;EAC3C,KAAA;EAAe,UAAA;EAAqB,SAAA;AAAA;EACpC,MAAA;EAAgB,UAAA;EAAqB,SAAA;AAAA;;KAG/B,eAAA;EAAoB,UAAA;AAAA;AAAhC;AAAA,KAGY,WAAA;EAAgB,UAAA;AAAA"}
|
package/dist/shared/results.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AccessToken, RefreshToken } from "./brand.js";
|
|
1
2
|
import { Value } from "convex/values";
|
|
2
3
|
|
|
3
4
|
//#region src/shared/results.d.ts
|
|
@@ -5,6 +6,11 @@ import { Value } from "convex/values";
|
|
|
5
6
|
type AuthParameters = {
|
|
6
7
|
[key: string]: Value | undefined;
|
|
7
8
|
};
|
|
9
|
+
/** Access/refresh token pair issued on a successful sign-in. */
|
|
10
|
+
type AuthTokens = {
|
|
11
|
+
token: AccessToken;
|
|
12
|
+
refreshToken: RefreshToken;
|
|
13
|
+
};
|
|
8
14
|
/** Data needed to provision a TOTP authenticator (otpauth URI + shared secret). */
|
|
9
15
|
type TotpSetupPayload = {
|
|
10
16
|
uri: string;
|
|
@@ -66,5 +72,5 @@ type SignInDeviceCodeResult<TDeviceCode = DeviceCodePayload> = {
|
|
|
66
72
|
/** Discriminated union of every possible sign-in flow outcome, keyed by `kind`. */
|
|
67
73
|
type SignInFlowResult<TSession, TRedirect = string> = SignInSessionResult<TSession> | SignInRedirectResult<TRedirect> | SignInStartResult | SignInWebAuthnOptionsResult | SignInTotpChallengeResult | SignInTotpSetupResult | SignInDeviceCodeResult;
|
|
68
74
|
//#endregion
|
|
69
|
-
export { AuthParameters, DeviceCodePayload, SignInDeviceCodeResult, SignInFlowResult, SignInRedirectResult, SignInStartResult, SignInTotpChallengeResult, SignInTotpSetupResult, TotpSetupPayload };
|
|
75
|
+
export { AuthParameters, AuthTokens, DeviceCodePayload, SignInDeviceCodeResult, SignInFlowResult, SignInRedirectResult, SignInStartResult, SignInTotpChallengeResult, SignInTotpSetupResult, TotpSetupPayload };
|
|
70
76
|
//# sourceMappingURL=results.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"results.d.ts","names":[],"sources":["../../src/shared/results.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"results.d.ts","names":[],"sources":["../../src/shared/results.ts"],"mappings":";;;;;KAWY,cAAA;EAAA,CAAoB,GAAA,WAAc,KAAA;AAAA;;KAGlC,UAAA;EACV,KAAA,EAAO,WAAA;EACP,YAAA,EAAc,YAAA;AAAA;;KAIJ,gBAAA;EACV,GAAA;EACA,MAAA;EACA,MAAA;AAAA;;KAIU,iBAAA;EACV,UAAA;EACA,QAAA;EACA,eAAA;EACA,uBAAA;EACA,SAAA;EACA,QAAA;AAAA;;KAIU,mBAAA;EACV,IAAA;EACA,OAAA,EAAS,QAAA;AAAA;;KAIC,oBAAA;EACV,IAAA;EACA,QAAA,EAAU,SAAA;EACV,QAAA;AAAA;;KAIU,iBAAA;EACV,IAAA;AAAA;;KAIU,2BAAA;EAEN,IAAA;EACA,OAAA,EAAS,cAAA;EACT,QAAA;AAAA;EAGA,IAAA;EACA,OAAA,EAAS,cAAA;EACT,QAAA;EACA,YAAA;EACA,SAAA;AAAA;;KAIM,yBAAA;EACV,IAAA;EACA,QAAA;AAAA;;KAIU,qBAAA,cAAmC,gBAAA;EAC7C,IAAA;EACA,SAAA,EAAW,UAAA;EACX,QAAA;AAAA;;KAIU,sBAAA,eAAqC,iBAAA;EAC/C,IAAA;EACA,UAAA,EAAY,WAAA;AAAA;;KAIF,gBAAA,iCACR,mBAAA,CAAoB,QAAA,IACpB,oBAAA,CAAqB,SAAA,IACrB,iBAAA,GACA,2BAAA,GACA,yBAAA,GACA,qBAAA,GACA,sBAAA"}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
type
|
|
1
|
+
import type { AuthState, AuthSubscriber } from "../client/core/types";
|
|
2
|
+
/** The lifecycle methods every client binding needs, without widening `signIn`. */
|
|
3
|
+
type AuthClient = {
|
|
4
|
+
subscribe: (handler: AuthSubscriber) => () => void;
|
|
5
|
+
getSnapshot: () => AuthState;
|
|
6
|
+
signIn: unknown;
|
|
7
|
+
signOut: () => Promise<void>;
|
|
8
|
+
};
|
|
4
9
|
/** Reactive auth state bridged from the client's `subscribe`. Read its fields in markup. */
|
|
5
|
-
|
|
10
|
+
declare class ConvexAuth<Client extends AuthClient> {
|
|
6
11
|
#private;
|
|
7
|
-
constructor(client:
|
|
12
|
+
constructor(client: Client);
|
|
8
13
|
/** The discriminated auth state; narrow on `.status` to reach `token`. */
|
|
9
14
|
get state(): AuthState;
|
|
10
15
|
get status(): AuthState["status"];
|
|
@@ -12,15 +17,21 @@ export declare class ConvexAuth {
|
|
|
12
17
|
get signedOut(): boolean;
|
|
13
18
|
get loading(): boolean;
|
|
14
19
|
get token(): string | null;
|
|
15
|
-
get signIn():
|
|
16
|
-
get signOut():
|
|
20
|
+
get signIn(): Client["signIn"];
|
|
21
|
+
get signOut(): Client["signOut"];
|
|
17
22
|
/** The underlying imperative client, for factor flows (`totp`, `webauthn`, `device`). */
|
|
18
|
-
get client():
|
|
23
|
+
get client(): Client;
|
|
19
24
|
dispose(): void;
|
|
20
25
|
}
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Read reactive auth state for an inferred client.
|
|
28
|
+
*
|
|
29
|
+
* Call this with the same client in the root layout and in descendants. The
|
|
30
|
+
* first call creates the tree's subscription; later calls reuse it.
|
|
31
|
+
*
|
|
32
|
+
* @param client - The client returned by `client({ api: api.auth, ... })`.
|
|
33
|
+
* @returns Reactive state and exact provider-specific client methods.
|
|
34
|
+
*/
|
|
35
|
+
export declare function useConvexAuth<const Client extends AuthClient>(client: Client): ConvexAuth<Client>;
|
|
25
36
|
export {};
|
|
26
37
|
//# sourceMappingURL=context.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/context.svelte.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"context.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/context.svelte.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtE,mFAAmF;AACnF,KAAK,UAAU,GAAG;IAChB,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,MAAM,IAAI,CAAC;IACnD,WAAW,EAAE,MAAM,SAAS,CAAC;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AAIF,4FAA4F;AAC5F,cAAM,UAAU,CAAC,MAAM,SAAS,UAAU;;gBAK5B,MAAM,EAAE,MAAM;IAO1B,0EAA0E;IAC1E,IAAI,KAAK,IAAI,SAAS,CAErB;IACD,IAAI,MAAM,IAAI,SAAS,CAAC,QAAQ,CAAC,CAEhC;IACD,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACD,IAAI,SAAS,IAAI,OAAO,CAEvB;IACD,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,KAAK,IAAI,MAAM,GAAG,IAAI,CAEzB;IACD,IAAI,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,CAE7B;IACD,IAAI,OAAO,IAAI,MAAM,CAAC,SAAS,CAAC,CAE/B;IACD,yFAAyF;IACzF,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,OAAO,IAAI,IAAI;CAGhB;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,KAAK,CAAC,MAAM,SAAS,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAajG"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getContext, onDestroy, setContext } from "svelte";
|
|
2
|
-
const
|
|
2
|
+
const AUTH_CONTEXT = Symbol("convex-auth");
|
|
3
3
|
/** Reactive auth state bridged from the client's `subscribe`. Read its fields in markup. */
|
|
4
|
-
|
|
4
|
+
class ConvexAuth {
|
|
5
5
|
#client;
|
|
6
6
|
#unsubscribe;
|
|
7
7
|
#state = $state({ status: "loading", token: null });
|
|
@@ -44,18 +44,25 @@ export class ConvexAuth {
|
|
|
44
44
|
this.#unsubscribe();
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
/**
|
|
48
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Read reactive auth state for an inferred client.
|
|
49
|
+
*
|
|
50
|
+
* Call this with the same client in the root layout and in descendants. The
|
|
51
|
+
* first call creates the tree's subscription; later calls reuse it.
|
|
52
|
+
*
|
|
53
|
+
* @param client - The client returned by `client({ api: api.auth, ... })`.
|
|
54
|
+
* @returns Reactive state and exact provider-specific client methods.
|
|
55
|
+
*/
|
|
56
|
+
export function useConvexAuth(client) {
|
|
57
|
+
const existing = getContext(AUTH_CONTEXT);
|
|
58
|
+
if (existing !== undefined) {
|
|
59
|
+
if (existing.client !== client) {
|
|
60
|
+
throw new Error("Every useConvexAuth() call in one tree must use the same client.");
|
|
61
|
+
}
|
|
62
|
+
return existing;
|
|
63
|
+
}
|
|
49
64
|
const auth = new ConvexAuth(client);
|
|
50
65
|
onDestroy(() => auth.dispose());
|
|
51
|
-
setContext(
|
|
52
|
-
return auth;
|
|
53
|
-
}
|
|
54
|
-
/** Read the reactive auth shared by {@link setupConvexAuth} from a descendant component. */
|
|
55
|
-
export function useConvexAuth() {
|
|
56
|
-
const auth = getContext(AUTH_KEY);
|
|
57
|
-
if (auth === undefined) {
|
|
58
|
-
throw new Error("useConvexAuth() must be called under a component tree that ran setupConvexAuth().");
|
|
59
|
-
}
|
|
66
|
+
setContext(AUTH_CONTEXT, auth);
|
|
60
67
|
return auth;
|
|
61
68
|
}
|
package/dist/svelte/index.d.ts
CHANGED
|
@@ -1,5 +1,2 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { default as SignedIn } from "./SignedIn.svelte";
|
|
3
|
-
export { default as SignedOut } from "./SignedOut.svelte";
|
|
4
|
-
export { default as AuthLoading } from "./AuthLoading.svelte";
|
|
1
|
+
export { useConvexAuth } from "./context.svelte.js";
|
|
5
2
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/svelte/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/svelte/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/svelte/index.js
CHANGED
|
@@ -1,4 +1 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { default as SignedIn } from "./SignedIn.svelte";
|
|
3
|
-
export { default as SignedOut } from "./SignedOut.svelte";
|
|
4
|
-
export { default as AuthLoading } from "./AuthLoading.svelte";
|
|
1
|
+
export { useConvexAuth } from "./context.svelte.js";
|