@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
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["key","url"],"sources":["../../src/client/index.ts"],"sourcesContent":["/**\n * Framework-agnostic auth client for `@estifanos-sh/convex-auth/client`.\n *\n * Exposes the {@link client} factory, which wires auth tokens into a Convex\n * transport and returns `signIn`, `signOut`, `subscribe`, `getSnapshot`, and the\n * factor helpers enabled by the configured providers. Platform entrypoints\n * (`browser`, `expo`) layer concrete runtime defaults on top of this.\n *\n * @module\n */\n\nimport { ConvexError, Value } from \"convex/values\";\n\nimport { LOG_LEVELS, logMessage } from \"../shared/log\";\nimport { retryWithBackoff } from \"../shared/retry\";\nimport {\n createDeferred,\n type AuthApiRefs,\n type AuthClient,\n type AuthFlowContext,\n type AuthState,\n type AuthSnapshot,\n type AuthSubscriber,\n type OAuthCompletionResult,\n type ClientAdapterDeps,\n type ClientAdapterFactories,\n type ActionTransport,\n type ClientAdapters,\n type ClientRuntime,\n type ClientOptions,\n type ConvexTransport,\n type DeviceCodeResult,\n type HandshakeWaiter,\n type PendingInvite,\n type SignInActionResult,\n type SignInResult,\n} from \"./core/types\";\nimport type { AccessToken } from \"../shared/brand\";\nimport type { AuthParameters, AuthTokens } from \"../shared/results\";\nimport { createHandshakeError } from \"./errors\";\nimport { createDeviceClient } from \"./factors/device\";\nimport { createTotpClient } from \"./factors/totp\";\nimport { createInviteManager } from \"./runtime/invite\";\nimport { localMutex } from \"./runtime/mutex\";\nimport {\n isAuthRefreshRejection,\n isRetriableProxyRefreshError,\n isTransientNetworkError,\n parseProxyErrorBody,\n parseProxyResponseBody,\n ProxyRequestError,\n type ProxyErrorBody,\n} from \"./runtime/proxy\";\nimport { createStorageHelpers } from \"./runtime/storage\";\n\nexport type {\n AnonymousParams,\n AuthApiRefs,\n AuthClient,\n AuthState,\n ClientOptions,\n ClientRuntime,\n CodeCompletionParams,\n DeviceClient,\n DeviceCodeResult,\n DevicePollParams,\n DeviceVerifyParams,\n EmailInitiateParams,\n OAuthCompletionResult,\n OAuthSignInParams,\n ParamsForProvider,\n WebAuthnClient,\n WebAuthnRegisterOptions,\n WebAuthnSignInOptions,\n PasswordParams,\n PendingInvite,\n PlatformAuthClient,\n SignInOverloads,\n SignInResult,\n ConnectionParams,\n Storage,\n TotpClient,\n TotpConfirmParams,\n TotpSetupOptions,\n TotpSetupResult,\n TotpVerifyParams,\n} from \"./core/types\";\n\nconst VERIFIER_STORAGE_KEY = \"__convexAuthOAuthVerifier\";\nconst JWT_STORAGE_KEY = \"__convexAuthJWT\";\nconst REFRESH_TOKEN_STORAGE_KEY = \"__convexAuthRefreshToken\";\nconst INVITE_TOKEN_KEY = \"__convexAuthPendingInvite\";\nconst INVITE_EMAIL_KEY = \"__convexAuthPendingInviteEmail\";\n\nconst RETRY_BASE_MS = 500;\nconst RETRY_MAX_RETRIES = 2;\nconst DEFAULT_AUTH_HANDSHAKE_TIMEOUT_MS = 5000;\nconst FORCED_REFRESH_REUSE_MS = 10000;\n\nconst retryWithJitteredBackoff = <T>(\n fn: () => Promise<T>,\n shouldRetry: (error: unknown) => boolean,\n): Promise<T> =>\n retryWithBackoff(fn, {\n maxRetries: RETRY_MAX_RETRIES,\n baseMs: RETRY_BASE_MS,\n jitterMode: \"centered\",\n shouldRetry,\n });\n\n/**\n * Resolve the Convex deployment URL from the client.\n *\n * `ConvexReactClient` exposes `.url` directly.\n * `ConvexClient` exposes `.client.url` via `BaseConvexClient`.\n *\n * @param convex - The Convex transport to read the deployment URL from.\n * @param explicit - An explicit URL that short-circuits discovery.\n * @returns The resolved deployment URL.\n * @throws {Error} When no URL can be determined and `explicit` is not provided.\n * @internal\n */\nexport function resolveUrl(convex: ConvexTransport, explicit?: string): string {\n if (explicit) return explicit;\n const candidate = convex as {\n url?: unknown;\n client?: { url?: unknown } | null;\n };\n const client =\n typeof candidate.client === \"object\" && candidate.client !== null\n ? candidate.client\n : undefined;\n const url: unknown = candidate.url ?? client?.url;\n if (typeof url === \"string\") return url;\n throw new Error(\"Could not determine Convex deployment URL. Pass `url` explicitly.\");\n}\n\nconst STABLE_STRINGIFY_MAX_DEPTH = 32;\n\nfunction stableStringify(value: unknown, depth = 0): string {\n if (depth > STABLE_STRINGIFY_MAX_DEPTH) {\n throw new Error(\"stableStringify: input exceeds maximum nesting depth\");\n }\n if (value === undefined) {\n return \"null\";\n }\n if (Array.isArray(value)) {\n return `[${value.map((item) => stableStringify(item, depth + 1)).join(\",\")}]`;\n }\n if (value !== null && typeof value === \"object\") {\n const entries = Object.entries(value)\n .filter(([, entryValue]) => entryValue !== undefined)\n .sort(([left], [right]) => left.localeCompare(right));\n return `{${entries\n .map(\n ([key, entryValue]) => `${JSON.stringify(key)}:${stableStringify(entryValue, depth + 1)}`,\n )\n .join(\",\")}}`;\n }\n return JSON.stringify(value);\n}\n\nfunction buildSignInRequestKey(\n provider: string | undefined,\n params: Record<string, Value>,\n): string {\n return stableStringify({ provider: provider ?? null, params });\n}\n\nfunction formDataEntries(formData: unknown): Iterable<[string, string | { name: string }]> {\n return formData as Iterable<[string, string | { name: string }]>;\n}\n\nfunction resolveExplicitToken(value: string | null | undefined): AccessToken | null | undefined {\n if (value === undefined) return undefined;\n if (value === null) return null;\n // A cleared auth cookie surfaces as an empty (or whitespace-only) string\n // during SSR. Treat it as an explicit signed-out seed instead of throwing and\n // crashing the render.\n if (value.trim().length === 0) return null;\n if (value.trim() !== value) {\n throw new Error(\"The `token` option must not include leading or trailing whitespace.\");\n }\n return value as AccessToken;\n}\n\n/**\n * Decode the `sub` (subject / user id) claim from a JWT access token without\n * verifying its signature. Used only to compare identities across browser tabs\n * — never for authorization. Returns `null` when the token cannot be decoded.\n */\nfunction decodeJwtSubject(jwt: string): string | null {\n try {\n const payloadSegment = jwt.split(\".\")[1];\n if (payloadSegment === undefined || payloadSegment.length === 0) {\n return null;\n }\n const normalized = payloadSegment.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = normalized + \"=\".repeat((4 - (normalized.length % 4)) % 4);\n if (typeof atob !== \"function\") {\n return null;\n }\n const claims = JSON.parse(atob(padded)) as { sub?: unknown };\n return typeof claims.sub === \"string\" ? claims.sub : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Create a framework-agnostic auth client.\n *\n * Returns an object with `signIn`, `signOut`, `subscribe`, `getSnapshot`, and any\n * factor helpers enabled by your configured providers. Platform-specific\n * WebAuthn support is added by higher-level entrypoints such as\n * `@estifanos-sh/convex-auth/browser`.\n *\n * ### SPA mode (default)\n *\n * ```ts\n * import { ConvexClient } from 'convex/browser';\n * import { client } from '@estifanos-sh/convex-auth/client';\n * import { api } from '../convex/_generated/api';\n *\n * const convex = new ConvexClient(CONVEX_URL);\n * const auth = client({ convex, api: api.auth });\n * ```\n *\n * ### Proxy mode\n *\n * ```ts\n * const auth = client({\n * convex,\n * proxyPath: '/api/auth',\n * runtime: myRuntime,\n * });\n * ```\n *\n * In proxy mode all auth operations go through the injected proxy runtime.\n * Tokens are stored in httpOnly cookies server-side — the client\n * holds the JWT in memory only.\n *\n * The client resolves auth through a Convex confirmation handshake. For SSR,\n * pass the server-known token via `token` so hydration starts from the same\n * resolved state. The returned client also auto-wires its tokens into the\n * passed Convex client via `setAuth`, so queries and mutations are\n * authenticated without further configuration.\n *\n * While a sign-in handshake is pending, a transient `false` from the Convex\n * client does not reject the session: Convex can emit `false` mid-reauth and a\n * subsequent `true` confirms the same session. Rejection happens only on\n * timeout or when the token actually changes or clears.\n *\n * @param options - Client configuration. See {@link ClientOptions}.\n * @typeParam Api - An AuthApiRefs type determining which factor helpers are available.\n * @returns Auth client with conditional `totp` and `device` helpers.\n * @throws {Error} When the Convex deployment URL cannot be determined and `url` is not passed explicitly.\n * @throws {Error} When `proxyPath` is not set and the `api` option is missing.\n * @throws {Error} When `proxyPath` is set and `runtime.proxy` is missing.\n * @throws {Error} When `token` has leading or trailing whitespace. An empty or\n * whitespace-only string is treated as an explicit signed-out seed, not an error.\n */\nexport function client<Api extends AuthApiRefs<boolean, boolean, boolean> = AuthApiRefs>(\n options: ClientOptions<Api>,\n): AuthClient<Api> {\n const { convex, proxyPath, api: apiRefs } = options;\n const proxy = proxyPath;\n const runtime: ClientRuntime = options.runtime ?? {};\n const adapters: ClientAdapters = options.adapters ?? {};\n const adapterFactories: ClientAdapterFactories = options.adapterFactories ?? {};\n\n function requireProxyRuntime() {\n if (!runtime.proxy) {\n throw new Error(\n \"The `runtime.proxy` option is required when `proxyPath` is set. \" +\n \"Use `@estifanos-sh/convex-auth/browser` for browser defaults or inject a proxy runtime explicitly.\",\n );\n }\n return runtime.proxy;\n }\n\n function requireApiRefs() {\n if (!apiRefs) {\n throw new Error(\n \"The `api` option is required when `proxyPath` is not set. \" + \"Pass { api: api.auth }.\",\n );\n }\n return apiRefs;\n }\n\n function requireHttpClient() {\n if (!httpClient) {\n throw new Error(\n \"The `httpClient` option is required when `proxyPath` is not set in a non-browser runtime. \" +\n \"Use `@estifanos-sh/convex-auth/browser` for browser defaults or pass an action-only transport explicitly.\",\n );\n }\n return httpClient;\n }\n\n const storage =\n options.storage !== undefined\n ? options.storage\n : runtime.storage !== undefined\n ? runtime.storage\n : null;\n const proxyRuntime = proxy ? requireProxyRuntime() : null;\n\n function getLocation(): URL | null {\n if (typeof options.location === \"function\") return options.location();\n if (options.location instanceof URL) return options.location;\n if (runtime.location) return runtime.location.get();\n return null;\n }\n\n /**\n * SSR-safe URL parameter reader.\n *\n * Uses the injected location source when provided.\n *\n * @param name - The query parameter name.\n * @returns The parameter value, or `null` if not present or in SSR.\n *\n * @example\n * ```ts\n * const workspaceId = auth.param(\"workspace\");\n * const tab = auth.param(\"tab\") ?? \"issues\";\n * ```\n */\n function param(name: string): string | null {\n const loc = getLocation();\n return loc?.searchParams.get(name) ?? null;\n }\n\n function cleanUrlParams(params: string[]) {\n const loc = getLocation();\n if (!loc) return;\n if (!runtime.location) return;\n const searchParams = new URLSearchParams(loc.search);\n let changed = false;\n for (const p of params) {\n if (searchParams.has(p)) {\n searchParams.delete(p);\n changed = true;\n }\n }\n if (changed) {\n const next = searchParams.toString() ? `${loc.pathname}?${searchParams}` : loc.pathname;\n void runtime.location.replace(next);\n }\n }\n\n const url = proxy ? undefined : resolveUrl(convex, options.url);\n const escapedNamespace = (proxy ?? url!)\n .replace(/^https?:\\/\\//, \"\")\n .replace(/[^a-zA-Z0-9]/g, \"_\");\n const key = (name: string) => `${name}_${escapedNamespace}`;\n const {\n get: storageGet,\n set: storageSet,\n remove: storageRemove,\n } = createStorageHelpers({ storage, key });\n const withAuthStorageMutex = async <T>(callback: () => Promise<T>): Promise<T> => {\n const mutexKey = `__convexAuthStorage_${escapedNamespace}`;\n return runtime.mutex\n ? await runtime.mutex.withKey(mutexKey, callback)\n : await localMutex(mutexKey, callback);\n };\n const proxyFetch = async (body: AuthParameters): Promise<Value> => {\n if (!proxy) {\n throw new Error(\"Proxy fetch requested without proxyPath.\");\n }\n const response = await proxyRuntime!.fetch(body, proxy);\n if (!response.ok) {\n let errorBody: ProxyErrorBody = {};\n try {\n errorBody = parseProxyErrorBody(await response.json());\n } catch {\n errorBody = {};\n }\n if (errorBody.authError !== undefined) {\n throw new ConvexError(errorBody.authError);\n }\n throw new ProxyRequestError(response.status, errorBody.error);\n }\n try {\n return parseProxyResponseBody(await response.json());\n } catch {\n throw new Error(\"Proxy response was not valid JSON\");\n }\n };\n const subscribers = new Set<() => void>();\n let disposeStorageListener: (() => void) | null = null;\n\n const httpClient: ActionTransport | null = proxy ? null : (options.httpClient ?? null);\n\n const readInitialToken = (): AccessToken | null => {\n if (!storage) return null;\n try {\n const raw = storage.getItem(key(JWT_STORAGE_KEY));\n return typeof raw === \"string\" && raw.length > 0 ? (raw as AccessToken) : null;\n } catch {\n return null;\n }\n };\n const explicitToken = resolveExplicitToken(options.token);\n const tokenProvided = explicitToken !== undefined;\n const serverToken: AccessToken | null = tokenProvided ? explicitToken : readInitialToken();\n const hasServerToken = serverToken !== null;\n\n const handshakeTimeoutMs =\n typeof options.handshakeTimeoutMs === \"number\" && options.handshakeTimeoutMs > 0\n ? options.handshakeTimeoutMs\n : DEFAULT_AUTH_HANDSHAKE_TIMEOUT_MS;\n\n let token: AccessToken | null = serverToken;\n let isLoading = !hasServerToken && !tokenProvided;\n let authConfirmed = hasServerToken;\n let handshakePending = false;\n let authEpoch = 0;\n let lastForcedRefreshTime = 0;\n let forcedRefreshPromise: Promise<string | null> | null = null;\n let destroyed = false;\n // Bounded grace window for a confirmed session that Convex reports as `false`.\n // A transient mid-rotation `false` is followed by a confirming `true` (which\n // clears the timer); a real server-side deauth (revoke/expiry) leaves it to\n // fire and sign out. The timer callback pins itself to the token epoch it was\n // armed for so a concurrent rotation cannot trigger a spurious sign-out.\n let deauthGraceTimer: ReturnType<typeof setTimeout> | null = null;\n let activeSignIn: {\n key: string;\n promise: Promise<SignInResult>;\n } | null = null;\n let initializePromise: Promise<void> | null = null;\n const handshakeWaiters = new Set<HandshakeWaiter>();\n let snapshot: AuthSnapshot = {\n phase: hasServerToken ? \"authenticated\" : isLoading ? \"loading\" : \"unauthenticated\",\n isLoading,\n isAuthenticated: hasServerToken,\n token,\n };\n\n const computePublic = (): AuthState => {\n if (snapshot.token !== null && snapshot.phase === \"authenticated\") {\n return { status: \"signedIn\", token: snapshot.token };\n }\n if (snapshot.phase === \"loading\" || snapshot.phase === \"handshake\") {\n return { status: \"loading\", token: null };\n }\n return { status: \"signedOut\", token: null };\n };\n let publicSnapshot: AuthState = computePublic();\n const authStatesEqual = (left: AuthState, right: AuthState) =>\n left.status === right.status && left.token === right.token;\n const refreshPublic = (): boolean => {\n const next = computePublic();\n if (authStatesEqual(publicSnapshot, next)) {\n return false;\n }\n publicSnapshot = next;\n return true;\n };\n\n const settleHandshakeWaiters = (\n epoch: number,\n outcome: { type: \"resolve\" } | { type: \"reject\"; error: ConvexError<Value> },\n ) => {\n for (const waiter of Array.from(handshakeWaiters)) {\n if (waiter.epoch !== epoch) {\n continue;\n }\n clearTimeout(waiter.timeoutId);\n handshakeWaiters.delete(waiter);\n if (outcome.type === \"resolve\") {\n waiter.deferred.resolve(undefined);\n } else {\n waiter.deferred.reject(outcome.error);\n }\n }\n };\n\n const rejectObsoleteHandshakeWaiters = (activeEpoch: number) => {\n for (const waiter of Array.from(handshakeWaiters)) {\n if (waiter.epoch >= activeEpoch) {\n continue;\n }\n clearTimeout(waiter.timeoutId);\n handshakeWaiters.delete(waiter);\n waiter.deferred.reject(\n createHandshakeError(\"AUTH_HANDSHAKE_REJECTED\", {\n ...waiter.context,\n reason: \"token_changed\",\n }),\n );\n }\n };\n\n const rejectAllHandshakeWaiters = (error: ConvexError<Value>) => {\n for (const waiter of Array.from(handshakeWaiters)) {\n clearTimeout(waiter.timeoutId);\n handshakeWaiters.delete(waiter);\n waiter.deferred.reject(error);\n }\n };\n\n const clearDeauthGrace = () => {\n if (deauthGraceTimer !== null) {\n clearTimeout(deauthGraceTimer);\n deauthGraceTimer = null;\n }\n };\n\n /**\n * Arm the bounded re-handshake window after Convex reports a confirmed\n * session as unauthenticated. The public snapshot is intentionally left on\n * the confirmed state so a transient mid-rotation `false` does not flicker the\n * UI; only if no confirming `true` arrives before the window elapses (and the\n * token identity is unchanged) do we sign out.\n */\n const startDeauthGrace = () => {\n if (deauthGraceTimer !== null) {\n return;\n }\n const epoch = authEpoch;\n deauthGraceTimer = setTimeout(() => {\n deauthGraceTimer = null;\n if (destroyed || authEpoch !== epoch || !authConfirmed || token === null) {\n return;\n }\n void setToken({ shouldStore: true, tokens: null }).catch((error) => {\n logMessage(\"convex-auth/client\", LOG_LEVELS.ERROR, [\n \"[convex-auth] Sign-out after server-side deauthentication failed:\",\n error,\n ]);\n });\n }, handshakeTimeoutMs);\n };\n\n const waitForAuthHandshake = async (context: AuthFlowContext) => {\n if (token === null) {\n return;\n }\n if (authConfirmed && !handshakePending) {\n return;\n }\n if (!handshakePending) {\n throw createHandshakeError(\"AUTH_HANDSHAKE_REJECTED\", {\n ...context,\n reason: \"auth_rejected\",\n });\n }\n\n const epoch = authEpoch;\n const deferred = createDeferred<void, ConvexError<Value>>();\n const waiter: HandshakeWaiter = {\n epoch,\n context,\n deferred,\n timeoutId: setTimeout(() => {\n handshakeWaiters.delete(waiter);\n deferred.reject(\n createHandshakeError(\"AUTH_HANDSHAKE_TIMEOUT\", {\n ...context,\n timeoutMs: handshakeTimeoutMs,\n }),\n );\n // Unstick the state machine: a never-confirmed handshake that times out\n // must not leave `handshakePending` set (which would spin the UI in\n // `loading` forever). Remove only the unconfirmed access token from\n // storage, preserving the refresh credential so a later reload can\n // recover. Keeping the stale JWT made the next client boot report\n // signed-in synchronously, skip refresh hydration, and then discard the\n // still-valid refresh token when Convex rejected that JWT.\n if (!destroyed && authEpoch === epoch && token !== null && handshakePending) {\n const timedOutToken = token;\n void withAuthStorageMutex(async () => {\n if (destroyed || authEpoch !== epoch || token !== timedOutToken || !handshakePending) {\n return;\n }\n await storageRemove(JWT_STORAGE_KEY);\n // Re-check after async storage I/O: a newer sign-in may have changed\n // the in-memory epoch while waiting. Its queued storage write must\n // win and its token must not be cleared here.\n if (destroyed || authEpoch !== epoch || token !== timedOutToken || !handshakePending) {\n return;\n }\n await setToken({ shouldStore: false, tokens: null });\n }).catch((error) => {\n logMessage(\"convex-auth/client\", LOG_LEVELS.ERROR, [\n \"[convex-auth] Sign-out after handshake timeout failed:\",\n error,\n ]);\n });\n }\n }, handshakeTimeoutMs),\n };\n handshakeWaiters.add(waiter);\n try {\n await deferred.promise;\n } finally {\n clearTimeout(waiter.timeoutId);\n handshakeWaiters.delete(waiter);\n }\n };\n\n const handleConvexAuthChange = (isAuthenticated: boolean) => {\n if (destroyed) {\n return;\n }\n\n if (isAuthenticated) {\n authConfirmed = true;\n handshakePending = false;\n clearDeauthGrace();\n settleHandshakeWaiters(authEpoch, { type: \"resolve\" });\n } else if (token === null || !authConfirmed) {\n authConfirmed = false;\n if (token !== null) {\n handshakePending = true;\n }\n } else {\n // A previously confirmed session (`token !== null && authConfirmed`) is\n // now reported unauthenticated. This is ambiguous: it may be a transient\n // mid-rotation `false` (a confirming `true` follows shortly) or a real\n // server-side deauthentication (revoke/expiry). Keep the confirmed\n // snapshot for now and arm a bounded grace window that signs out only if\n // no `true` re-confirms in time.\n startDeauthGrace();\n }\n\n if (updateSnapshot()) {\n notify();\n }\n };\n\n const notify = () => {\n refreshPublic();\n for (const cb of subscribers) cb();\n };\n\n const updateSnapshot = () => {\n const tag =\n token !== null && handshakePending\n ? \"handshake\"\n : isLoading\n ? \"loading\"\n : token !== null && authConfirmed\n ? \"authenticated\"\n : \"unauthenticated\";\n\n const phase = tag as AuthSnapshot[\"phase\"];\n\n const next: AuthSnapshot = {\n phase,\n isLoading: phase === \"loading\" || phase === \"handshake\",\n isAuthenticated: phase === \"authenticated\",\n token,\n };\n if (\n snapshot.phase === next.phase &&\n snapshot.isLoading === next.isLoading &&\n snapshot.isAuthenticated === next.isAuthenticated &&\n snapshot.token === next.token\n ) {\n return false;\n }\n snapshot = next;\n return true;\n };\n\n const finalizeLoadingState = () => {\n if (!isLoading) {\n return;\n }\n isLoading = false;\n if (updateSnapshot()) {\n notify();\n }\n };\n\n const inviteManager = createInviteManager({\n param,\n storageGet,\n storageSet,\n storageRemove,\n cleanUrlParams,\n tokenKey: INVITE_TOKEN_KEY,\n emailKey: INVITE_EMAIL_KEY,\n });\n const getPendingInvite = () => inviteManager.getPendingInvite();\n const persistInvite = () => inviteManager.persistInvite();\n const acceptInvite = () => inviteManager.acceptInvite();\n\n const bindConvexAuth = () => {\n convex.setAuth(fetchAccessToken, handleConvexAuthChange);\n };\n\n const setToken = async (\n args:\n | {\n shouldStore: true;\n tokens: AuthTokens | null;\n requireHandshake?: boolean;\n resyncConvexAuth?: boolean;\n }\n | {\n shouldStore: false;\n tokens: { token: AccessToken } | null;\n requireHandshake?: boolean;\n resyncConvexAuth?: boolean;\n },\n ) => {\n const previousToken = token;\n const nextToken = args.tokens === null ? null : args.tokens.token;\n\n // Assign the token and bump the epoch synchronously — before any storage\n // I/O await — so `authEpoch` and `token` never disagree within a tick. The\n // forced-refresh race guard depends on this: a concurrent `signOut` that\n // clears the token must be observable by an in-flight refresh the instant\n // it happens, not only after the storage write resolves.\n token = nextToken;\n const tokenChanged = token !== previousToken;\n if (tokenChanged) {\n authEpoch += 1;\n rejectObsoleteHandshakeWaiters(authEpoch);\n clearDeauthGrace();\n }\n\n if (args.tokens === null) {\n lastForcedRefreshTime = 0;\n }\n if (args.shouldStore) {\n await withAuthStorageMutex(async () => {\n if (args.tokens === null) {\n await storageRemove(JWT_STORAGE_KEY);\n await storageRemove(REFRESH_TOKEN_STORAGE_KEY);\n } else if (\"refreshToken\" in args.tokens) {\n await storageSet(JWT_STORAGE_KEY, args.tokens.token);\n await storageSet(REFRESH_TOKEN_STORAGE_KEY, args.tokens.refreshToken);\n }\n });\n }\n\n if (token === null) {\n authConfirmed = false;\n handshakePending = false;\n settleHandshakeWaiters(authEpoch, {\n type: \"reject\",\n error: createHandshakeError(\"AUTH_HANDSHAKE_REJECTED\", {\n reason: \"token_cleared\",\n }),\n });\n } else {\n const keepConfirmedAuth =\n authConfirmed && args.resyncConvexAuth === false && args.requireHandshake !== true;\n const shouldEnterHandshake =\n !keepConfirmedAuth && (args.requireHandshake === true || tokenChanged || !authConfirmed);\n if (shouldEnterHandshake) {\n authConfirmed = false;\n handshakePending = true;\n } else {\n handshakePending = false;\n }\n }\n\n const hadPendingLoad = isLoading;\n isLoading = false;\n const changed = updateSnapshot();\n if (args.resyncConvexAuth !== false) {\n bindConvexAuth();\n }\n if (hadPendingLoad || changed) {\n notify();\n }\n };\n\n const setTokenAndMaybeWait = async (\n args:\n | {\n shouldStore: true;\n tokens: AuthTokens | null;\n waitForHandshake: boolean;\n context: AuthFlowContext;\n }\n | {\n shouldStore: false;\n tokens: { token: AccessToken } | null;\n waitForHandshake: boolean;\n context: AuthFlowContext;\n },\n ): Promise<boolean> => {\n const { waitForHandshake, context, ...tokenArgs } = args;\n await setToken({\n ...(tokenArgs as\n | { shouldStore: true; tokens: AuthTokens | null }\n | { shouldStore: false; tokens: { token: AccessToken } | null }),\n requireHandshake: waitForHandshake,\n });\n if (tokenArgs.tokens === null) {\n return false;\n }\n if (waitForHandshake) {\n await waitForAuthHandshake(context);\n }\n return true;\n };\n\n const adapterDeps: ClientAdapterDeps = {\n proxy,\n convex,\n requireApiRefs,\n proxyFetch,\n setTokenAndMaybeWait,\n };\n const webauthnAdapter = adapters.webauthn ?? adapterFactories.webauthn?.(adapterDeps);\n\n const verifyCode = async (\n args: { code: string; verifier?: string } | { refreshToken: string },\n ) => {\n return retryWithJitteredBackoff(\n () =>\n requireHttpClient().action(\n requireApiRefs().signIn,\n \"code\" in args ? { params: { code: args.code }, verifier: args.verifier } : args,\n ) as Promise<SignInActionResult>,\n isTransientNetworkError,\n );\n };\n\n const normalizeDeviceCodeResult = (device_code: unknown): DeviceCodeResult => {\n const input = device_code as {\n deviceCode: string;\n userCode: string;\n verification_uri?: string;\n verificationUri?: string;\n verification_uri_complete?: string;\n verificationUriComplete?: string;\n expiresIn: number;\n interval: number;\n };\n return {\n deviceCode: input.deviceCode,\n userCode: input.userCode,\n verificationUri: input.verification_uri ?? input.verificationUri ?? \"\",\n verificationUriComplete:\n input.verification_uri_complete ?? input.verificationUriComplete ?? \"\",\n expiresIn: input.expiresIn,\n interval: input.interval,\n };\n };\n\n const isSignedInResult = (\n result: SignInActionResult,\n ): result is Extract<SignInActionResult, { kind: \"signedIn\" }> => result.kind === \"signedIn\";\n\n /**\n * Hand an OAuth authorization URL to the platform launcher.\n *\n * Browser-style runtimes navigate away and return nothing, so the caller\n * receives the `redirect` result. Session-style runtimes (Expo) return the\n * callback URL, which is completed inline and resolves to `signedIn`.\n */\n const launchOAuth = async (redirect: URL, verifier: string): Promise<SignInResult> => {\n const redirectResult = { kind: \"redirect\" as const, redirect, verifier } satisfies SignInResult;\n if (!runtime.oauth) {\n return redirectResult;\n }\n const callback = await runtime.oauth.open(redirect);\n if (callback === undefined) {\n return redirectResult;\n }\n const completion = await completeOAuth(callback);\n return completion.handled ? { kind: \"signedIn\" as const } : redirectResult;\n };\n\n /**\n * Sign in with a provider.\n *\n * @param provider - Provider ID (e.g. `\"email\"`, `\"password\"`, `\"google\"`).\n * Omit when exchanging an OAuth code (the code carries the provider info).\n * @param args - Provider-specific arguments. Pass a `Record<string, Value>`\n * or `FormData`. Common fields: `email`, `password`, `code`, `redirectTo`.\n * @returns A {@link SignInResult} indicating the outcome.\n * @throws {ConvexError} When the server action rejects the sign-in attempt (e.g. invalid credentials, provider error, or rate limiting).\n *\n * @example Email magic link\n * ```ts\n * await auth.signIn('email', { email: 'user@example.com' });\n * ```\n *\n * @example Password\n * ```ts\n * const result = await auth.signIn('password', { email, password, flow: 'signIn' });\n * if (result.kind === 'totpRequired') {\n * await auth.totp.verify({ code: totpCode, verifier: result.verifier });\n * }\n * ```\n *\n * @example OAuth (returns a redirect URL)\n * ```ts\n * const result = await auth.signIn('google');\n * if (result.kind === 'redirect') {\n * window.location.href = result.redirect.toString();\n * }\n * ```\n */\n const signIn = async (\n provider?: string,\n args?: FormData | Record<string, Value>,\n ): Promise<SignInResult> => {\n if (destroyed) {\n throw new Error(\"Convex auth client has been destroyed.\");\n }\n await persistInvite();\n\n const rawParams =\n args instanceof FormData\n ? (() => {\n const formParams: Record<string, Value> = {};\n for (const [key, value] of formDataEntries(args)) {\n if (typeof value === \"string\") {\n formParams[key] = value;\n }\n }\n return formParams;\n })()\n : (args ?? {});\n const params: Record<string, Value> =\n options.oauthRedirectTo !== undefined && rawParams.redirectTo === undefined\n ? { ...rawParams, redirectTo: options.oauthRedirectTo }\n : rawParams;\n const flow = typeof params.flow === \"string\" && params.flow.length > 0 ? params.flow : \"signIn\";\n const signInKey = buildSignInRequestKey(provider, params);\n\n const handleSignInActionResult = async (\n result: SignInActionResult,\n resultOptions: { shouldStore: boolean; persistVerifier: boolean },\n ): Promise<SignInResult> => {\n if (result.kind === \"redirect\") {\n const redirectUrl = new URL(result.redirect);\n if (resultOptions.persistVerifier) {\n await storageSet(VERIFIER_STORAGE_KEY, result.verifier);\n }\n return {\n kind: \"redirect\" as const,\n redirect: redirectUrl,\n verifier: result.verifier,\n } satisfies SignInResult;\n }\n\n if (result.kind === \"totpRequired\") {\n return {\n kind: \"totpRequired\" as const,\n verifier: result.verifier,\n } satisfies SignInResult;\n }\n\n if (result.kind === \"deviceCode\") {\n return {\n kind: \"deviceCode\" as const,\n deviceCode: normalizeDeviceCodeResult(result.deviceCode),\n } satisfies SignInResult;\n }\n\n if (result.kind === \"webauthnOptions\" && \"operation\" in result) {\n if (!webauthnAdapter) {\n throw new Error(\"This client does not support WebAuthn continuations.\");\n }\n return await webauthnAdapter.completeRegistration(result);\n }\n\n if (result.kind === \"signedIn\") {\n const signingIn = await setTokenAndMaybeWait(\n resultOptions.shouldStore\n ? {\n shouldStore: true as const,\n tokens: result.session,\n waitForHandshake: true,\n context: { provider, flow },\n }\n : {\n shouldStore: false as const,\n tokens: result.session === null ? null : { token: result.session.token },\n waitForHandshake: true,\n context: { provider, flow },\n },\n );\n return signingIn\n ? ({ kind: \"signedIn\" as const } satisfies SignInResult)\n : ({ kind: \"started\" as const } satisfies SignInResult);\n }\n\n return { kind: \"started\" as const } satisfies SignInResult;\n };\n\n if (activeSignIn !== null) {\n if (activeSignIn.key === signInKey) {\n // Share the entire in-flight flow — INCLUDING any OAuth launch — so a\n // concurrent same-key signIn observes the single launch's outcome\n // instead of opening a second browser / native auth session.\n return await activeSignIn.promise;\n }\n throw new Error(\"Another sign-in flow is already in progress.\");\n }\n\n const runSignInAction = async (): Promise<SignInResult> => {\n if (proxy) {\n const result = (await proxyFetch({\n action: \"auth:signIn\",\n args: { provider, params },\n })) as SignInActionResult;\n return await handleSignInActionResult(result, {\n shouldStore: false,\n persistVerifier: false,\n });\n }\n\n const verifier = (await storageGet(VERIFIER_STORAGE_KEY)) ?? undefined;\n try {\n const result = (await convex.action(requireApiRefs().signIn, {\n provider,\n params,\n verifier,\n })) as SignInActionResult;\n if (params.code !== undefined) {\n await storageRemove(VERIFIER_STORAGE_KEY);\n }\n return await handleSignInActionResult(result, {\n shouldStore: true,\n persistVerifier: true,\n });\n } catch (error) {\n if (params.code !== undefined) {\n const convexCode =\n error instanceof ConvexError && typeof error.data?.code === \"string\"\n ? error.data.code\n : null;\n if (\n convexCode !== null &&\n [\"INVALID_VERIFICATION_CODE\", \"INVALID_VERIFIER\"].includes(convexCode)\n ) {\n await storageRemove(VERIFIER_STORAGE_KEY);\n }\n }\n throw error;\n }\n };\n\n const signInPromise = (async (): Promise<SignInResult> => {\n const result = await runSignInAction();\n // Launch any OAuth redirect from WITHIN the shared promise. Deduping only\n // the server action (as before) still let each concurrent caller run its\n // own `launchOAuth` — double navigation on web, two auth sessions on\n // native. Launching here means the single shared promise owns the one\n // launch, and every awaiting caller resolves to its result.\n return result.kind === \"redirect\"\n ? await launchOAuth(result.redirect, result.verifier)\n : result;\n })();\n\n activeSignIn = { key: signInKey, promise: signInPromise };\n try {\n return await signInPromise;\n } finally {\n if (activeSignIn?.promise === signInPromise) {\n activeSignIn = null;\n }\n }\n };\n\n /**\n * Sign out the current user.\n *\n * Invalidates the server session and clears local token state.\n * Errors are silently caught — calling `signOut` on an already\n * signed-out user is a no-op.\n */\n const signOut = async () => {\n if (destroyed) return;\n if (proxy) {\n try {\n await proxyFetch({ action: \"auth:signOut\", args: {} });\n } catch {\n /* empty */\n }\n await setToken({ shouldStore: false, tokens: null });\n if (convex.clearAuth) convex.clearAuth();\n return;\n }\n\n try {\n await convex.action(requireApiRefs().signOut, {});\n } catch {\n /* empty */\n }\n await setToken({ shouldStore: true, tokens: null });\n if (convex.clearAuth) convex.clearAuth();\n };\n\n const fetchAccessToken = async ({\n forceRefreshToken,\n }: {\n forceRefreshToken: boolean;\n }): Promise<string | null> => {\n if (destroyed) return null;\n if (!forceRefreshToken) return token;\n if (forcedRefreshPromise !== null) {\n return await forcedRefreshPromise;\n }\n if (token !== null && Date.now() - lastForcedRefreshTime < FORCED_REFRESH_REUSE_MS) {\n return token;\n }\n\n forcedRefreshPromise = (async () => {\n try {\n return await refreshAccessToken();\n } finally {\n forcedRefreshPromise = null;\n }\n })();\n return await forcedRefreshPromise;\n };\n\n async function refreshAccessToken(): Promise<string | null> {\n const mutex = runtime.mutex;\n const withMutex = mutex\n ? <T>(key: string, callback: () => Promise<T>) => mutex.withKey(key, callback)\n : localMutex;\n\n if (proxy) {\n const tokenBeforeRefresh = token;\n return await withMutex(`__convexAuthProxyRefresh_${escapedNamespace}`, async () => {\n if (token !== tokenBeforeRefresh) return token;\n\n const epochAtRefreshStart = authEpoch;\n try {\n const result = await retryWithJitteredBackoff(\n () =>\n proxyFetch({\n action: \"auth:signIn\",\n args: { refreshToken: true },\n }) as Promise<SignInActionResult>,\n isRetriableProxyRefreshError,\n );\n // A concurrent signOut / rotation / destroy superseded this refresh:\n // drop the result instead of re-authenticating a stale identity.\n if (destroyed || authEpoch !== epochAtRefreshStart) {\n return token;\n }\n if (isSignedInResult(result) && result.session) {\n await setToken({\n shouldStore: false,\n tokens: { token: result.session.token },\n resyncConvexAuth: false,\n });\n lastForcedRefreshTime = Date.now();\n } else {\n await setToken({\n shouldStore: false,\n tokens: null,\n resyncConvexAuth: false,\n });\n }\n } catch (error) {\n logMessage(\"convex-auth/client\", LOG_LEVELS.ERROR, [\n \"[convex-auth] Proxy refresh failed:\",\n error,\n ]);\n if (destroyed || authEpoch !== epochAtRefreshStart) {\n return token;\n }\n if (isAuthRefreshRejection(error)) {\n // A proxy 401 / invalid-refresh is a real sign-out. Clearing the\n // token here breaks the Convex re-force-refresh -> 401 loop the old\n // catch-and-retain behavior caused; a transient failure (network,\n // 429, 5xx) still retains the token so the session can recover.\n await setToken({\n shouldStore: false,\n tokens: null,\n resyncConvexAuth: false,\n });\n } else if (token === null) {\n finalizeLoadingState();\n }\n }\n return token;\n });\n }\n\n const tokenBeforeLockAcquisition = token;\n return await withMutex(key(REFRESH_TOKEN_STORAGE_KEY), async () => {\n const tokenAfterLockAcquisition = token;\n if (tokenAfterLockAcquisition !== tokenBeforeLockAcquisition) {\n return tokenAfterLockAcquisition;\n }\n const refreshToken = (await storageGet(REFRESH_TOKEN_STORAGE_KEY)) ?? null;\n if (!refreshToken) {\n await setToken({ shouldStore: true, tokens: null, resyncConvexAuth: false });\n return null;\n }\n const epochAtRefreshStart = authEpoch;\n try {\n const result = await verifyCode({ refreshToken });\n // Superseded by a concurrent signOut / rotation / destroy: discard the\n // freshly minted token rather than re-authenticating a stale identity.\n if (destroyed || authEpoch !== epochAtRefreshStart) {\n return token;\n }\n if (result.kind !== \"signedIn\") {\n throw new Error(\"Code exchange did not return tokens.\");\n }\n await setToken({ shouldStore: true, tokens: result.session, resyncConvexAuth: false });\n lastForcedRefreshTime = Date.now();\n } catch (error) {\n if (destroyed || authEpoch !== epochAtRefreshStart) {\n return token;\n }\n if (isAuthRefreshRejection(error)) {\n // The refresh credential was rejected (revoked/expired): sign out and\n // delete the stored refresh token.\n await setToken({ shouldStore: true, tokens: null, resyncConvexAuth: false });\n return null;\n }\n // Transient failure (network / 5xx after in-flight retries): keep the\n // session and the stored refresh token so a later forced refresh can\n // recover instead of signing the user out on a blip.\n logMessage(\"convex-auth/client\", LOG_LEVELS.ERROR, [\n \"[convex-auth] Token refresh failed transiently, keeping session:\",\n error,\n ]);\n }\n return token;\n });\n }\n\n const resolveOAuthInput = (input: URL | string | { code: string }) => {\n if (input instanceof URL) {\n return {\n code: input.searchParams.get(\"code\"),\n cleanupUrl: input.searchParams.has(\"code\")\n ? (() => {\n const next = new URL(input.toString());\n next.searchParams.delete(\"code\");\n next.searchParams.delete(\"state\");\n return next;\n })()\n : null,\n };\n }\n if (typeof input === \"object\") {\n return { code: input.code, cleanupUrl: null };\n }\n try {\n const url = new URL(input);\n return {\n code: url.searchParams.get(\"code\"),\n cleanupUrl: url.searchParams.has(\"code\")\n ? (() => {\n const next = new URL(url.toString());\n next.searchParams.delete(\"code\");\n next.searchParams.delete(\"state\");\n return next;\n })()\n : null,\n };\n } catch {\n return { code: input, cleanupUrl: null };\n }\n };\n\n const replaceWithCleanupUrl = async (cleanupUrl: URL) => {\n if (!runtime.location) return;\n const current = runtime.location.get();\n const relativeUrl =\n current !== null && cleanupUrl.origin === current.origin\n ? `${cleanupUrl.pathname}${cleanupUrl.search}${cleanupUrl.hash}`\n : cleanupUrl.toString();\n await runtime.location.replace(relativeUrl);\n };\n\n const completeOAuth = async (\n input: URL | string | { code: string },\n ): Promise<OAuthCompletionResult> => {\n const { code, cleanupUrl } = resolveOAuthInput(input);\n if (!code) {\n return { handled: false };\n }\n const result = await signIn(undefined, { code });\n if (result.kind !== \"signedIn\") {\n throw new Error(\"OAuth code exchange did not complete sign-in.\");\n }\n if (cleanupUrl) {\n await replaceWithCleanupUrl(cleanupUrl);\n }\n return { handled: true, cleanupUrl };\n };\n\n const hydrateFromStorage = async () => {\n const storedToken = (await storageGet(JWT_STORAGE_KEY)) ?? null;\n await setToken({\n shouldStore: false,\n tokens: storedToken === null ? null : { token: storedToken as AccessToken },\n resyncConvexAuth: storedToken !== null && storedToken !== token,\n });\n };\n\n const initialize = async (): Promise<void> => {\n if (destroyed) return;\n if (initializePromise !== null) {\n return await initializePromise;\n }\n\n initializePromise = (async () => {\n if (proxy) {\n if (!tokenProvided && !hasServerToken && runtime.environment !== \"server\") {\n try {\n await fetchAccessToken({ forceRefreshToken: true });\n } catch (error) {\n logMessage(\"convex-auth/client\", LOG_LEVELS.ERROR, [\n \"[convex-auth] Proxy token refresh failed:\",\n error,\n ]);\n }\n }\n return;\n }\n\n // Only hydrate from storage when we did NOT already load a token\n // synchronously at boot (`readInitialToken`). With synchronous storage\n // (web localStorage) `hasServerToken` is already true, so re-reading is\n // redundant and can clobber a token a cross-tab sync adopted into memory\n // in the same tick; async-only storage (e.g. Expo SecureStore) returns\n // null synchronously, leaving `hasServerToken` false, and needs this.\n if (!tokenProvided && !hasServerToken) {\n try {\n await hydrateFromStorage();\n } catch {\n try {\n await setToken({ shouldStore: false, tokens: null, resyncConvexAuth: false });\n } catch {\n /* empty */\n }\n }\n }\n\n try {\n const loc = runtime.location?.get() ?? null;\n // A generic `code` query parameter can belong to the host app (for\n // example, an invitation code). OAuth callbacks include `state`, so\n // require both before consuming the URL during automatic startup.\n // Explicit `completeOAuth(...)` calls still accept a bare code.\n if (loc && loc.searchParams.has(\"code\") && loc.searchParams.has(\"state\")) {\n await completeOAuth(loc);\n }\n } catch (error) {\n logMessage(\"convex-auth/client\", LOG_LEVELS.ERROR, [\n \"[convex-auth] OAuth completion failed:\",\n error,\n ]);\n }\n })();\n\n try {\n await initializePromise;\n } finally {\n initializePromise = Promise.resolve();\n }\n };\n\n const subscribe = (handler: AuthSubscriber): (() => void) => {\n let last: AuthState | null = null;\n const emit = () => {\n const state = publicSnapshot;\n if (last !== null && authStatesEqual(last, state)) {\n return;\n }\n last = state;\n handler(state);\n };\n emit();\n subscribers.add(emit);\n return () => {\n subscribers.delete(emit);\n };\n };\n\n if (!proxy && runtime.sync) {\n const logStorageSyncError = (error: unknown) => {\n logMessage(\"convex-auth/client\", LOG_LEVELS.ERROR, [\n \"[convex-auth] Storage event handler failed:\",\n error,\n ]);\n };\n disposeStorageListener =\n runtime.sync.subscribe(key(JWT_STORAGE_KEY), (value) => {\n if (destroyed) return;\n if (value === token) return;\n\n // Another tab signed out: propagate the sign-out here too.\n if (value === null) {\n void setToken({ shouldStore: false, tokens: null }).catch(logStorageSyncError);\n return;\n }\n\n const incoming = value as AccessToken;\n\n if (authConfirmed && token !== null) {\n // This tab holds a confirmed identity. Adopt a cross-tab token only\n // when it belongs to the SAME subject (a rotated access token) — and\n // do so without dropping confirmation, so a same-user rotation does\n // not flicker the UI through `loading`. A token for a DIFFERENT\n // subject is ignored to prevent one tab silently adopting another\n // user's identity.\n const currentSubject = decodeJwtSubject(token);\n const incomingSubject = decodeJwtSubject(incoming);\n if (currentSubject !== null && incomingSubject === currentSubject) {\n void setToken({\n shouldStore: false,\n tokens: { token: incoming },\n resyncConvexAuth: false,\n }).catch(logStorageSyncError);\n }\n return;\n }\n\n // This tab is signed out or still resolving: adopt a cross-tab sign-in\n // and run the confirmation handshake.\n void setToken({\n shouldStore: false,\n tokens: { token: incoming },\n }).catch(logStorageSyncError);\n }) ?? null;\n }\n\n bindConvexAuth();\n\n void initialize().catch((error) => {\n logMessage(\"convex-auth/client\", LOG_LEVELS.ERROR, [\n \"[convex-auth] Client initialization failed:\",\n error,\n ]);\n finalizeLoadingState();\n });\n\n const totp =\n adapters.totp ??\n createTotpClient({\n proxy,\n convex,\n requireApiRefs,\n proxyFetch,\n setTokenAndMaybeWait,\n });\n\n const device =\n adapters.device ??\n createDeviceClient({\n proxy,\n convex,\n requireApiRefs,\n proxyFetch,\n setTokenAndMaybeWait,\n });\n\n const auth = {\n /** Restore persisted auth state for the current runtime. */\n initialize,\n /** SSR-safe URL param reader. */\n param,\n /** Pending invite from URL or recovered from storage. Null if none. */\n get invite(): PendingInvite | null {\n const pendingInvite = getPendingInvite();\n if (!pendingInvite) return null;\n return {\n token: pendingInvite.token,\n email: pendingInvite.email,\n accept: acceptInvite,\n };\n },\n /** Complete an OAuth callback from a URL or authorization code. */\n completeOAuth,\n /** Sign in with a provider. See {@link SignInResult} for return shape. */\n signIn,\n /** Sign out and clear all token state. */\n signOut,\n subscribe,\n getSnapshot: () => publicSnapshot,\n /** TOTP two-factor authentication helpers. */\n totp,\n /** Device authorization (RFC 8628) helpers. */\n device,\n /**\n * Tear down this auth client instance.\n *\n * Removes the cross-tab `storage` event listener, clears all\n * `subscribe` subscribers, and rejects any in-flight handshake\n * waiters. Call this when the client is no longer needed\n * (e.g. on SPA unmount or hot-module replacement) to prevent\n * memory leaks and stale callbacks.\n *\n * @example\n * ```ts\n * // SvelteKit onDestroy\n * import { onDestroy } from \"svelte\";\n * const auth = client({ convex, api: api.auth });\n * onDestroy(() => auth.destroy());\n * ```\n *\n * @example\n * ```ts\n * const unsubscribe = auth.subscribe((state) => console.log(state.status));\n *\n * // Later, during cleanup:\n * unsubscribe();\n * auth.destroy();\n * ```\n */\n destroy: () => {\n destroyed = true;\n clearDeauthGrace();\n // Reject ALL waiters, not just those at the current epoch — leftover\n // waiters from an earlier epoch would otherwise leak their timers and\n // never-settled promises across an unmount / hot-module replacement.\n rejectAllHandshakeWaiters(\n createHandshakeError(\"AUTH_HANDSHAKE_REJECTED\", {\n reason: \"destroyed\",\n }),\n );\n disposeStorageListener?.();\n subscribers.clear();\n },\n } as AuthClient<Api> & { webauthn?: NonNullable<ClientAdapters[\"webauthn\"]> };\n if (webauthnAdapter !== undefined) auth.webauthn = webauthnAdapter;\n return auth;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["key","url"],"sources":["../../src/client/index.ts"],"sourcesContent":["/**\n * Framework-agnostic auth client for `@estifanos-sh/convex-auth/client`.\n *\n * Exposes the {@link client} factory, which wires auth tokens into a Convex\n * transport and returns `signIn`, `signOut`, `subscribe`, `getSnapshot`, and the\n * factor helpers enabled by the configured providers. Platform entrypoints\n * (`browser`, `expo`) layer concrete runtime defaults on top of this.\n *\n * @module\n */\n\nimport { ConvexError, Value } from \"convex/values\";\n\nimport { LOG_LEVELS, logMessage } from \"../shared/log\";\nimport { retryWithBackoff } from \"../shared/retry\";\nimport {\n createDeferred,\n type AuthApiRefs,\n type AuthClient,\n type AuthFlowContext,\n type AuthState,\n type AuthSnapshot,\n type AuthSubscriber,\n type OAuthCompletionResult,\n type ClientAdapterDeps,\n type ClientAdapterFactories,\n type ActionTransport,\n type ClientAdapters,\n type ClientRuntime,\n type ClientOptions,\n type ConvexTransport,\n type DeviceCodeResult,\n type HandshakeWaiter,\n type PendingInvite,\n type SignInActionResult,\n type SignInResult,\n} from \"./core/types\";\nimport type { AccessToken } from \"../shared/brand\";\nimport type { AuthParameters, AuthTokens } from \"../shared/results\";\nimport { createHandshakeError } from \"./errors\";\nimport { createDeviceClient } from \"./factors/device\";\nimport { createTotpClient } from \"./factors/totp\";\nimport { createInviteManager } from \"./runtime/invite\";\nimport { localMutex } from \"./runtime/mutex\";\nimport {\n isAuthRefreshRejection,\n isRetriableProxyRefreshError,\n isTransientNetworkError,\n parseProxyErrorBody,\n parseProxyResponseBody,\n ProxyRequestError,\n type ProxyErrorBody,\n} from \"./runtime/proxy\";\nimport { createStorageHelpers } from \"./runtime/storage\";\n\nexport type {\n AnonymousParams,\n AuthClient,\n AuthState,\n ClientOptions,\n ClientRuntime,\n CodeCompletionParams,\n DeviceClient,\n DeviceCodeResult,\n DevicePollParams,\n DeviceVerifyParams,\n EmailInitiateParams,\n OAuthCompletionResult,\n OAuthSignInParams,\n WebAuthnClient,\n WebAuthnRegisterOptions,\n WebAuthnSignInOptions,\n PasswordParams,\n PendingInvite,\n PlatformAuthClient,\n SignInResult,\n ConnectionParams,\n Storage,\n TotpClient,\n TotpConfirmParams,\n TotpSetupOptions,\n TotpSetupResult,\n TotpVerifyParams,\n} from \"./core/types\";\nexport { ErrorCode, HandshakeErrorCode } from \"./errors\";\nexport type { ErrorCodeValue, HandshakeErrorCode as HandshakeErrorCodeValue } from \"./errors\";\n\nconst VERIFIER_STORAGE_KEY = \"__convexAuthOAuthVerifier\";\nconst JWT_STORAGE_KEY = \"__convexAuthJWT\";\nconst REFRESH_TOKEN_STORAGE_KEY = \"__convexAuthRefreshToken\";\nconst INVITE_TOKEN_KEY = \"__convexAuthPendingInvite\";\nconst INVITE_EMAIL_KEY = \"__convexAuthPendingInviteEmail\";\n\nconst RETRY_BASE_MS = 500;\nconst RETRY_MAX_RETRIES = 2;\nconst DEFAULT_AUTH_HANDSHAKE_TIMEOUT_MS = 5000;\nconst FORCED_REFRESH_REUSE_MS = 10000;\n\nconst retryWithJitteredBackoff = <T>(\n fn: () => Promise<T>,\n shouldRetry: (error: unknown) => boolean,\n): Promise<T> =>\n retryWithBackoff(fn, {\n maxRetries: RETRY_MAX_RETRIES,\n baseMs: RETRY_BASE_MS,\n jitterMode: \"centered\",\n shouldRetry,\n });\n\n/**\n * Resolve the Convex deployment URL from the client.\n *\n * `ConvexReactClient` exposes `.url` directly.\n * `ConvexClient` exposes `.client.url` via `BaseConvexClient`.\n *\n * @param convex - The Convex transport to read the deployment URL from.\n * @param explicit - An explicit URL that short-circuits discovery.\n * @returns The resolved deployment URL.\n * @throws {Error} When no URL can be determined and `explicit` is not provided.\n * @internal\n */\nexport function resolveUrl(convex: ConvexTransport, explicit?: string): string {\n if (explicit) return explicit;\n const candidate = convex as {\n url?: unknown;\n client?: { url?: unknown } | null;\n };\n const client =\n typeof candidate.client === \"object\" && candidate.client !== null\n ? candidate.client\n : undefined;\n const url: unknown = candidate.url ?? client?.url;\n if (typeof url === \"string\") return url;\n throw new Error(\"Could not determine Convex deployment URL. Pass `url` explicitly.\");\n}\n\nconst STABLE_STRINGIFY_MAX_DEPTH = 32;\n\nfunction stableStringify(value: unknown, depth = 0): string {\n if (depth > STABLE_STRINGIFY_MAX_DEPTH) {\n throw new Error(\"stableStringify: input exceeds maximum nesting depth\");\n }\n if (value === undefined) {\n return \"null\";\n }\n if (Array.isArray(value)) {\n return `[${value.map((item) => stableStringify(item, depth + 1)).join(\",\")}]`;\n }\n if (value !== null && typeof value === \"object\") {\n const entries = Object.entries(value)\n .filter(([, entryValue]) => entryValue !== undefined)\n .sort(([left], [right]) => left.localeCompare(right));\n return `{${entries\n .map(\n ([key, entryValue]) => `${JSON.stringify(key)}:${stableStringify(entryValue, depth + 1)}`,\n )\n .join(\",\")}}`;\n }\n return JSON.stringify(value);\n}\n\nfunction buildSignInRequestKey(\n provider: string | undefined,\n params: Record<string, Value>,\n): string {\n return stableStringify({ provider: provider ?? null, params });\n}\n\nfunction formDataEntries(formData: unknown): Iterable<[string, string | { name: string }]> {\n return formData as Iterable<[string, string | { name: string }]>;\n}\n\nfunction resolveExplicitToken(value: string | null | undefined): AccessToken | null | undefined {\n if (value === undefined) return undefined;\n if (value === null) return null;\n // A cleared auth cookie surfaces as an empty (or whitespace-only) string\n // during SSR. Treat it as an explicit signed-out seed instead of throwing and\n // crashing the render.\n if (value.trim().length === 0) return null;\n if (value.trim() !== value) {\n throw new Error(\"The `token` option must not include leading or trailing whitespace.\");\n }\n return value as AccessToken;\n}\n\n/**\n * Decode the `sub` (subject / user id) claim from a JWT access token without\n * verifying its signature. Used only to compare identities across browser tabs\n * — never for authorization. Returns `null` when the token cannot be decoded.\n */\nfunction decodeJwtSubject(jwt: string): string | null {\n try {\n const payloadSegment = jwt.split(\".\")[1];\n if (payloadSegment === undefined || payloadSegment.length === 0) {\n return null;\n }\n const normalized = payloadSegment.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = normalized + \"=\".repeat((4 - (normalized.length % 4)) % 4);\n if (typeof atob !== \"function\") {\n return null;\n }\n const claims = JSON.parse(atob(padded)) as { sub?: unknown };\n return typeof claims.sub === \"string\" ? claims.sub : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Create a framework-agnostic auth client.\n *\n * Returns an object with `signIn`, `signOut`, `subscribe`, `getSnapshot`, and any\n * factor helpers enabled by your configured providers. Platform-specific\n * WebAuthn support is added by higher-level entrypoints such as\n * `@estifanos-sh/convex-auth/browser`.\n *\n * ### SPA mode (default)\n *\n * ```ts\n * import { ConvexClient } from 'convex/browser';\n * import { client } from '@estifanos-sh/convex-auth/client';\n * import { api } from '../convex/_generated/api';\n *\n * const convex = new ConvexClient(CONVEX_URL);\n * const auth = client({ convex, api: api.auth });\n * ```\n *\n * ### Proxy mode\n *\n * ```ts\n * const auth = client({\n * convex,\n * proxyPath: '/api/auth',\n * runtime: myRuntime,\n * });\n * ```\n *\n * In proxy mode all auth operations go through the injected proxy runtime.\n * Tokens are stored in httpOnly cookies server-side — the client\n * holds the JWT in memory only.\n *\n * The client resolves auth through a Convex confirmation handshake. For SSR,\n * pass the server-known token via `token` so hydration starts from the same\n * resolved state. The returned client also auto-wires its tokens into the\n * passed Convex client via `setAuth`, so queries and mutations are\n * authenticated without further configuration.\n *\n * While a sign-in handshake is pending, a transient `false` from the Convex\n * client does not reject the session: Convex can emit `false` mid-reauth and a\n * subsequent `true` confirms the same session. Rejection happens only on\n * timeout or when the token actually changes or clears.\n *\n * @param options - Client configuration. See {@link ClientOptions}.\n * @typeParam Api - An AuthApiRefs type determining which factor helpers are available.\n * @returns Auth client with conditional `totp` and `device` helpers.\n * @throws {Error} When the Convex deployment URL cannot be determined and `url` is not passed explicitly.\n * @throws {Error} When `proxyPath` is not set and the `api` option is missing.\n * @throws {Error} When `proxyPath` is set and `runtime.proxy` is missing.\n * @throws {Error} When `token` has leading or trailing whitespace. An empty or\n * whitespace-only string is treated as an explicit signed-out seed, not an error.\n */\nexport function client<Api extends AuthApiRefs = AuthApiRefs>(\n options: ClientOptions<Api>,\n): AuthClient<Api> {\n const { convex, proxyPath, api: apiRefs } = options;\n const proxy = proxyPath;\n const runtime: ClientRuntime = options.runtime ?? {};\n const adapters: ClientAdapters = options.adapters ?? {};\n const adapterFactories: ClientAdapterFactories = options.adapterFactories ?? {};\n\n function requireProxyRuntime() {\n if (!runtime.proxy) {\n throw new Error(\n \"The `runtime.proxy` option is required when `proxyPath` is set. \" +\n \"Use `@estifanos-sh/convex-auth/browser` for browser defaults or inject a proxy runtime explicitly.\",\n );\n }\n return runtime.proxy;\n }\n\n function requireApiRefs() {\n if (!apiRefs) {\n throw new Error(\n \"The `api` option is required when `proxyPath` is not set. \" + \"Pass { api: api.auth }.\",\n );\n }\n return apiRefs;\n }\n\n function requireHttpClient() {\n if (!httpClient) {\n throw new Error(\n \"The `httpClient` option is required when `proxyPath` is not set in a non-browser runtime. \" +\n \"Use `@estifanos-sh/convex-auth/browser` for browser defaults or pass an action-only transport explicitly.\",\n );\n }\n return httpClient;\n }\n\n const storage =\n options.storage !== undefined\n ? options.storage\n : runtime.storage !== undefined\n ? runtime.storage\n : null;\n const proxyRuntime = proxy ? requireProxyRuntime() : null;\n\n function getLocation(): URL | null {\n if (typeof options.location === \"function\") return options.location();\n if (options.location instanceof URL) return options.location;\n if (runtime.location) return runtime.location.get();\n return null;\n }\n\n /**\n * SSR-safe URL parameter reader.\n *\n * Uses the injected location source when provided.\n *\n * @param name - The query parameter name.\n * @returns The parameter value, or `null` if not present or in SSR.\n *\n * @example\n * ```ts\n * const workspaceId = auth.param(\"workspace\");\n * const tab = auth.param(\"tab\") ?? \"issues\";\n * ```\n */\n function param(name: string): string | null {\n const loc = getLocation();\n return loc?.searchParams.get(name) ?? null;\n }\n\n function cleanUrlParams(params: string[]) {\n const loc = getLocation();\n if (!loc) return;\n if (!runtime.location) return;\n const searchParams = new URLSearchParams(loc.search);\n let changed = false;\n for (const p of params) {\n if (searchParams.has(p)) {\n searchParams.delete(p);\n changed = true;\n }\n }\n if (changed) {\n const next = searchParams.toString() ? `${loc.pathname}?${searchParams}` : loc.pathname;\n void runtime.location.replace(next);\n }\n }\n\n const url = proxy ? undefined : resolveUrl(convex, options.url);\n const escapedNamespace = (proxy ?? url!)\n .replace(/^https?:\\/\\//, \"\")\n .replace(/[^a-zA-Z0-9]/g, \"_\");\n const key = (name: string) => `${name}_${escapedNamespace}`;\n const {\n get: storageGet,\n set: storageSet,\n remove: storageRemove,\n } = createStorageHelpers({ storage, key });\n const withAuthStorageMutex = async <T>(callback: () => Promise<T>): Promise<T> => {\n const mutexKey = `__convexAuthStorage_${escapedNamespace}`;\n return runtime.mutex\n ? await runtime.mutex.withKey(mutexKey, callback)\n : await localMutex(mutexKey, callback);\n };\n const proxyFetch = async (body: AuthParameters): Promise<Value> => {\n if (!proxy) {\n throw new Error(\"Proxy fetch requested without proxyPath.\");\n }\n const response = await proxyRuntime!.fetch(body, proxy);\n if (!response.ok) {\n let errorBody: ProxyErrorBody = {};\n try {\n errorBody = parseProxyErrorBody(await response.json());\n } catch {\n errorBody = {};\n }\n if (errorBody.authError !== undefined) {\n throw new ConvexError(errorBody.authError);\n }\n throw new ProxyRequestError(response.status, errorBody.error);\n }\n try {\n return parseProxyResponseBody(await response.json());\n } catch {\n throw new Error(\"Proxy response was not valid JSON\");\n }\n };\n const subscribers = new Set<() => void>();\n let disposeStorageListener: (() => void) | null = null;\n\n const httpClient: ActionTransport | null = proxy ? null : (options.httpClient ?? null);\n\n const readInitialToken = (): AccessToken | null => {\n if (!storage) return null;\n try {\n const raw = storage.getItem(key(JWT_STORAGE_KEY));\n return typeof raw === \"string\" && raw.length > 0 ? (raw as AccessToken) : null;\n } catch {\n return null;\n }\n };\n const explicitToken = resolveExplicitToken(options.token);\n const tokenProvided = explicitToken !== undefined;\n const serverToken: AccessToken | null = tokenProvided ? explicitToken : readInitialToken();\n const hasServerToken = serverToken !== null;\n\n const handshakeTimeoutMs =\n typeof options.handshakeTimeoutMs === \"number\" && options.handshakeTimeoutMs > 0\n ? options.handshakeTimeoutMs\n : DEFAULT_AUTH_HANDSHAKE_TIMEOUT_MS;\n\n let token: AccessToken | null = serverToken;\n let isLoading = !hasServerToken && !tokenProvided;\n let authConfirmed = hasServerToken;\n let handshakePending = false;\n let authEpoch = 0;\n let lastForcedRefreshTime = 0;\n let forcedRefreshPromise: Promise<string | null> | null = null;\n let destroyed = false;\n // Bounded grace window for a confirmed session that Convex reports as `false`.\n // A transient mid-rotation `false` is followed by a confirming `true` (which\n // clears the timer); a real server-side deauth (revoke/expiry) leaves it to\n // fire and sign out. The timer callback pins itself to the token epoch it was\n // armed for so a concurrent rotation cannot trigger a spurious sign-out.\n let deauthGraceTimer: ReturnType<typeof setTimeout> | null = null;\n let activeSignIn: {\n key: string;\n promise: Promise<SignInResult>;\n } | null = null;\n let initializePromise: Promise<void> | null = null;\n const handshakeWaiters = new Set<HandshakeWaiter>();\n let snapshot: AuthSnapshot = {\n phase: hasServerToken ? \"authenticated\" : isLoading ? \"loading\" : \"unauthenticated\",\n isLoading,\n isAuthenticated: hasServerToken,\n token,\n };\n\n const computePublic = (): AuthState => {\n if (snapshot.token !== null && snapshot.phase === \"authenticated\") {\n return { status: \"signedIn\", token: snapshot.token };\n }\n if (snapshot.phase === \"loading\" || snapshot.phase === \"handshake\") {\n return { status: \"loading\", token: null };\n }\n return { status: \"signedOut\", token: null };\n };\n let publicSnapshot: AuthState = computePublic();\n const authStatesEqual = (left: AuthState, right: AuthState) =>\n left.status === right.status && left.token === right.token;\n const refreshPublic = (): boolean => {\n const next = computePublic();\n if (authStatesEqual(publicSnapshot, next)) {\n return false;\n }\n publicSnapshot = next;\n return true;\n };\n\n const settleHandshakeWaiters = (\n epoch: number,\n outcome: { type: \"resolve\" } | { type: \"reject\"; error: ConvexError<Value> },\n ) => {\n for (const waiter of Array.from(handshakeWaiters)) {\n if (waiter.epoch !== epoch) {\n continue;\n }\n clearTimeout(waiter.timeoutId);\n handshakeWaiters.delete(waiter);\n if (outcome.type === \"resolve\") {\n waiter.deferred.resolve(undefined);\n } else {\n waiter.deferred.reject(outcome.error);\n }\n }\n };\n\n const rejectObsoleteHandshakeWaiters = (activeEpoch: number) => {\n for (const waiter of Array.from(handshakeWaiters)) {\n if (waiter.epoch >= activeEpoch) {\n continue;\n }\n clearTimeout(waiter.timeoutId);\n handshakeWaiters.delete(waiter);\n waiter.deferred.reject(\n createHandshakeError(\"AUTH_HANDSHAKE_REJECTED\", {\n ...waiter.context,\n reason: \"token_changed\",\n }),\n );\n }\n };\n\n const rejectAllHandshakeWaiters = (error: ConvexError<Value>) => {\n for (const waiter of Array.from(handshakeWaiters)) {\n clearTimeout(waiter.timeoutId);\n handshakeWaiters.delete(waiter);\n waiter.deferred.reject(error);\n }\n };\n\n const clearDeauthGrace = () => {\n if (deauthGraceTimer !== null) {\n clearTimeout(deauthGraceTimer);\n deauthGraceTimer = null;\n }\n };\n\n /**\n * Arm the bounded re-handshake window after Convex reports a confirmed\n * session as unauthenticated. The public snapshot is intentionally left on\n * the confirmed state so a transient mid-rotation `false` does not flicker the\n * UI; only if no confirming `true` arrives before the window elapses (and the\n * token identity is unchanged) do we sign out.\n */\n const startDeauthGrace = () => {\n if (deauthGraceTimer !== null) {\n return;\n }\n const epoch = authEpoch;\n deauthGraceTimer = setTimeout(() => {\n deauthGraceTimer = null;\n if (destroyed || authEpoch !== epoch || !authConfirmed || token === null) {\n return;\n }\n void setToken({ shouldStore: true, tokens: null }).catch((error) => {\n logMessage(\"convex-auth/client\", LOG_LEVELS.ERROR, [\n \"[convex-auth] Sign-out after server-side deauthentication failed:\",\n error,\n ]);\n });\n }, handshakeTimeoutMs);\n };\n\n const waitForAuthHandshake = async (context: AuthFlowContext) => {\n if (token === null) {\n return;\n }\n if (authConfirmed && !handshakePending) {\n return;\n }\n if (!handshakePending) {\n throw createHandshakeError(\"AUTH_HANDSHAKE_REJECTED\", {\n ...context,\n reason: \"auth_rejected\",\n });\n }\n\n const epoch = authEpoch;\n const deferred = createDeferred<void, ConvexError<Value>>();\n const waiter: HandshakeWaiter = {\n epoch,\n context,\n deferred,\n timeoutId: setTimeout(() => {\n handshakeWaiters.delete(waiter);\n deferred.reject(\n createHandshakeError(\"AUTH_HANDSHAKE_TIMEOUT\", {\n ...context,\n timeoutMs: handshakeTimeoutMs,\n }),\n );\n // Unstick the state machine: a never-confirmed handshake that times out\n // must not leave `handshakePending` set (which would spin the UI in\n // `loading` forever). Remove only the unconfirmed access token from\n // storage, preserving the refresh credential so a later reload can\n // recover. Keeping the stale JWT made the next client boot report\n // signed-in synchronously, skip refresh hydration, and then discard the\n // still-valid refresh token when Convex rejected that JWT.\n if (!destroyed && authEpoch === epoch && token !== null && handshakePending) {\n const timedOutToken = token;\n void withAuthStorageMutex(async () => {\n if (destroyed || authEpoch !== epoch || token !== timedOutToken || !handshakePending) {\n return;\n }\n await storageRemove(JWT_STORAGE_KEY);\n // Re-check after async storage I/O: a newer sign-in may have changed\n // the in-memory epoch while waiting. Its queued storage write must\n // win and its token must not be cleared here.\n if (destroyed || authEpoch !== epoch || token !== timedOutToken || !handshakePending) {\n return;\n }\n await setToken({ shouldStore: false, tokens: null });\n }).catch((error) => {\n logMessage(\"convex-auth/client\", LOG_LEVELS.ERROR, [\n \"[convex-auth] Sign-out after handshake timeout failed:\",\n error,\n ]);\n });\n }\n }, handshakeTimeoutMs),\n };\n handshakeWaiters.add(waiter);\n try {\n await deferred.promise;\n } finally {\n clearTimeout(waiter.timeoutId);\n handshakeWaiters.delete(waiter);\n }\n };\n\n const handleConvexAuthChange = (isAuthenticated: boolean) => {\n if (destroyed) {\n return;\n }\n\n if (isAuthenticated) {\n authConfirmed = true;\n handshakePending = false;\n clearDeauthGrace();\n settleHandshakeWaiters(authEpoch, { type: \"resolve\" });\n } else if (token === null || !authConfirmed) {\n authConfirmed = false;\n if (token !== null) {\n handshakePending = true;\n }\n } else {\n // A previously confirmed session (`token !== null && authConfirmed`) is\n // now reported unauthenticated. This is ambiguous: it may be a transient\n // mid-rotation `false` (a confirming `true` follows shortly) or a real\n // server-side deauthentication (revoke/expiry). Keep the confirmed\n // snapshot for now and arm a bounded grace window that signs out only if\n // no `true` re-confirms in time.\n startDeauthGrace();\n }\n\n if (updateSnapshot()) {\n notify();\n }\n };\n\n const notify = () => {\n refreshPublic();\n for (const cb of subscribers) cb();\n };\n\n const updateSnapshot = () => {\n const tag =\n token !== null && handshakePending\n ? \"handshake\"\n : isLoading\n ? \"loading\"\n : token !== null && authConfirmed\n ? \"authenticated\"\n : \"unauthenticated\";\n\n const phase = tag as AuthSnapshot[\"phase\"];\n\n const next: AuthSnapshot = {\n phase,\n isLoading: phase === \"loading\" || phase === \"handshake\",\n isAuthenticated: phase === \"authenticated\",\n token,\n };\n if (\n snapshot.phase === next.phase &&\n snapshot.isLoading === next.isLoading &&\n snapshot.isAuthenticated === next.isAuthenticated &&\n snapshot.token === next.token\n ) {\n return false;\n }\n snapshot = next;\n return true;\n };\n\n const finalizeLoadingState = () => {\n if (!isLoading) {\n return;\n }\n isLoading = false;\n if (updateSnapshot()) {\n notify();\n }\n };\n\n const inviteManager = createInviteManager({\n param,\n storageGet,\n storageSet,\n storageRemove,\n cleanUrlParams,\n tokenKey: INVITE_TOKEN_KEY,\n emailKey: INVITE_EMAIL_KEY,\n });\n const getPendingInvite = () => inviteManager.getPendingInvite();\n const persistInvite = () => inviteManager.persistInvite();\n const acceptInvite = () => inviteManager.acceptInvite();\n\n const bindConvexAuth = () => {\n convex.setAuth(fetchAccessToken, handleConvexAuthChange);\n };\n\n const setToken = async (\n args:\n | {\n shouldStore: true;\n tokens: AuthTokens | null;\n requireHandshake?: boolean;\n resyncConvexAuth?: boolean;\n }\n | {\n shouldStore: false;\n tokens: { token: AccessToken } | null;\n requireHandshake?: boolean;\n resyncConvexAuth?: boolean;\n },\n ) => {\n const previousToken = token;\n const nextToken = args.tokens === null ? null : args.tokens.token;\n\n // Assign the token and bump the epoch synchronously — before any storage\n // I/O await — so `authEpoch` and `token` never disagree within a tick. The\n // forced-refresh race guard depends on this: a concurrent `signOut` that\n // clears the token must be observable by an in-flight refresh the instant\n // it happens, not only after the storage write resolves.\n token = nextToken;\n const tokenChanged = token !== previousToken;\n if (tokenChanged) {\n authEpoch += 1;\n rejectObsoleteHandshakeWaiters(authEpoch);\n clearDeauthGrace();\n }\n\n if (args.tokens === null) {\n lastForcedRefreshTime = 0;\n }\n if (args.shouldStore) {\n await withAuthStorageMutex(async () => {\n if (args.tokens === null) {\n await storageRemove(JWT_STORAGE_KEY);\n await storageRemove(REFRESH_TOKEN_STORAGE_KEY);\n } else if (\"refreshToken\" in args.tokens) {\n await storageSet(JWT_STORAGE_KEY, args.tokens.token);\n await storageSet(REFRESH_TOKEN_STORAGE_KEY, args.tokens.refreshToken);\n }\n });\n }\n\n if (token === null) {\n authConfirmed = false;\n handshakePending = false;\n settleHandshakeWaiters(authEpoch, {\n type: \"reject\",\n error: createHandshakeError(\"AUTH_HANDSHAKE_REJECTED\", {\n reason: \"token_cleared\",\n }),\n });\n } else {\n const keepConfirmedAuth =\n authConfirmed && args.resyncConvexAuth === false && args.requireHandshake !== true;\n const shouldEnterHandshake =\n !keepConfirmedAuth && (args.requireHandshake === true || tokenChanged || !authConfirmed);\n if (shouldEnterHandshake) {\n authConfirmed = false;\n handshakePending = true;\n } else {\n handshakePending = false;\n }\n }\n\n const hadPendingLoad = isLoading;\n isLoading = false;\n const changed = updateSnapshot();\n if (args.resyncConvexAuth !== false) {\n bindConvexAuth();\n }\n if (hadPendingLoad || changed) {\n notify();\n }\n };\n\n const setTokenAndMaybeWait = async (\n args:\n | {\n shouldStore: true;\n tokens: AuthTokens | null;\n waitForHandshake: boolean;\n context: AuthFlowContext;\n }\n | {\n shouldStore: false;\n tokens: { token: AccessToken } | null;\n waitForHandshake: boolean;\n context: AuthFlowContext;\n },\n ): Promise<boolean> => {\n const { waitForHandshake, context, ...tokenArgs } = args;\n await setToken({\n ...(tokenArgs as\n | { shouldStore: true; tokens: AuthTokens | null }\n | { shouldStore: false; tokens: { token: AccessToken } | null }),\n requireHandshake: waitForHandshake,\n });\n if (tokenArgs.tokens === null) {\n return false;\n }\n if (waitForHandshake) {\n await waitForAuthHandshake(context);\n }\n return true;\n };\n\n const adapterDeps: ClientAdapterDeps = {\n proxy,\n convex,\n requireApiRefs,\n proxyFetch,\n setTokenAndMaybeWait,\n };\n const webauthnAdapter = adapters.webauthn ?? adapterFactories.webauthn?.(adapterDeps);\n\n const verifyCode = async (\n args: { code: string; verifier?: string } | { refreshToken: string },\n ) => {\n return retryWithJitteredBackoff(\n () =>\n requireHttpClient().action(\n requireApiRefs().signIn,\n \"code\" in args ? { params: { code: args.code }, verifier: args.verifier } : args,\n ) as Promise<SignInActionResult>,\n isTransientNetworkError,\n );\n };\n\n const normalizeDeviceCodeResult = (device_code: unknown): DeviceCodeResult => {\n const input = device_code as {\n deviceCode: string;\n userCode: string;\n verification_uri?: string;\n verificationUri?: string;\n verification_uri_complete?: string;\n verificationUriComplete?: string;\n expiresIn: number;\n interval: number;\n };\n return {\n deviceCode: input.deviceCode,\n userCode: input.userCode,\n verificationUri: input.verification_uri ?? input.verificationUri ?? \"\",\n verificationUriComplete:\n input.verification_uri_complete ?? input.verificationUriComplete ?? \"\",\n expiresIn: input.expiresIn,\n interval: input.interval,\n };\n };\n\n const isSignedInResult = (\n result: SignInActionResult,\n ): result is Extract<SignInActionResult, { kind: \"signedIn\" }> => result.kind === \"signedIn\";\n\n /**\n * Hand an OAuth authorization URL to the platform launcher.\n *\n * Browser-style runtimes navigate away and return nothing, so the caller\n * receives the `redirect` result. Session-style runtimes (Expo) return the\n * callback URL, which is completed inline and resolves to `signedIn`.\n */\n const launchOAuth = async (redirect: URL, verifier: string): Promise<SignInResult> => {\n const redirectResult = { kind: \"redirect\" as const, redirect, verifier } satisfies SignInResult;\n if (!runtime.oauth) {\n return redirectResult;\n }\n const callback = await runtime.oauth.open(redirect);\n if (callback === undefined) {\n return redirectResult;\n }\n const completion = await completeOAuth(callback);\n return completion.handled ? { kind: \"signedIn\" as const } : redirectResult;\n };\n\n /**\n * Sign in with a provider.\n *\n * @param provider - Provider ID (e.g. `\"email\"`, `\"password\"`, `\"google\"`).\n * Omit when exchanging an OAuth code (the code carries the provider info).\n * @param args - Provider-specific arguments. Pass a `Record<string, Value>`\n * or `FormData`. Common fields: `email`, `password`, `code`, `redirectTo`.\n * @returns A {@link SignInResult} indicating the outcome.\n * @throws {ConvexError} When the server action rejects the sign-in attempt (e.g. invalid credentials, provider error, or rate limiting).\n *\n * @example Email magic link\n * ```ts\n * await auth.signIn('email', { email: 'user@example.com' });\n * ```\n *\n * @example Password\n * ```ts\n * const result = await auth.signIn('password', { email, password, flow: 'signIn' });\n * if (result.kind === 'totpRequired') {\n * await auth.totp.verify({ code: totpCode, verifier: result.verifier });\n * }\n * ```\n *\n * @example OAuth (returns a redirect URL)\n * ```ts\n * const result = await auth.signIn('google');\n * if (result.kind === 'redirect') {\n * window.location.href = result.redirect.toString();\n * }\n * ```\n */\n const signIn = async (\n provider?: string,\n args?: FormData | Record<string, Value>,\n ): Promise<SignInResult> => {\n if (destroyed) {\n throw new Error(\"Convex auth client has been destroyed.\");\n }\n await persistInvite();\n\n const rawParams =\n args instanceof FormData\n ? (() => {\n const formParams: Record<string, Value> = {};\n for (const [key, value] of formDataEntries(args)) {\n if (typeof value === \"string\") {\n formParams[key] = value;\n }\n }\n return formParams;\n })()\n : (args ?? {});\n const params: Record<string, Value> =\n options.oauthRedirectTo !== undefined && rawParams.redirectTo === undefined\n ? { ...rawParams, redirectTo: options.oauthRedirectTo }\n : rawParams;\n const flow = typeof params.flow === \"string\" && params.flow.length > 0 ? params.flow : \"signIn\";\n const signInKey = buildSignInRequestKey(provider, params);\n\n const handleSignInActionResult = async (\n result: SignInActionResult,\n resultOptions: { shouldStore: boolean; persistVerifier: boolean },\n ): Promise<SignInResult> => {\n if (result.kind === \"redirect\") {\n const redirectUrl = new URL(result.redirect);\n if (resultOptions.persistVerifier) {\n await storageSet(VERIFIER_STORAGE_KEY, result.verifier);\n }\n return {\n kind: \"redirect\" as const,\n redirect: redirectUrl,\n verifier: result.verifier,\n } satisfies SignInResult;\n }\n\n if (result.kind === \"totpRequired\") {\n return {\n kind: \"totpRequired\" as const,\n verifier: result.verifier,\n } satisfies SignInResult;\n }\n\n if (result.kind === \"deviceCode\") {\n return {\n kind: \"deviceCode\" as const,\n deviceCode: normalizeDeviceCodeResult(result.deviceCode),\n } satisfies SignInResult;\n }\n\n if (result.kind === \"webauthnOptions\" && \"operation\" in result) {\n if (!webauthnAdapter) {\n throw new Error(\"This client does not support WebAuthn continuations.\");\n }\n return await webauthnAdapter.completeRegistration(result);\n }\n\n if (result.kind === \"signedIn\") {\n const signingIn = await setTokenAndMaybeWait(\n resultOptions.shouldStore\n ? {\n shouldStore: true as const,\n tokens: result.session,\n waitForHandshake: true,\n context: { provider, flow },\n }\n : {\n shouldStore: false as const,\n tokens: result.session === null ? null : { token: result.session.token },\n waitForHandshake: true,\n context: { provider, flow },\n },\n );\n return signingIn\n ? ({ kind: \"signedIn\" as const } satisfies SignInResult)\n : ({ kind: \"started\" as const } satisfies SignInResult);\n }\n\n return { kind: \"started\" as const } satisfies SignInResult;\n };\n\n if (activeSignIn !== null) {\n if (activeSignIn.key === signInKey) {\n // Share the entire in-flight flow — INCLUDING any OAuth launch — so a\n // concurrent same-key signIn observes the single launch's outcome\n // instead of opening a second browser / native auth session.\n return await activeSignIn.promise;\n }\n throw new Error(\"Another sign-in flow is already in progress.\");\n }\n\n const runSignInAction = async (): Promise<SignInResult> => {\n if (proxy) {\n const result = (await proxyFetch({\n action: \"auth:signIn\",\n args: { provider, params },\n })) as SignInActionResult;\n return await handleSignInActionResult(result, {\n shouldStore: false,\n persistVerifier: false,\n });\n }\n\n const verifier = (await storageGet(VERIFIER_STORAGE_KEY)) ?? undefined;\n try {\n const result = (await convex.action(requireApiRefs().signIn, {\n provider,\n params,\n verifier,\n })) as SignInActionResult;\n if (params.code !== undefined) {\n await storageRemove(VERIFIER_STORAGE_KEY);\n }\n return await handleSignInActionResult(result, {\n shouldStore: true,\n persistVerifier: true,\n });\n } catch (error) {\n if (params.code !== undefined) {\n const convexCode =\n error instanceof ConvexError && typeof error.data?.code === \"string\"\n ? error.data.code\n : null;\n if (\n convexCode !== null &&\n [\"INVALID_VERIFICATION_CODE\", \"INVALID_VERIFIER\"].includes(convexCode)\n ) {\n await storageRemove(VERIFIER_STORAGE_KEY);\n }\n }\n throw error;\n }\n };\n\n const signInPromise = (async (): Promise<SignInResult> => {\n const result = await runSignInAction();\n // Launch any OAuth redirect from WITHIN the shared promise. Deduping only\n // the server action (as before) still let each concurrent caller run its\n // own `launchOAuth` — double navigation on web, two auth sessions on\n // native. Launching here means the single shared promise owns the one\n // launch, and every awaiting caller resolves to its result.\n return result.kind === \"redirect\"\n ? await launchOAuth(result.redirect, result.verifier)\n : result;\n })();\n\n activeSignIn = { key: signInKey, promise: signInPromise };\n try {\n return await signInPromise;\n } finally {\n if (activeSignIn?.promise === signInPromise) {\n activeSignIn = null;\n }\n }\n };\n\n /**\n * Sign out the current user.\n *\n * Invalidates the server session and clears local token state.\n * Errors are silently caught — calling `signOut` on an already\n * signed-out user is a no-op.\n */\n const signOut = async () => {\n if (destroyed) return;\n if (proxy) {\n try {\n await proxyFetch({ action: \"auth:signOut\", args: {} });\n } catch {\n /* empty */\n }\n await setToken({ shouldStore: false, tokens: null });\n if (convex.clearAuth) convex.clearAuth();\n return;\n }\n\n try {\n await convex.action(requireApiRefs().signOut, {});\n } catch {\n /* empty */\n }\n await setToken({ shouldStore: true, tokens: null });\n if (convex.clearAuth) convex.clearAuth();\n };\n\n const fetchAccessToken = async ({\n forceRefreshToken,\n }: {\n forceRefreshToken: boolean;\n }): Promise<string | null> => {\n if (destroyed) return null;\n if (!forceRefreshToken) return token;\n if (forcedRefreshPromise !== null) {\n return await forcedRefreshPromise;\n }\n if (token !== null && Date.now() - lastForcedRefreshTime < FORCED_REFRESH_REUSE_MS) {\n return token;\n }\n\n forcedRefreshPromise = (async () => {\n try {\n return await refreshAccessToken();\n } finally {\n forcedRefreshPromise = null;\n }\n })();\n return await forcedRefreshPromise;\n };\n\n async function refreshAccessToken(): Promise<string | null> {\n const mutex = runtime.mutex;\n const withMutex = mutex\n ? <T>(key: string, callback: () => Promise<T>) => mutex.withKey(key, callback)\n : localMutex;\n\n if (proxy) {\n const tokenBeforeRefresh = token;\n return await withMutex(`__convexAuthProxyRefresh_${escapedNamespace}`, async () => {\n if (token !== tokenBeforeRefresh) return token;\n\n const epochAtRefreshStart = authEpoch;\n try {\n const result = await retryWithJitteredBackoff(\n () =>\n proxyFetch({\n action: \"auth:signIn\",\n args: { refreshToken: true },\n }) as Promise<SignInActionResult>,\n isRetriableProxyRefreshError,\n );\n // A concurrent signOut / rotation / destroy superseded this refresh:\n // drop the result instead of re-authenticating a stale identity.\n if (destroyed || authEpoch !== epochAtRefreshStart) {\n return token;\n }\n if (isSignedInResult(result) && result.session) {\n await setToken({\n shouldStore: false,\n tokens: { token: result.session.token },\n resyncConvexAuth: false,\n });\n lastForcedRefreshTime = Date.now();\n } else {\n await setToken({\n shouldStore: false,\n tokens: null,\n resyncConvexAuth: false,\n });\n }\n } catch (error) {\n logMessage(\"convex-auth/client\", LOG_LEVELS.ERROR, [\n \"[convex-auth] Proxy refresh failed:\",\n error,\n ]);\n if (destroyed || authEpoch !== epochAtRefreshStart) {\n return token;\n }\n if (isAuthRefreshRejection(error)) {\n // A proxy 401 / invalid-refresh is a real sign-out. Clearing the\n // token here breaks the Convex re-force-refresh -> 401 loop the old\n // catch-and-retain behavior caused; a transient failure (network,\n // 429, 5xx) still retains the token so the session can recover.\n await setToken({\n shouldStore: false,\n tokens: null,\n resyncConvexAuth: false,\n });\n } else if (token === null) {\n finalizeLoadingState();\n }\n }\n return token;\n });\n }\n\n const tokenBeforeLockAcquisition = token;\n return await withMutex(key(REFRESH_TOKEN_STORAGE_KEY), async () => {\n const tokenAfterLockAcquisition = token;\n if (tokenAfterLockAcquisition !== tokenBeforeLockAcquisition) {\n return tokenAfterLockAcquisition;\n }\n const refreshToken = (await storageGet(REFRESH_TOKEN_STORAGE_KEY)) ?? null;\n if (!refreshToken) {\n await setToken({ shouldStore: true, tokens: null, resyncConvexAuth: false });\n return null;\n }\n const epochAtRefreshStart = authEpoch;\n try {\n const result = await verifyCode({ refreshToken });\n // Superseded by a concurrent signOut / rotation / destroy: discard the\n // freshly minted token rather than re-authenticating a stale identity.\n if (destroyed || authEpoch !== epochAtRefreshStart) {\n return token;\n }\n if (result.kind !== \"signedIn\") {\n throw new Error(\"Code exchange did not return tokens.\");\n }\n await setToken({ shouldStore: true, tokens: result.session, resyncConvexAuth: false });\n lastForcedRefreshTime = Date.now();\n } catch (error) {\n if (destroyed || authEpoch !== epochAtRefreshStart) {\n return token;\n }\n if (isAuthRefreshRejection(error)) {\n // The refresh credential was rejected (revoked/expired): sign out and\n // delete the stored refresh token.\n await setToken({ shouldStore: true, tokens: null, resyncConvexAuth: false });\n return null;\n }\n // Transient failure (network / 5xx after in-flight retries): keep the\n // session and the stored refresh token so a later forced refresh can\n // recover instead of signing the user out on a blip.\n logMessage(\"convex-auth/client\", LOG_LEVELS.ERROR, [\n \"[convex-auth] Token refresh failed transiently, keeping session:\",\n error,\n ]);\n }\n return token;\n });\n }\n\n const resolveOAuthInput = (input: URL | string | { code: string }) => {\n if (input instanceof URL) {\n return {\n code: input.searchParams.get(\"code\"),\n cleanupUrl: input.searchParams.has(\"code\")\n ? (() => {\n const next = new URL(input.toString());\n next.searchParams.delete(\"code\");\n next.searchParams.delete(\"state\");\n return next;\n })()\n : null,\n };\n }\n if (typeof input === \"object\") {\n return { code: input.code, cleanupUrl: null };\n }\n try {\n const url = new URL(input);\n return {\n code: url.searchParams.get(\"code\"),\n cleanupUrl: url.searchParams.has(\"code\")\n ? (() => {\n const next = new URL(url.toString());\n next.searchParams.delete(\"code\");\n next.searchParams.delete(\"state\");\n return next;\n })()\n : null,\n };\n } catch {\n return { code: input, cleanupUrl: null };\n }\n };\n\n const replaceWithCleanupUrl = async (cleanupUrl: URL) => {\n if (!runtime.location) return;\n const current = runtime.location.get();\n const relativeUrl =\n current !== null && cleanupUrl.origin === current.origin\n ? `${cleanupUrl.pathname}${cleanupUrl.search}${cleanupUrl.hash}`\n : cleanupUrl.toString();\n await runtime.location.replace(relativeUrl);\n };\n\n const completeOAuth = async (\n input: URL | string | { code: string },\n ): Promise<OAuthCompletionResult> => {\n const { code, cleanupUrl } = resolveOAuthInput(input);\n if (!code) {\n return { handled: false };\n }\n const result = await signIn(undefined, { code });\n if (result.kind !== \"signedIn\") {\n throw new Error(\"OAuth code exchange did not complete sign-in.\");\n }\n if (cleanupUrl) {\n await replaceWithCleanupUrl(cleanupUrl);\n }\n return { handled: true, cleanupUrl };\n };\n\n const hydrateFromStorage = async () => {\n const storedToken = (await storageGet(JWT_STORAGE_KEY)) ?? null;\n await setToken({\n shouldStore: false,\n tokens: storedToken === null ? null : { token: storedToken as AccessToken },\n resyncConvexAuth: storedToken !== null && storedToken !== token,\n });\n };\n\n const initialize = async (): Promise<void> => {\n if (destroyed) return;\n if (initializePromise !== null) {\n return await initializePromise;\n }\n\n initializePromise = (async () => {\n if (proxy) {\n if (!tokenProvided && !hasServerToken && runtime.environment !== \"server\") {\n try {\n await fetchAccessToken({ forceRefreshToken: true });\n } catch (error) {\n logMessage(\"convex-auth/client\", LOG_LEVELS.ERROR, [\n \"[convex-auth] Proxy token refresh failed:\",\n error,\n ]);\n }\n }\n return;\n }\n\n // Only hydrate from storage when we did NOT already load a token\n // synchronously at boot (`readInitialToken`). With synchronous storage\n // (web localStorage) `hasServerToken` is already true, so re-reading is\n // redundant and can clobber a token a cross-tab sync adopted into memory\n // in the same tick; async-only storage (e.g. Expo SecureStore) returns\n // null synchronously, leaving `hasServerToken` false, and needs this.\n if (!tokenProvided && !hasServerToken) {\n try {\n await hydrateFromStorage();\n } catch {\n try {\n await setToken({ shouldStore: false, tokens: null, resyncConvexAuth: false });\n } catch {\n /* empty */\n }\n }\n }\n\n try {\n const loc = runtime.location?.get() ?? null;\n // A generic `code` query parameter can belong to the host app (for\n // example, an invitation code). OAuth callbacks include `state`, so\n // require both before consuming the URL during automatic startup.\n // Explicit `completeOAuth(...)` calls still accept a bare code.\n if (loc && loc.searchParams.has(\"code\") && loc.searchParams.has(\"state\")) {\n await completeOAuth(loc);\n }\n } catch (error) {\n logMessage(\"convex-auth/client\", LOG_LEVELS.ERROR, [\n \"[convex-auth] OAuth completion failed:\",\n error,\n ]);\n }\n })();\n\n try {\n await initializePromise;\n } finally {\n initializePromise = Promise.resolve();\n }\n };\n\n const subscribe = (handler: AuthSubscriber): (() => void) => {\n let last: AuthState | null = null;\n const emit = () => {\n const state = publicSnapshot;\n if (last !== null && authStatesEqual(last, state)) {\n return;\n }\n last = state;\n handler(state);\n };\n emit();\n subscribers.add(emit);\n return () => {\n subscribers.delete(emit);\n };\n };\n\n if (!proxy && runtime.sync) {\n const logStorageSyncError = (error: unknown) => {\n logMessage(\"convex-auth/client\", LOG_LEVELS.ERROR, [\n \"[convex-auth] Storage event handler failed:\",\n error,\n ]);\n };\n disposeStorageListener =\n runtime.sync.subscribe(key(JWT_STORAGE_KEY), (value) => {\n if (destroyed) return;\n if (value === token) return;\n\n // Another tab signed out: propagate the sign-out here too.\n if (value === null) {\n void setToken({ shouldStore: false, tokens: null }).catch(logStorageSyncError);\n return;\n }\n\n const incoming = value as AccessToken;\n\n if (authConfirmed && token !== null) {\n // This tab holds a confirmed identity. Adopt a cross-tab token only\n // when it belongs to the SAME subject (a rotated access token) — and\n // do so without dropping confirmation, so a same-user rotation does\n // not flicker the UI through `loading`. A token for a DIFFERENT\n // subject is ignored to prevent one tab silently adopting another\n // user's identity.\n const currentSubject = decodeJwtSubject(token);\n const incomingSubject = decodeJwtSubject(incoming);\n if (currentSubject !== null && incomingSubject === currentSubject) {\n void setToken({\n shouldStore: false,\n tokens: { token: incoming },\n resyncConvexAuth: false,\n }).catch(logStorageSyncError);\n }\n return;\n }\n\n // This tab is signed out or still resolving: adopt a cross-tab sign-in\n // and run the confirmation handshake.\n void setToken({\n shouldStore: false,\n tokens: { token: incoming },\n }).catch(logStorageSyncError);\n }) ?? null;\n }\n\n bindConvexAuth();\n\n void initialize().catch((error) => {\n logMessage(\"convex-auth/client\", LOG_LEVELS.ERROR, [\n \"[convex-auth] Client initialization failed:\",\n error,\n ]);\n finalizeLoadingState();\n });\n\n const totp =\n adapters.totp ??\n createTotpClient({\n proxy,\n convex,\n requireApiRefs,\n proxyFetch,\n setTokenAndMaybeWait,\n });\n\n const device =\n adapters.device ??\n createDeviceClient({\n proxy,\n convex,\n requireApiRefs,\n proxyFetch,\n setTokenAndMaybeWait,\n });\n\n const auth = {\n /** Restore persisted auth state for the current runtime. */\n initialize,\n /** SSR-safe URL param reader. */\n param,\n /** Pending invite from URL or recovered from storage. Null if none. */\n get invite(): PendingInvite | null {\n const pendingInvite = getPendingInvite();\n if (!pendingInvite) return null;\n return {\n token: pendingInvite.token,\n email: pendingInvite.email,\n accept: acceptInvite,\n };\n },\n /** Complete an OAuth callback from a URL or authorization code. */\n completeOAuth,\n /** Sign in with a provider. See {@link SignInResult} for return shape. */\n signIn,\n /** Sign out and clear all token state. */\n signOut,\n subscribe,\n getSnapshot: () => publicSnapshot,\n /** TOTP two-factor authentication helpers. */\n totp,\n /** Device authorization (RFC 8628) helpers. */\n device,\n /**\n * Tear down this auth client instance.\n *\n * Removes the cross-tab `storage` event listener, clears all\n * `subscribe` subscribers, and rejects any in-flight handshake\n * waiters. Call this when the client is no longer needed\n * (e.g. on SPA unmount or hot-module replacement) to prevent\n * memory leaks and stale callbacks.\n *\n * @example\n * ```ts\n * // SvelteKit onDestroy\n * import { onDestroy } from \"svelte\";\n * const auth = client({ convex, api: api.auth });\n * onDestroy(() => auth.destroy());\n * ```\n *\n * @example\n * ```ts\n * const unsubscribe = auth.subscribe((state) => console.log(state.status));\n *\n * // Later, during cleanup:\n * unsubscribe();\n * auth.destroy();\n * ```\n */\n destroy: () => {\n destroyed = true;\n clearDeauthGrace();\n // Reject ALL waiters, not just those at the current epoch — leftover\n // waiters from an earlier epoch would otherwise leak their timers and\n // never-settled promises across an unmount / hot-module replacement.\n rejectAllHandshakeWaiters(\n createHandshakeError(\"AUTH_HANDSHAKE_REJECTED\", {\n reason: \"destroyed\",\n }),\n );\n disposeStorageListener?.();\n subscribers.clear();\n },\n } as AuthClient<Api> & { webauthn?: NonNullable<ClientAdapters[\"webauthn\"]> };\n if (webauthnAdapter !== undefined) auth.webauthn = webauthnAdapter;\n return auth;\n}\n"],"mappings":"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"}
|