Skip to content

Releases: VortexNYC/convex-auth

@vortex-api/convex-auth@3.2.0

Choose a tag to compare

@github-actions github-actions released this 30 Sep 22:27
639f6f9

Minor Changes

  • c1acd65: Add global-scope webhook endpoint lifecycle operations. createWebhookEndpoint accepts org-less (platform-wide) endpoints, but the existing setWebhookEndpointStatus / deleteWebhookEndpoint require an organizationId and fail closed on org-less rows — so global endpoints could be created but never disabled, archived, or deleted.

    New component functions mirror the org-scoped set: listGlobalWebhookEndpoints, setGlobalWebhookEndpointStatus, and deleteGlobalWebhookEndpoint. They resolve through a dedicated requireGlobalWebhookEndpoint guard that rejects org-scoped endpoints, matching the org ops which already reject org-less ones — each scope fails closed on the other.

  • 1bd3bc6: feat(auth): Google One Tap sign-in (#206)

    Adds convexAuth({ oneTap }) — verifies a Google Identity Services ID token
    against Google's JWKS via the existing createGoogleProvider path (RS256,
    iss/aud/exp, optional hd), then provisions the same google identity as
    redirect OAuth so both flows resolve to one account.

    • Server action signInOneTap with disableSignUp,
      disableImplicitSignUp, requireEmailVerification, nonce claim pinning,
      and per-subject rate limiting (on by default)
    • oneTap: true inherits clientId/hd/maxTokenAge/fetchImpl from
      oauth.google; clientSecret is never required
    • useAuthActions().signInOneTap({ idToken }) on React and React Native,
      signIn.oneTap({ idToken }) on the Better Auth client
    • SSR cookie-mode support via the signInOneTap proxy intent and Next.js
      serialization
  • 1ad578f: Add phone-number sign-in via SMS OTP (convexAuth({ phone }) → sendPhoneOtp/verifyPhoneOtp, signIn.phoneOtp). Numbers normalize to E.164, land on users.phoneNumber/phoneNumberVerified with a by_phoneNumber index, and provision a phoneOtp identity that fails closed on collisions. Session-gated phone-verification codes verify an existing account's number and link the phoneOtp identity. Reuses createTwilioSmsOtpSender; optional per-phone rate limiting on sends and verifies. Migration CLI now writes username/displayUsername/phoneNumber/phoneNumberVerified onto migrated users instead of dropping them.

  • 7110b45: Add username/password authentication. convexAuth({ username }) enables signUpUsername / signInUsername actions, POST /api/auth/sign-up/username and /api/auth/sign-in/username HTTP routes, signUpUsername/signInUsername SSR proxy intents, useAuthActions().signUpUsername/signInUsername, and client.signUp.username/client.signIn.username on the Better Auth-shaped client.

    Users gain optional username (normalized, unique via by_username) and displayUsername (original casing) fields. provisionFromIdentity now rejects username collisions inside the serialized mutation with duplicateField: "username" | "email", and exposes getUserAndAccountByUsername. Username identities use provider: "username", issuer: "native" and are never linkable by email claim.

    Sign-in shares the email/password pipeline: migrated bcrypt credentials rehash to argon2id on first verify (CAS-guarded), per-username rate limiting, and the same 2FA / trusted-device challenge flow. Optional usernameValidator, length bounds, checkBreach, requireVerifiedEmail (for attached emails), captcha, and onExistingUserSignUp are supported.

