Changelog

All changes, fixes, and updates

Every release shipped to Better Auth, straight from GitHub.

CHANGELOG

better-auth

Features

  • Added support for server-side accountInfo calls with an optional userId parameter, allowing trusted callers to read provider profiles without constructing session headers (#9813)

Bug Fixes

  • Clarified that viewBackupCodes is a server-only function not accessible via HTTP in its API documentation (#9822)
  • Fixed Google One Tap authenticating the wrong user when the presented Google account is already linked to a different local user, by resolving identity through the shared OAuth path
  • Fixed storeStateStrategy defaulting to "cookie" instead of "database" when only secondaryStorage is configured, preventing oversized-cookie errors on platforms like AWS Lambda (#9591)
  • Fixed updateUserInfoOnLink not being applied when linking accounts through the standard OAuth redirect flow (#8758)
  • Fixed oidc-provider and mcp plugins accepting invalid redirect_uri schemes such as javascript: and data: (#9838)
  • Fixed organization logo not accepting null, preventing users from clearing an existing logo on create and update (#9842)

For detailed changes, see CHANGELOG

@better-auth/sso

Bug Fixes

  • Fixed SAML Single Logout leaving the user signed in due to session deletion matching on row ID instead of session token
  • Fixed ambiguous internalAdapter helper methods that could silently match the wrong account or wipe all sessions for a user (#9818)
  • Fixed a high-severity XML injection vulnerability in signed SAML assertions by updating samlify to 2.13.1 (GHSA-34r5-q4jw-r36m) (#9821)

For detailed changes, see CHANGELOG

@better-auth/api-key

Bug Fixes

  • Fixed verifyApiKey rejecting keys created under a non-default configId when the request omitted configId (#9794)

For detailed changes, see CHANGELOG

@better-auth/core

Bug Fixes

  • Fixed a silent failure in consumeOne when an adapter's deleteMany returned a non-numeric value, now surfacing a clear error (#9831)

For detailed changes, see CHANGELOG

@better-auth/expo

Bug Fixes

  • Fixed sign-in being lost on Expo when a provider issues large tokens, by splitting oversized account cookies across multiple storage keys (#9815)

For detailed changes, see CHANGELOG

@better-auth/oauth-provider

Bug Fixes

  • Fixed POST /oauth2/register bypassing the clientPrivileges create check, allowing unauthorized dynamic client registration (#9837)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@bytaesu, @gustavovalverde, @Vishesh-Verma-07

Full changelog: v1.6.12...v1.6.13

better-auth

Bug Fixes

  • Fixed field index ordering in getMigration migrations. (#9691)
  • Fixed synthetic user construction to exclude extra fields. (#9347)
  • Fixed session cookie refresh headers not being forwarded when resolving sessions. (#9667)
  • Fixed changeEmail to return an error when emailVerification.sendVerificationEmail is missing, and URL-encoded callbackURL in verify-email links. (#9614)
  • Fixed callbackURL URL-encoding in verify-email links for OAuth account linking and username sign-in. (#9792)
  • Fixed role.authorize to reject empty action lists and correctly evaluate OR conditions on unknown resources. (#9603)
  • Fixed missing exports of AdminClientOptions and OrganizationClientOptions. (#9642)
  • Fixed email OTP sign-in failing with captcha errors under default captcha settings. (#9596)
  • Fixed parseJSON to properly decode escape sequences in quoted strings. (#9617)
  • Fixed cookie parsing to tolerate missing spaces after `
  • Fixed getTrustedOrigins to respect the dynamic baseURL protocol option. (#9644)
  • Fixed request mutation by cloning the request before passing it to the sendVerificationEmail callback. (#9619)
  • Added accessTokenExpiresIn config option to genericOAuth for providers that omit expires_in in their token response. (#9799)
  • Fixed oauth-proxy to forward specific error codes instead of collapsing all errors into user_creation_failed. (#9723)
  • Fixed oauth-proxy flows failing with state_mismatch when production and preview environments use different secrets. (#9385)
  • Fixed OAuth callback errors to forward specific error codes (state_not_found, state_invalid, state_mismatch) instead of the generic please_restart_the_process code. (#9788)
  • Fixed OAuth state validation failures to redirect to the per-flow errorCallbackURL instead of the default error page. (#9789)
  • Fixed OpenAPI schema generation to emit unique operationIds for endpoints that expose multiple HTTP methods. (#9721)
  • Fixed organization invitations silently routing users to the wrong team when team IDs contained a comma. (#9616)
  • Fixed deleteOrganization and removeMember to roll back on failure instead of leaving orphaned rows. (#9630)
  • Fixed stateless session cache refresh to preserve the real session expiry instead of resetting it. (#8817)
  • Fixed a session cookie leak that allowed session_token and session_data cookies to be captured and replayed to bypass 2FA when cookie caching is enabled. (#9639)
  • Fixed missing username validation on the admin createUser endpoint. (#9464)

For detailed changes, see CHANGELOG

@better-auth/oauth-provider

Bug Fixes

  • Fixed expired magic-link tokens and OAuth authorization codes to be reliably rejected, and corrected their error codes. (#9624)
  • Fixed the registration_endpoint to be hidden from .well-known metadata unless dynamic client registration is enabled. (#9448)
  • Fixed Basic Auth credential parsing to accept client_secret values containing colons. (#9601)
  • Fixed the consent update endpoint to return NOT_FOUND when the referenced client no longer exists. (#9600)
  • Fixed OAuth and OIDC metadata discovery for path-prefixed issuers. (#9668)

For detailed changes, see CHANGELOG

@better-auth/core

Features

  • Added toCamelCase, toSnakeCase, toPascalCase, and toKebabCase utilities to @better-auth/core/utils/string. (#9727) – @better-auth

Bug Fixes

  • Fixed Sign in with Apple to accept hashed nonces for native iOS sign-in. (#8870)
  • Fixed verifyAccessToken to return proper unauthorized errors for invalid token verification failures. (#9655)

For detailed changes, see CHANGELOG

@better-auth/sso

Bug Fixes

  • Fixed hook rejections in SSO OIDC and SAML callbacks to redirect to errorCallbackURL instead of returning a JSON error. (#9702)
  • Updated XML parser dependency to a patched release to resolve security alerts. (#9662)
  • Fixed SSO OIDC callback to URL-encode error values in redirect query strings. (#9722)

For detailed changes, see CHANGELOG

@better-auth/drizzle-adapter

Bug Fixes

  • Fixed the Drizzle adapter dropping OR clauses when mixed with AND conditions in where queries. (#9756)
  • Fixed MySQL insert-return handling with a robust cascading fallback strategy wrapped in a transaction. (#9665)

For detailed changes, see CHANGELOG

@better-auth/passkey

Bug Fixes

  • Fixed a crash when passkey transports is undefined. (#9746)
  • Fixed passkey challenges to be consumed atomically, preventing replay attacks, and improved error status codes for failed registrations and authentications. (#9622)

For detailed changes, see CHANGELOG

@better-auth/api-key

Bug Fixes

  • Fixed TypeScript TS4023 declaration emit errors by adding better-call as a peer dependency. (#9759)

For detailed changes, see CHANGELOG

@better-auth/electron

Bug Fixes

  • Fixed cookie serialization to percent-encode values containing special characters like `

For detailed changes, see CHANGELOG

@better-auth/kysely-adapter

Bug Fixes

  • Fixed SQLite introspectors (BunSqliteDialect, NodeSqliteDialect) incorrectly reporting tables as views. (#9615)

For detailed changes, see CHANGELOG

@better-auth/stripe

Bug Fixes

  • Improved URL normalization and Stripe search query escaping to handle edge cases correctly. (#9661)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@bytaesu, @chdanielmueller, @cyphercodes, @gustavovalverde, @jsj, @kgarg2468, @Paola3stefania, @ping-maxwell, @reslear

Full changelog: v1.6.11...v1.6.12

better-auth

Bug Fixes

  • Added an error code to the change-email-disabled response to help clients identify the rejection reason (#8948)
  • Fixed access-control role statement types so predefined organization roles expose only their configured permissions in TypeScript (#9507)
  • Fixed the anonymous plugin to correctly call onLinkAccount when email verification triggers auto sign-in (#9548)
  • Fixed device authorization to bind pending codes to the verifying session, preventing any authenticated user from approving or denying another user's device code (#9573)
  • Fixed a race condition in the magic-link plugin that allowed concurrent requests to mint multiple sessions from the same single-use token (#9572)
  • Fixed the oidc-provider and mcp plugins to require client_secret for confidential clients on refresh token grants and use constant-time secret comparison (#9576)
  • Hardened oidc-provider and mcp plugins to follow OAuth 2.1: removed "none" from advertised signing algorithms, defaulted plain PKCE off, and rejected incomplete PKCE parameters (#9575)
  • Fixed an invitation takeover vulnerability by enabling requireEmailVerificationOnInvitation by default and extending the verification gate to getInvitation and listUserInvitations (#9577)

For detailed changes, see CHANGELOG

@better-auth/oauth-provider

Bug Fixes

  • Fixed a race condition in the OAuth authorization-code grant that allowed concurrent token-exchange requests to mint multiple token sets from the same authorization code
  • Fixed a race condition in OAuth refresh-token rotation that allowed concurrent requests to fork refresh token families, and added a unique constraint on oauthRefreshToken.token
  • Fixed OAuth account linking to require a verified local email before linking an OAuth identity to a local account (#9578)

For detailed changes, see CHANGELOG

@better-auth/core

Bug Fixes

  • Fixed an invalid import list in the instrumentation module (#9582)
  • Widened advanced.ipAddress.ipv6Subnet to accept any valid IPv6 prefix length (0-128) instead of a narrow set of values (#9545)

For detailed changes, see CHANGELOG

@better-auth/scim

Bug Fixes

  • Fixed session cleanup to run when admin, anonymous, or SCIM operations delete a user (#9162)
  • Fixed generateSCIMToken to reject providerId values that collide with built-in account providers, preventing tokens from authenticating against unintended accounts (#9579)

For detailed changes, see CHANGELOG

@better-auth/sso

Bug Fixes

  • Fixed SSO provider registration to require an org admin or owner role, preventing any organization member from registering providers (#9220)
  • Fixed an SSRF vulnerability by validating user-supplied OIDC endpoint URLs against a public-routable host allowlist at provider registration and update (#9574)

For detailed changes, see CHANGELOG

auth

Features

  • Added an atomic claimOne adapter primitive for consuming database rows without race conditions (#9560)

Bug Fixes

  • Renamed the claimOne adapter primitive to consumeOne and added internalAdapter.consumeVerificationValue for atomically consuming verification rows (#9568)

For detailed changes, see CHANGELOG

@better-auth/api-key

Bug Fixes

  • Fixed API key rate-limited responses to return HTTP 429 instead of 401, so clients can distinguish throttling from authentication failures (#9505)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@dipan-ck, @GautamBytes, @gustavovalverde, @Kvizas, @ping-maxwell, @stewartjarod

Full changelog: v1.6.10...v1.6.11

better-auth

Bug Fixes

  • Exposed refreshUserSessions on the internal adapter (#7764)
  • Fixed organization invitation roles to accept dynamic access control roles (#9437)
  • Improved link accessibility (#9521)
  • Fixed incorrect email casing in one-tap, email-otp, and email-verification flows (#9369)
  • Fixed OpenAPI schema for POST /sign-in/social mis-declaring required fields (#9268)
  • Added a warning when the cookie plugin is placed last in the plugins array (#9484)
  • Fixed useSession not revalidating after admin impersonation starts or stops (#9402)
  • Fixed duplicate Set-Cookie headers being emitted on redirect responses from social sign-in and magic-link endpoints (#9497)
  • Fixed the bearer plugin writing duplicate cookie entries when merging the session token into request headers (#9387)
  • Fixed captcha plugin breaking the email-otp flow (#8339)
  • Fixed email enumeration protection not applying when emailAndPassword.autoSignIn is false (#8839)
  • Fixed a TypeError caused by non-ASCII characters in OAuth error descriptions on redirect (#9065)
  • Renamed internalAdapter.deleteAccount parameter from accountId to id to reflect that it queries by primary key (#9503)
  • Fixed OAuth callbacks accepting a missing provider account ID, which could link accounts under an undefined id (#9456)
  • Fixed cancelPendingInvitationsOnReInvite having no effect, where re-inviting the same email always returned USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION (#9453)
  • Fixed a TS2742 type error caused by missing re-exports when using additionalFields in the organization plugin (#9349)
  • Fixed useActiveMemberRole retaining a previous user's role after sign-out in SPA flows (#9440)
  • Fixed setActiveTeam to only accept teams from the currently active organization (#9239)
  • Added authClient.siwe.getNonce() as a compatibility alias for the SIWE nonce endpoint (#9461)
  • Fixed callbackURL being ignored on signIn.username, so it now redirects correctly like signIn.email (#9475)

For detailed changes, see CHANGELOG

@better-auth/oauth-provider

Bug Fixes

  • Fixed sessionId typing in refresh token types to be optional, matching the schema (#9324)
  • Fixed stale prompt=login consent continuations not completing after a forced login
  • Exported OAuth provider helper types needed for portable downstream TypeScript declaration emit (#9406)
  • Fixed prompt=login not being honored after consent continuation, preventing session bypass (#9344)
  • Added database indexes to OAuth provider foreign-key fields in generated schemas (#9389)

For detailed changes, see CHANGELOG

@better-auth/stripe

Bug Fixes

  • Fixed onSubscriptionUpdate to receive the raw stripeSubscription object, and fixed onSubscriptionCancel to receive the post-update subscription row instead of a stale snapshot (#9354)
  • Fixed getCheckoutSessionParams overriding internally managed Stripe Checkout Session fields such as success_url, cancel_url, customer, and line_items (#9481)
  • Fixed onSubscriptionDeleted, onTrialEnd, and onTrialExpired receiving a stale pre-update subscription snapshot instead of the post-update row (#9356)
  • Fixed getCheckoutSessionParams overriding free trial and internal metadata, which could hide trial periods and create duplicate subscription rows on webhook (#9474)
  • Renamed internal subscription webhook variables for clarity (#9355)

For detailed changes, see CHANGELOG

@better-auth/api-key

Bug Fixes

  • Fixed api.verifyApiKey not validating the key's configId against the request body (#9393)

For detailed changes, see CHANGELOG

@better-auth/core

Bug Fixes

  • Fixed Cloudflare Workers instrumentation imports to use a no-op entry when OpenTelemetry is not installed (#9395)

For detailed changes, see CHANGELOG

@better-auth/passkey

Bug Fixes

  • Fixed passkey autofill authentication to return a handled cancellation instead of an unhandled error when it cannot start (#9429)

For detailed changes, see CHANGELOG

@better-auth/sso

Bug Fixes

  • Fixed /sso/saml2/sp/metadata throwing NOT_FOUND for providers configured via defaultSSO (#9398)

For detailed changes, see CHANGELOG

auth

Bug Fixes

  • Fixed auth init generating broken MySQL and PostgreSQL Kysely database configs (#9455)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@bytaesu, @Craga89, @cyphercodes, @dipan-ck, @dvanmali, @GautamBytes, @gustavovalverde, @IcanDivideBy0, @jaydeep-pipaliya, @mausic, @onmax, @ping-maxwell, @programming-with-ia, @zllovesuki

Full changelog: v1.6.9...v1.6.10

better-auth

Bug Fixes

  • Fixed instrumentation resolution in the adapter factory so edge and browser environments correctly use the pure variant (#9340)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@erquhart

Full changelog: v1.6.8...v1.6.9

better-auth

Bug Fixes

  • Fixed mapProfileToUser fallback for OAuth providers that may omit email from their profile response (#9331)
  • Fixed support for passing id through beforeCreateTeam and beforeCreateInvitation hooks (#9253)

For detailed changes, see CHANGELOG

@better-auth/oauth-provider

Bug Fixes

  • Fixed authorization flows that do not include a state parameter (#9328)

For detailed changes, see CHANGELOG

@better-auth/passkey

Bug Fixes

  • Fixed incompatibility with TypeScript's exactOptionalPropertyTypes compiler option (#9270)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@baptisteArno, @gustavovalverde, @ping-maxwell

Full changelog: v1.6.7...v1.6.8

better-auth

Features

  • Added support for an array of client IDs as the ID token audience in social providers (#9292)

Bug Fixes

  • Fixed response headers being lost when an APIError is thrown (#9211)
  • Fixed browser and edge runtime errors by serving a no-op ./instrumentation module in those environments (#9281)
  • Fixed a crash when parsing OAuth2 state with an undefined request body (#9293)
  • Fixed callbackOnVerification not being called when updatePhoneNumber is enabled (#4894)

For detailed changes, see CHANGELOG

@better-auth/oauth-provider

Bug Fixes

  • Fixed the userinfo endpoint to read the Authorization header from request context when using auth.api (#9244)

For detailed changes, see CHANGELOG

@better-auth/passkey

Bug Fixes

  • Fixed passkey authentication verification not returning the user (#5209)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@gustavovalverde, @Kinfe123, @ouwargui, @ramonclaudio, @stewartjarod, @TanishValesha

Full changelog: v1.6.6...v1.6.7

better-auth

Bug Fixes

  • Fixed preservation of the Partitioned attribute when forwarding Set-Cookie headers (#9235)
  • Fixed boolean coercion for the disableRefresh query parameter in custom session validation (#9214)
  • Fixed incorrect inference of team additional fields in the organization plugin (#9266)
  • Added support for removing a phone number via updateUser({ phoneNumber: null }) (#9219)

For detailed changes, see CHANGELOG

@better-auth/core

Features

  • Added mapConcurrent, a bounded-concurrency async utility, at @better-auth/core/utils/async (#9227) – @better-auth

Bug Fixes

  • Made @opentelemetry/api an optional peer dependency (#9111) – @opentelemetry

For detailed changes, see CHANGELOG

@better-auth/api-key

Bug Fixes

  • Improved performance by running secondary-storage API key lookups in parallel (#9187)

For detailed changes, see CHANGELOG

@better-auth/expo

Bug Fixes

  • Fixed session loading to read cached data from SecureStore on app startup, eliminating the login screen flash for returning users (#8953)

For detailed changes, see CHANGELOG

@better-auth/oauth-provider

Bug Fixes

  • Fixed several SSRF vulnerabilities by unifying host classification and closing loopback bypass vectors across packages (#9226)

For detailed changes, see CHANGELOG

@better-auth/sso

Bug Fixes

  • Fixed an ESM/CJS compatibility issue when loading samlify (#9262)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@bytaesu, @gustavovalverde, @jonathansamines, @ping-maxwell, @terijaki

Full changelog: v1.6.5...v1.6.6

better-auth

Bug Fixes

  • Clarified recommended production usage for the test utils plugin (#9119)
  • Fixed session not refreshing after /change-password and /revoke-other-sessions (#9087)

For detailed changes, see CHANGELOG

@better-auth/oauth-provider

Security

  • Fixed GHSA-xr8f-h2gw-9xh6, a high-severity authorization bypass in @better-auth/oauth-provider where unprivileged authenticated users could create OAuth clients when deployments relied on clientPrivileges to restrict client creation. – @better-auth
  • First patched stable version: @better-auth/oauth-provider@1.6.5. – @better-auth @1
  • Note: the published beta line (1.7.0-beta.0 and 1.7.0-beta.1) remains affected until a fixed beta release is published.

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@GautamBytes, @ramonclaudio

Full changelog: v1.6.4...v1.6.5

better-auth

Bug Fixes

  • Fixed forceAllowId UUIDs set in database hooks being ignored on PostgreSQL adapters when advanced.database.generateId is set to "uuid" (#9068)
  • Reverted 2FA enforcement scope to credential sign-in paths only, so magic link, email OTP, OAuth, SSO, passkey, and other non-credential sign-in flows no longer trigger a 2FA challenge (#9205)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@GautamBytes, @gustavovalverde

Full changelog: v1.6.3...v1.6.4

better-auth

Features

  • Added support for Stripe SDK v21 and v22 (#9084)

Bug Fixes

  • Fixed incorrect operationId for the requestPasswordResetCallback endpoint in the OpenAPI spec (#9072)
  • Fixed dynamic baseURL resolution from request headers for direct auth.api calls (#9113)
  • Fixed isMounted race condition that caused excessive requests per second in the client (#9078)
  • Fixed nullable schema for the get-session endpoint in the OpenAPI 3.1 spec (#8389)
  • Fixed checkout and upgrade flows to omit quantity for metered prices (#8926)
  • Fixed 2FA enforcement to trigger on all sign-in paths, including magic-link, OAuth, passkey, email-OTP, and SIWE (#9122)
  • Fixed backup code updates to respect the configured storeBackupCodes storage strategy after verification (#7231)

For detailed changes, see CHANGELOG

@better-auth/oauth-provider

Features

  • Added customTokenResponseFields callback for injecting custom fields into token endpoint responses, and hardened authorization code validation (#9118)

Bug Fixes

  • Hardened dynamic baseURL resolution for direct auth.api calls and plugin metadata helpers (#9131)
  • Fixed unauthenticated dynamic client registration to silently override confidential auth methods to public, improving compatibility with MCP clients (#9123)

For detailed changes, see CHANGELOG

@better-auth/sso

Bug Fixes

  • Fixed multiple SAML response processing bugs, including ACS URL generation, encryption field handling, and provider config parsing (#9097)

For detailed changes, see CHANGELOG

@better-auth/stripe

Bug Fixes

  • Fixed prototype pollution vulnerability when merging user-supplied metadata in the Stripe plugin (#9164)

For detailed changes, see CHANGELOG

auth

Bug Fixes

  • Fixed tsconfig path alias resolution for extended configs and mid-path wildcards in the CLI (#9032)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@bytaesu, @Byte-Biscuit, @gustavovalverde, @Oluwatobi-Mustapha, @ping-maxwell, @ramonclaudio

Full changelog: v1.6.2...v1.6.3

better-auth

❗ Breaking Changes

  • Prevented unverified TOTP enrollment from blocking sign-in (#8711)

Migration: Schema migration required.

Add the verified column to the twoFactor table, then regenerate/apply your ORM migration.

  • Prisma: run npx auth@latest generate, then npx prisma migrate dev (or npx prisma db push) and npx prisma generate.
  • Drizzle: run npx auth@latest generate, then npx drizzle-kit generate and npx drizzle-kit migrate.

Existing rows do not need a backfill because the column defaults to true.

Features

  • Included enabled 2FA methods in sign-in redirect response (#8772)

Bug Fixes

  • Fixed OAuth state verification against cookie-stored nonce to prevent CSRF (#8949)
  • Fixed infinite router refresh loops in nextCookies() by replacing cookie probe with header-based RSC detection (#9059)
  • Fixed cross-provider account collision in link-social callback (#8983)
  • Included RelayState in signed SAML AuthnRequests (#9058)

For detailed changes, see CHANGELOG

@better-auth/oauth-provider

Bug Fixes

  • Fixed multi-valued query params collapsing through prompt redirects (#9060)
  • Rejected skip_consent at schema level in dynamic client registration (#8998)

For detailed changes, see CHANGELOG

@better-auth/sso

Bug Fixes

  • Fixed SAMLResponse decoding failures caused by line-wrapped base64 (#8968)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@aarmful, @cyphercodes, @dvanmali, @gustavovalverde, @jaydeep-pipaliya, @ping-maxwell

Full changelog: v1.6.1...v1.6.2

better-auth

Bug Fixes

  • Fixed endpoint instrumentation to always use the route template (#9023)
  • Returned INVALID_PASSWORD for all checkPassword failures (#8902)
  • Restored getSession accessibility in generic Auth<O> context (#9017)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@bytaesu, @jonathansamines, @ping-maxwell

Full changelog: v1.6.0...v1.6.1

Blog post: Better Auth 1.6

better-auth

❗ Breaking Changes

  • Aligned freshAge calculation with session creation time instead of update time (#8762)

Migration: session.freshAge now calculates from createdAt. Set session: { freshAge: 0 } to disable the check entirely.

Features

  • Added experimental OpenTelemetry instrumentation for endpoints, hooks, middleware, and database operations (#8027)
  • Added resendStrategy option to reuse existing OTP in email-otp plugin (#8560)
  • Added enable option for HaveIBeenPwned plugin (#8728)
  • Added request metadata to sendMagicLink callback (#8571)
  • Added dedicated secret option to OAuth proxy to reduce shared key exposure (#8699)
  • Added explicit organizationId parameter in team endpoints (#5062)
  • Added WeChat social provider (#5189)
  • Added twoFactorPage config option for custom 2FA page routing (#5329)

Bug Fixes

  • Deprecated oidc-provider plugin in favor of @better-auth/oauth-provider (#8985) – @better-auth
  • Fixed access control indexing type (#8155)
  • Added origin check middleware to password reset request (#8392)
  • Fixed account cookie comparison to use provider accountId instead of internal id (#8786)
  • Fixed session id generation when using secondary storage without database (#8927)
  • Fixed skipOriginCheck array handling (#8582)
  • Fixed misleading rate limit IP warning (#8617)
  • Passed user field through idToken sign-in body for Apple name support (#8417)
  • Preserved custom session fields on focus refresh (#8354)
  • Fixed double encoded cookie (#8133)
  • Prevented revoked sessions from being restored via database fallback (#8708)
  • Resolved duplicate operationId in admin plugin endpoints (#8570)
  • Rethrew phone sendOTP failures instead of silently swallowing them (#8842)
  • Set stateless cookieCache maxAge to match session.expiresIn (#8648)
  • Threw on duplicate email when autoSignIn: false without requireEmailVerification (#8521)
  • Fixed accountInfo endpoint to use accountId instead of internal id (#8346)
  • Restored deprecated createAdapter and type exports for backwards compatibility (#8461)
  • Fixed Response return for HTTP request contexts (#7521)
  • Fixed throw: true handling in client session refresh (#8610)
  • Preserved stale session data on network or server errors (#8437)
  • Fixed bundler re-export type resolution with direct imports (#8261)
  • Fixed Set-Cookie header splitting with lookahead heuristic (#8301)
  • Prioritized generateId: "uuid" over adapter customIdGenerator (#8679)
  • Fixed date string revival in safeJSONParse for pre-parsed objects (#8248)
  • Fixed postgres migration to use CREATE INDEX (#8538)
  • Triggered sessionSignal after requesting email change in email-otp (#8816)
  • Fixed generic-oauth to use discovery userinfo endpoint instead of hardcoded URLs (#8223)
  • Normalized missing resolver path in last-login-method plugin (#8589)
  • Returned additional fields in /magic-link/verify (#7223)
  • Fixed OAuth proxy to read callback params from body for form_post (#8895)
  • Fixed double-hashing of OAuth state when storeIdentifier is hashed (#8980)
  • Fixed redirect_uri validation for prompt=none in oidc-provider (#8398)
  • Opted into FedCM to suppress Google GSI deprecation warnings (#8720)
  • Filtered null organizations in listUserInvitations (#8694)
  • Fixed multi-role user handling in invite and member removal checks (#8442)
  • Enforced authorization on SCIM management endpoints and normalized passkey ownership checks (#8843)
  • Allowed passwordless users to manage 2FA (#7243)
  • Wired twoFactorTable option to schema modelName (#8443)
  • Prevented any from collapsing auth.$Infer and client inference types (#8981)
  • Fixed updateUser to not overwrite unrelated username fields (#7570)
  • Enforced username uniqueness in updateUser (#8731)
  • Used non-blocking scrypt for password hashing to avoid blocking the event loop (#8685)

For detailed changes, see CHANGELOG

@better-auth/sso

❗ Breaking Changes

  • Enabled InResponseTo validation by default for SP-initiated SAML flows (#8736)

Migration: Set sso({ saml: { enableInResponseToValidation: false } }) to restore the previous behavior.

Features

  • Added logging for OIDC callback code validation failures (#8693)

Bug Fixes

  • Patched transitive node-forge vulnerability via samlify pin (#8838)
  • Fixed bare domain handling in domain verification (#8369)
  • Preferred UserInfo endpoint over ID token and mapped sub claim correctly (#8276)
  • Fixed provisionUser inconsistency and added provisionUserOnEveryLogin option (#8818)
  • Skipped state cookie check for SAML ACS cross-site POST (#8735)
  • Fixed verification operations to use internalAdapter (#8353)
  • Fixed ESM compatibility with namespace import for samlify (#8697)

For detailed changes, see CHANGELOG

@better-auth/mongo-adapter

❗ Breaking Changes

  • Stored UUIDs as native BSON UUID type (#8681)

Migration: New documents use native BSON UUIDs. Existing string UUIDs continue to work. No data migration required.

For detailed changes, see CHANGELOG

@better-auth/oauth-provider

Features

  • Added pairwise subject identifiers (OIDC Core Section 8) (#8292)
  • Added public client prelogin endpoint (#8214)

Bug Fixes

  • Allowed localhost subdomains in isLocalhost function (#8286)
  • Fixed fetch redirect CORS after login (#8519)
  • Allowed customIdTokenClaims to override standard claims (#7865)
  • Enforced DB-backed sessions when secondary storage is enabled (#8894)
  • Fixed dist declaration type errors (#8701)
  • Fixed dynamic baseURL config handling in init (#8649)
  • Improved allowed paths for oauth_query in client plugin (#8320)
  • Allowed customIdTokenClaims to override acr and auth_time (#8633)
  • Normalized auth_time timestamps across adapter shapes (#8761)
  • Returned JSON redirects from post-login OAuth continuation to fix CORS-blocked 302s (#8815)
  • Fixed PAR scope loss, loopback redirect matching, and DCR skip_consent (#8632)
  • Added prompt=none support (#8554)

For detailed changes, see CHANGELOG

@better-auth/stripe

Features

  • Added customizable prorationBehavior per plan (#8525)

Bug Fixes

  • Improved organization customer search by adding customerType check (#8609)
  • Replaced {CHECKOUT_SESSION_ID} placeholder in success callbackURL (#8568)
  • Returned correct priceId for annual subscriptions in list (#8810)

For detailed changes, see CHANGELOG

@better-auth/drizzle-adapter

Features

  • Added case-insensitive query support (mode: "insensitive") (#8556)

Bug Fixes

  • Fixed Drizzle adapter failing date transformation (#8289)
  • Used IS NULL / IS NOT NULL for null value comparisons (#8660)

For detailed changes, see CHANGELOG

@better-auth/expo

Features

  • Exposed plugin version field on all built-in plugins (#8750)

Bug Fixes

  • Fixed shim require issue (#8253)
  • Fixed origin override handling across mutable and immutable requests (#8405)

For detailed changes, see CHANGELOG

@better-auth/prisma-adapter

Bug Fixes

  • Moved adapter packages to dependencies to fix missing module errors (#8401)
  • Used updateMany fallback for non-unique updates (#8524)
  • Used deleteMany when deleting by non-unique field (#8314)

For detailed changes, see CHANGELOG

auth

Features

  • Migrated MCP server URL to mcp.better-auth.com (#8747)

Bug Fixes

  • Fixed path alias resolution from extended tsconfig files (#8520)
  • Treated omitted required as true in Drizzle and Prisma generators (#8614)

For detailed changes, see CHANGELOG

@better-auth/electron

Bug Fixes

  • Fixed verification operations with secondary storage (#8247)
  • Handled safeStorage encryption failures gracefully (#8530)

For detailed changes, see CHANGELOG

@better-auth/passkey

Features

  • Added pre-auth registration and WebAuthn extensions support (#7154)

Bug Fixes

  • Fixed error message strings in passkey client (#8751)

For detailed changes, see CHANGELOG

@better-auth/test-utils

Features

  • Exported adapter test suites from @better-auth/test-utils/adapter (#8564) – @better-auth

Bug Fixes

  • Removed using keyword for runtime compatibility (#8756)

For detailed changes, see CHANGELOG

@better-auth/api-key

Bug Fixes

  • Fixed turbo caching, enforced lockfile integrity, and expanded pre-commit hooks (#8892)

For detailed changes, see CHANGELOG

@better-auth/core

Bug Fixes

  • Stopped marking redirect APIErrors as span errors in OpenTelemetry traces (#8850)

For detailed changes, see CHANGELOG

@better-auth/kysely-adapter

Bug Fixes

  • Removed deprecated numUpdatedOrDeletedRows from D1 dialect (#8798)

For detailed changes, see CHANGELOG

@better-auth/telemetry

Bug Fixes

  • Used conditional exports to replace dynamic import hacks (#8458)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@aarmful, @bytaesu, @dvanmali, @Eric-Song-Nop, @formatlos, @GautamBytes, @GoPro16, @gustavovalverde, @himself65, @jonathansamines, @jslno, @mrgrauel, @NathanColosimo, @okisdev, @olliethedev, @Oluwatobi-Mustapha, @OscarCornish, @ping-maxwell, @raihanbrillmark, @sicarius97, @Sigmabrogz, @wuzgood98, @xiaoyu2er, @YevheniiKotyrlo

Full changelog: v1.5.6...v1.6.0

   🚀 Features

  • Agent auth plugin – @Bekacru
  • core: Add experimental opentelemetry instrumentation – @jonathansamines @bytaesu
  • email-otp: Add resendStrategy option to reuse existing OTP – @bytaesu
  • magic-link: Add request metadata to sendMagicLink – @mrgrauel
  • mongo-adapter: Store UUIDs as native BSON UUID – @bytaesu
  • oauth-provider: Public client prelogin endpoint – @dvanmali
  • organization: Explicit organizationId in team endpoints – @xiaoyu2er @himself65
  • social-provider: Add wechat social provider – @Eric-Song-Nop @himself65
  • stripe: Allow customizable prorationBehavior per plan – @bytaesu
  • test-utils: Export adapter test suites from @better-auth/test-utils/adapter@bytaesu
  • two-factor: Add twoFactorPage in config – @wuzgood98

   🐞 Bug Fixes

  • Handle skipOriginCheck array – @jslno
  • Prevent revoked sessions from being restored via database fallback – @bytaesu
  • api:
    • Return Response for HTTP request contexts – @gustavovalverde
  • client:
    • Handle throw:true in session refresh – @bytaesu
  • core:
    • Prioritize generateId "uuid" over adapter customIdGenerator – @bytaesu
  • docs:
    • Improve AI chat security and cleanup – @himself65
    • Add missing Encore icon to sidebar icons – @himself65
  • electron:
    • Handle safeStorage encryption failures gracefully – @jslno
  • oauth-provider:
    • Support prompt=none – @dvanmali
    • Improve allowed paths for oauth_query for client plugin – @dvanmali
    • Fix dist declaration type errors – @gustavovalverde
  • organization:
    • Filter null organizations in listUserInvitations – @raihanbrillmark
  • sso:
    • Use namespace import for samlify to fix ESM compatibility – @himself65
  • stripe:
    • Replace {CHECKOUT_SESSION_ID} placeholder in success callbackURL – @bytaesu
    • Improve organization customer search by adding customerType check – @bytaesu
    View changes on GitHub

   🐞 Bug Fixes

  • cli: Warn when old @better-auth/cli is used with better-auth v1.5.x+ – @himself65
    View changes on GitHub

   🚀 Features

  • oauth-provider: Pairwise subject identifiers (OIDC Core §8) – @gustavovalverde @himself65

   🐞 Bug Fixes

  • Pass user field through idToken sign-in body for Apple name support – @bytaesu
  • Add missing SubpageItem properties for docs-sidebar compatibility – @bytaesu
  • Add icon prop to SubpageLink component – @bytaesu
  • Correct sign-in link to dash.better-auth.com – @bytaesu
  • Restore features.tsx and align import with canary – @bytaesu
  • Add suppressHydrationWarning to video elements – @bytaesu
  • Preserve custom session fields on focus refresh – @jslno
  • Throw on duplicate email when autoSignIn: false without requireEmailVerification@himself65
  • Add origin check middleware to password reset request – @jslno
  • adapters: Restore deprecated createAdapter and type exports for backcompat – @himself65
  • blog: Fix RSS feed link path, image path and blog date – @0-Sandy
  • cli: Resolve path aliases from extended tsconfig files – @himself65
  • client: Preserve stale session data on network or server errors – @bytaesu
  • db: Use CREATE INDEX for postgres migration – @himself65
  • oauth-provider: Avoid fetch redirect CORS after login – @GautamBytes
  • oidc-provider: Validate redirect_uri for prompt=none – @jslno
  • organization: Handle multi-role users in invite and member removal checks – @himself65
  • prisma-adapter: Fall back to updateMany for non-unique updates – @himself65
  • sso: Handle bare domains in domain verification – @himself65
  • telemetry: Use conditional exports to replace dynamic import hacks – @himself65
  • two-factor: Wire twoFactorTable option to schema modelName – @himself65
    View changes on GitHub

   🐞 Bug Fixes

  • Move adapter packages to dependencies to fix missing module errors – @himself65
  • expo: Handle origin override across mutable and immutable requests – @NathanColosimo @bytaesu
    View changes on GitHub

   🐞 Bug Fixes

  • account: Use accountId instead of id in accountInfo endpoint – @NathanColosimo @himself65
  • sso: Use internalAdapter for verification operations – @himself65
    View changes on GitHub

   🐞 Bug Fixes

  • Access control indexing type – @YevheniiKotyrlo @himself65
  • Prevent double encoded cookie – @Oluwatobi-Mustapha @himself65
  • cookies:
    • Use lookahead heuristic for splitting Set-Cookie headers – @bytaesu
  • oauth-provider:
    • Allow localhost subdomains in isLocalhost function – @sicarius97 @himself65
    • CustomIdTokenClaims should override standard claims – @gustavovalverde
  • prisma-adapter:
    • Use deleteMany when deleting by non-unique field – @himself65
  • sso:
    • Prefer UserInfo endpoint over ID token and map sub claim correctly – @himself65
    View changes on GitHub

   🐞 Bug Fixes

  • client: Use direct imports to fix bundler re-export type resolution – @himself65
  • core: Revive date strings in safeJSONParse for pre-parsed objects – @himself65
  • db: Support verification operations with secondary storage – @himself65
  • expo: Avoid shim require@himself65
  • generic-oauth: Use discovery userinfo endpoint instead of hardcoded URLs – @himself65
    View changes on GitHub