Patch Changes

  • 5ffc617: fix(component): use by_admin index in listAdminAudits when adminId filter is set — previously scanned the whole audit table on every filtered page

  • 6be730d: Fix proxied password/username sign-in rejecting the cookie-substituted landingVerifier with ArgumentValidationError. The SSR proxy substitutes the landing verifier from its cookie into signIn, signInUsername, and callback args; the two password actions never declared the field, so every cookie-mode sign-in 400'd once a verifier cookie existed (the middleware mints one on any navigation). Both actions now accept and echo it into the session result, matching the nativeAuthSessionValidator contract used by the component's redirect-stamping routes. Also fixes getConvexNextjsOptions emitting { url: undefined } when callers pass convexUrl as a present-but-unset key — convex/nextjs warns "deploymentUrl is undefined" on every call with an explicit undefined and will treat it as an error in a future release; omitting the key preserves the NEXT_PUBLIC_CONVEX_URL env fallback cleanly.

  • 8e8f8e2: Redirect with unsupported_provider instead of an unhandled 500 when the OAuth signin or callback route is hit with an unconfigured provider id.

  • 0b29e27: convex-auth preflight now validates convex/auth.config.ts — presence plus the canonical createConvexAuthProvider wiring — via a new providerConfigPath backend-setup option wired in the CLI's backendSetupFromConvexDir. The existing "Convex auth config" slot validates auth.ts (the convexAuth() callsite) and its default path is corrected to convex/auth.ts to match; previously it pointed at convex/auth.config.ts while requiring a convexAuth snippet that file never contains, so programmatic callers with defaults could false-fail it.

    Nothing validated auth.config.ts before this change, which let a real consumer-facing bug ship: without the provider registration, Convex rejects any request carrying a session JWT as caller auth with NoAuthProvider before the function runs — proxied signOut cleared cookies and returned 400 while the server-side session stayed valid, so a stolen cookie pair remained live after "logout". convex-auth check/bug-report already diagnosed the file; preflight now fails on its absence too.

    Heads-up for consumers running convex-auth preflight or auth:preflight: a previously green run will now fail if convex/auth.config.ts is missing or lacks createConvexAuthProvider — that failure is the check working (the file is required wiring); --skip-backend-setup remains the escape hatch.

  • 0b68330: fix(runtime): map expected 2FA errors to 401/400 on HTTP routes — Invalid two factor code, Invalid two factor token, Not enrolled, and Unauthorized previously surfaced as 500 unknown on /api/auth/two-factor/*

  • 4cb8d26: Make pending two-factor challenges retryable: a wrong TOTP or backup code
    no longer consumes the challenge — it records a failed attempt and the user
    can retry. Verification attempts are reserved atomically before the code is
    checked, so after 5 attempts the challenge stops accepting guesses even
    under concurrent requests. authVerificationCodes gains an optional
    failedAttempts field and the component exposes reserveVerificationAttempt.

@vortex-api/convex-auth@3.1.0

Choose a tag to compare

@github-actions github-actions released this 29 Sep 06:48
7bcc384

Minor Changes

  • 96de151: feat: Clerk/WorkOS migration foundation — verifyPassword now accepts imported bcrypt digests ($2a$/$2b$/$2y$ via hash-wasm, isolate-safe) and any verified non-native or below-floor credential lazily rehashes to argon2id with a CAS guard; new @vortex-api/convex-auth/migrations export ships the normalizers (Clerk API+CSV, WorkOS) plus idempotent internal migrateOrganization/migrateMembership writers with role seeding, invited→active promotion, and migration-marker attach guards

  • f68d288: Component contract tightening:

    • Passkey options require a non-empty origin — generatePasskeyRegistrationOptions and generatePasskeyAuthenticationOptions now reject calls without origin (or with an empty string/array), so every new challenge records its expected origin. Challenges created before this change still verify via the caller-supplied fallback during their TTL. The runtime wrapper already requires origin in config; direct component callers must now pass it.
    • Return validators filled in — generatePasskeyRegistrationOptions (v.any() → record), native/accounts (updateAccountTokens → null, createAccount → id, getAccountBySubject → doc|null), native/codes (all four functions).
    • rateLimits honesty — recordAttempt/checkRateLimit no longer emit count (the backing rate-limiter doesn't expose bucket counts; it was always 0). count is now optional in the return type. cleanupExpiredRateLimits documented as a kept no-op for contract compatibility.
  • ce51bce: Add the Hono server adapter — @vortex-api/convex-auth/hono.

    convexAuthMiddleware is the whole server surface in one app.use("*"): it proxies POST /api/auth auth intents, lands OAuth/magic-link session triples as HttpOnly cookies, rotates near-expiry tokens onto downstream responses (rebuilding immutable responses when needed), and strips auth cookies from cross-origin requests. convexAuthProxyHandler mounts the proxy as an explicit route instead. Session helpers (getConvexAuthSession, getConvexAuthToken, convexAuthCookieState, getAuthServerState) take the Hono context and are rotation- and revocation-aware. Works on @hono/node-server, Bun, Deno, and any fetch-runtime Hono supports. hono is an optional peer (>=4); the adapter is type-only against it.

    The request-scoped session bookkeeping (rotation overrides, CORS-strip tracking, verifySession memoization) moved from the TanStack adapter into the shared ssr/ substrate — it was already framework-agnostic; ./tanstack-start/server re-exports it unchanged.

  • f626e8f: Declare engines.node >=22 and drop Node 20 from the tested matrix.

    Node 20 reached end-of-life in April 2026 and no longer receives security fixes. The library's Node-touching surface (server-side helpers used inside Next.js handlers, TanStack server functions, and SSR glue) is now tested on Node 22 and Node 24 only. Consumers on Node 20 will see an install-time engines warning rather than a silent unsupported environment; the deployed component code is unaffected — it runs in the Convex V8 isolate, which is not Node at all.

Patch Changes

  • cf4ef80: Component review hardening — three correctness fixes:

    • OAuth requireEmailVerification ordering: the check now runs before provisionFromIdentity and account-token writes, so a denied sign-in no longer leaves orphaned user/identity rows or overwrites stored OAuth tokens on a rejected request.
    • provisionFromIdentity linking invariant: implicit email-match linking now requires identity.emailVerified === true — callers can no longer link an unverified provider email into an existing account via the default allowLink: true. The trustedProviders escape is preserved through a new explicit allowUnverifiedEmailLink arg. Provisions can no longer downgrade a verified user's emailVerified flag (monotonic).
    • Admin listUsers search: partial substring search now works — the previous path exact-matched by_email/by_name then substring-filtered the already-exact set, so partial terms silently returned nothing. Search now paginates with filterWith (matching the admin audit pattern) and supports cursors.
  • d0ea688: chore: move set-cookie-parser to devDependencies — it is only used by the internal conformance harness, so it no longer lands in consumer installs

  • fa5582f: perf: index sweep — passkey revocation now reads authSessions through by_credential_id instead of scanning every session for the user, and 17 never-referenced index declarations (16 unique names) are removed across users, auth_identities, auth_admin_audits, webhook_deliveries, auth_passkeys, organization_members, agent_auth_audit_events, auth_md_*, and mcp_oauth_signing_keys (one-line re-add if a query path lands for them)

  • d0ea688: Bump internal dependencies: @convex-dev/rate-limiter 0.3 → 0.4 (additive release — RateLimiter constructor, limit, and check APIs unchanged; verified against the full rate-limit suite) and convex-helpers 0.1.80 → 0.1.124. Also bumps @simplewebauthn/server, jose, oauth4webapi, and otpauth to latest patches.

@vortex-api/convex-auth@3.0.1

Choose a tag to compare

@shlomokabareti shlomokabareti released this 25 Sep 03:39
c123fa9

Patch Changes

  • fix(nextjs): translate :name(.*) legacy wildcards to {*name} — createRouteMatcher no longer throws on patterns like /:locale(.*) under path-to-regexp v8

v3.0.0

Choose a tag to compare

@shlomokabareti shlomokabareti released this 24 Sep 23:58
09390ac

Breaking changes

  • OidcProviderStorageAdapter.getSessionByToken requires expiresAt (#334) — presence-based enforcement meant a lazy adapter returning { userId } silently skipped session expiry. Custom storage adapters must return expiresAt (ms).
  • Deprecated provider args removed — nativeEmailAndPassword no longer accepts top-level sendVerificationEmailOnSignUp/sendVerificationEmailOnSignIn; use email.sendOnSignUp/email.sendOnSignIn.
  • resolvePermissionOverride adapter removed — express permission variance as roles; expandPermissions is unchanged.
  • Session fallback removals — revokePasskeySessions drops pre-credentialId compat (#337); session refresh no longer reads identityId from JWT claims (#373); revokeSessionFamily no longer matches familyId-less sessions (#374). Deploy the latest 2.x first if upgrading from < 2.5.x so live sessions backfill.
  • path-to-regexp v8 — advertised wildcard forms (X/(.*), glued X(.*)) keep matching identically via internal translation to {*splat}; unnamed groups like /(a|b)/ now throw a descriptive error at createRouteMatcher construction.
  • Stricter redirect validation — the OAuth signIn action validates callbackURL/errorURL/newUserURL against the trusted-origins allowlist; protocol-relative URLs and Sec-Fetch-Site bypasses closed.
  • Landing verifier binding — session-triple landings require the __convexAuthLandingVerifier cookie from the initiating browser; cross-browser landings get ?error=landing_verifier_mismatch unless requireLandingVerifier: false.

Migration guide: docs/(clients-and-migration)/v3-migration.md

Added

  • TanStack Start SSR adapter — @vortex-api/convex-auth/tanstack-start (+ /server): app-origin HttpOnly cookie sessions, server-confidential refresh token, intent-based /api/auth proxy.
  • @vortex-api/convex-auth/test — register, convexAuthTest, modules, schema for consumer convex-test harnesses; wired into the component conformance suite.
  • API keys — allowedIpRanges on issueApiKey/issueServiceOwnedApiKey in the feature-gated component (parity with the monolith).

Fixed

  • revokeOtherSessions spares the caller's whole session family (excludeFamilyId on revokeSessionsForUser).
  • Family revocation scoped to rotated-token replay — administrative revocation replays no longer nuke the family.
  • SSR/auth hardening sweep — redirect allowlists, CSRF, CORS landings, response forwarding (7 issues).
  • Index cleanup — 8 covered prefix indexes dropped (apply on next convex dev/push); creation-ordered list endpoints keep dedicated indexes.

Published with npm provenance (sigstore).

convex-auth v2.5.2

Choose a tag to compare

@shlomokabareti shlomokabareti released this 22 Sep 19:04
5c3a437

Patch Changes

Ship complete component source in the published tarball — #351

The 2.5.1 tarball still could not install cleanly for consumers: files shipped src/convex-runtime/** and the nine convex.config.ts files, but not the component implementation itself — schema.ts, _generated/, and every function module under src/component/ — nor transitive deps (src/compat, src/mcp, src/core). Bundling the convex entry failed with Could not resolve "../../component/_generated/server.js" and the component could not deploy.

files now ships src/** wholesale (minus *.test.*, *.vitest.ts, *.type-test.ts), matching the upstream @convex-dev/auth source-publish model. Verified by bundling every component and runtime source file from the packed tarball under esbuild.

convex-auth v2.5.1

Choose a tag to compare

@shlomokabareti shlomokabareti released this 22 Sep 18:23
bccf2b3

Minor Changes

Next.js SSR adapter — #343

Add a Next.js SSR adapter with HttpOnly cookie sessions.

  • New: packages/auth/src/nextjs exports the App Router surface — a route-handler proxy (/api/auth), middleware for protected routes and proactive refresh, server-side session reads, and server-action support. Sessions live in HttpOnly cookies; tokens never reach client JS.
  • Demo: examples/nextjs is a working App Router app verified end-to-end against a local Convex deployment.
  • SSR renders verify the session server-side and fail closed: expired or revoked sessions redirect to sign-in instead of flashing authenticated UI.

Patch Changes

Session-rotation convergence — #343

  • Two parallel requests presenting the same refresh token (the normal SSR pattern) no longer destroy the session. rotateSession marks rotatedAt and reports "converge" inside a 15s grace window; convergeSession mints a bounded sibling session+refresh pair in the same family. Bounds: ≤8 grace redemptions per predecessor, ≤10 live sessions per family, convergence requires a live member.
  • updateSession now resolves the session's identity — OAuth/passkey sessions refresh correctly. authSessions gains identityId; authRefreshTokens gains rotatedAt/graceRedemptions.

Family-aware revocation — #349, #344

  • signOut, stopImpersonation, user-facing revokeSession, and admin revokeSession all revoke the whole session family. revokeSessionsForUser now marks revoked sessions' refresh tokens too.
  • Audit: session.sign_out (voluntary), session.revoke (sessions-list), refresh_token_reuse (replay detection) — admin actions stay on auth_admin_audits.

Published package fix — #345 (thanks @david-potgieter)

  • The files allowlist shipped only a handful of convex-runtime modules — installs failed with Could not resolve "./convexAuth.js" and friends. The tarball now ships the full src/convex-runtime/** tree; convex/types export conditions point at src/convex-runtime/index.ts.

convex-auth v2.4.1

Choose a tag to compare

@shlomokabareti shlomokabareti released this 18 Sep 14:29
63d0e98

Highlights

  • Argon2id password hashing — argon2id-wasm (Rust → WASM), ~10x faster in the Convex isolate. New hashes in standard PHC format ($argon2id$v=19$m=19456,t=2,p=1$); legacy packed, Better Auth scrypt, and PBKDF2 hashes still verify — no migration needed.
  • Passkeys, web + React Native/Expo — registration, sign-in, revocation, and rename, bound to the caller's identity. Conditional-UI autofill on web; react-native-passkeys-backed ceremonies on iOS/Android verified by the same server actions. Validated end-to-end on both platforms.
  • Session hardening — every session-validating path now rejects revoked sessions; session lists never expose raw tokens (isCurrent computed server-side, revokeSession takes sessionId with an ownership check); 2FA pending challenges are opaque hashed tokens, not JWTs.
  • Passkey revocation cascade — sessions and refresh tokens minted by a passkey record credentialId; revoking the passkey (or detecting a cloned authenticator via signature-counter regression) revokes them all, across arbitrarily large histories including pre-credentialId legacy rows.
  • Admin dashboard — impersonation with audited sessions, user search + detail panel, organization member/role management, audit-log filters.
  • Query exhaustion fixes — all revocation and code-consume paths paginate to exhaustion; sessions, refresh tokens, and live codes beyond the first index page can no longer survive revocation.

What's Changed

Password hashing

  • Switch password hashing to argon2id-wasm; WASM init failures now surface as errors instead of reading as "wrong password".

Passkeys

  • Passkey management (register, list, revoke, rename) bound to ctx.auth.getUserIdentity().
  • Non-increasing signature counter revokes the passkey and writes a passkey_counter_regression audit event (cloned-authenticator detection).
  • renamePasskey mutation via auth.renamePasskey, usePasskeys().rename, onRename prop on PasskeyManager.
  • passkey.maxPasskeysPerUser config (default 10), re-checked at verification time.
  • passkey.origin accepts string | string[]; attestationType, userVerification, residentKey, authenticatorAttachment configurable.
  • usePasskeys({ autofill: true }) enables WebAuthn conditional-UI sign-in.
  • Challenges record rpID, accepted origins, effective identifier, and UV policy at generation; verification uses stored values.
  • Relaxed-UV passkey sign-in on a 2FA account returns twoFactorRedirect; the pending challenge carries credentialId so the minted session stays revocable with the passkey. Both usePasskeys hooks store the challenge token so ConvexVerifyTwoFactorForm completes the flow.
  • Passkey revocation paginates all lookups to exhaustion, attributes legacy sessions via stored-JWT identityId, and consumes in-flight pending challenges.
  • usePasskeys fetches ceremony options at invocation time, not mount — no stale challenges.
  • Audit events: passkey_registered, passkey_authenticated, passkey_renamed, passkey_revoked.
  • React Native / Expo: usePasskeys under @vortex-api/convex-auth/react-native/passkeys (optional react-native-passkeys peer). Configure passkey.origin per platform including android:apk-key-hash:<hash>.

Sessions & 2FA

  • listSessions no longer returns token; items carry server-computed isCurrent.
  • revokeSession takes { sessionId } with caller-ownership verification.
  • ConvexSessionList drops currentSessionToken (web + React Native). Migration: revokeSession({ token }) → revokeSession({ sessionId }); remove the currentSessionToken prop.
  • revokedAt enforced on session resolution, token-verify action, bearer /convex/token and /session, MCP OAuth authorize, OAuth account linking, and verifySession (no more arbitrary-active-session fallback).
  • Session-family revocation, revokeSessionsForUser, refresh-token revocation, password-reset revoke, and verification-code consume/revoke paths all paginate to exhaustion.
  • authAccounts gains by_user_provider_issuer; organization_members gains by_user_status.

Admin

  • Safe impersonation: short-lived audited sessions, IMPERSONATION_SESSION_DURATION_MS config, banner + stop in the React example.
  • createUser, setRole, setUserPassword, listUsers with search/filter/sort/pagination.
  • listAdminAudits filters (action, targetType, targetId, adminId, from, to); auditsFilters dashboard controls.
  • Member list/search/pagination, updateMemberRole, removeMember dashboard wiring.
  • User search + detail side panel via usersSearch, onViewUser, selectedUser.

React Native demo

  • Single react-native version across the workspace; Metro native extensions; Expo SDK 57 alignment; Uniwind theme init + accent-color patch; session JWT no longer rendered in the header.

Full Changelog: v2.3.0...v2.4.1

convex-auth v2.3.0

Choose a tag to compare

@shlomokabareti shlomokabareti released this 16 Sep 04:37
58f4dba

What's Changed

Full Changelog: v2.0.5...v2.3.0

convex-auth v2.0.5

Choose a tag to compare

@shlomokabareti shlomokabareti released this 10 Sep 14:46

What's Changed

  • Hardened examples/oauth Convex wrappers to enforce caller identity and organization membership before listing, creating, revoking, or rotating organizations, sessions, API keys, service principals, webhooks, and audit events.
  • Added examples/oauth/convex/authz.ts helpers (requireCaller, requireMatchingUserId, requireOrganizationMembership).
  • Added repository security scanning and CI:
    • pnpm run scan:security (deps, secrets, SAST, leaks)
    • .github/workflows/security-audit.yml
    • Semgrep, TruffleHog, Secretlint integration
    • pnpm-audit-exceptions for unfixable image-size transitive advisories
  • Bumped convex-auth to 2.0.5.

Verification

  • pnpm run check ✅
  • pnpm run typecheck ✅
  • pnpm run build ✅
  • pnpm test (1275 tests) ✅
  • pnpm run scan:security ✅
  • Live Browser-Use IDOR probes against perfect-dragon-698 rejected cross-user / cross-organization access.

convex-auth v2.0.4

Choose a tag to compare

@shlomokabareti shlomokabareti released this 10 Sep 13:47

What's changed

  • Remove module-scope JWT private-key/JWKS caching so rotated keys take effect immediately and warm processes cannot sign with stale material.
  • Add the missing convex/auth.config.ts native Convex auth provider wiring in the OAuth demo.
  • Share the pending 2FA challenge token through ConvexAuthProvider context so the sign-in page and reusable verification form stay in sync.
  • Improve toError in convex-auth-client.ts to preserve string/object messages.
  • Increase JWT iat future-clock skew leeway.
  • Update mintOAuthState for the new getJwtPrivateKey return shape and include the key id in the header.
  • Clean up demo lint warnings and harden forgot/reset/verify-email response handling.

Verification

  • pnpm run check, typecheck, build, test (1275 tests), lint, lint:convex, scan:secrets all pass locally.
  • convex-auth check and convex-auth preflight pass for examples/oauth.
  • Browser (Safari MCP): 2FA TOTP sign-in, backup codes, sessions, API keys, service principals + verify-as-session, webhooks, audit, and preflight UI all verified.