@better-auth/test-utils
1.7.12 · 20 Oct 2026MIT11.2k / weekRepositoryHomepagenpmAtom feed
54 stable releases, newest first, by release line. Notes are quoted as the maintainers published them.
1.7.12
20 Oct 2026Fixed the JWT plugin to verify JWTs and session cookie-cache JWTs with the stored key's algorithm instead of the token header's, and to warn when signing or verifying a JWT without an issuer or audience
Fixed OAuth callbacks completed through the OAuth proxy to run the same checks as the standard OAuth callback, including issuer and ID token nonce validation
Fixed
auth.api.getSession()to keep the original database error aserror.causewhen the session lookup fails
[!IMPORTANT]
Action requiredIf you use ID token sign-in with a provider that has no
clientId, setclientIdto every client ID your apps sign in with.
better-authBug Fixes
Fixed the JWT plugin to verify JWTs and session cookie-cache JWTs with the stored key's algorithm instead of the token header's, and to warn when signing or verifying a JWT without an issuer or audience
Fixed OAuth callbacks completed through the OAuth proxy to run the same checks as the standard OAuth callback, including issuer and ID token nonce validation
Fixed
auth.api.getSession()to keep the original database error aserror.causewhen the session lookup fails
For detailed changes, see
CHANGELOGBug Fixes
Fixed
mapProfileToUserfailing to typecheck underexactOptionalPropertyTypeswhen it returnsundefinedforname,email, orimageFixed ID token sign-in to reject tokens when the provider has no client ID, since their audience cannot be checked
Fixed a custom Facebook
getUserInfoto no longer receive client-submitted access tokens issued to another Facebook app, and ID tokens missingexporiatare now rejectedRestored the Twitch and Paybin ID token handling from before 1.7.11, which made account info fail once the ID token stored at sign-in expired
Added a
causeoption as a third argument toAPIError.from()to keep the underlying error for server-side callers without exposing it in the response
For detailed changes, see
CHANGELOGBug Fixes
Fixed
requireMcpAuthto accept only OAuth access tokens and reject session JWTs from the JWT plugin, even when they share the issuer and audience
For detailed changes, see
CHANGELOGBug Fixes
Fixed OIDC SSO sign-in to reject ID tokens missing
exporiatand providers with an empty client ID, matching social sign-in
For detailed changes, see
CHANGELOG1.7.11
19 Oct 2026Fixed repeated PostgreSQL migration diffs for
int8columns.Fixed database-backed rate limiting returning an excessively large
X-Retry-Aftervalue.Fixed plugin
initreturns withoptions: undefinedfailing type checks underexactOptionalPropertyTypes.
better-authBug Fixes
Fixed repeated PostgreSQL migration diffs for
int8columns.Fixed database-backed rate limiting returning an excessively large
X-Retry-Aftervalue.
For detailed changes, see
CHANGELOGBug Fixes
Fixed plugin
initreturns withoptions: undefinedfailing type checks underexactOptionalPropertyTypes.Fixed ID token signature and claim validation for Twitch and Paybin sign-in. Custom Paybin issuers must publish RS256 signing keys at
<issuer>/.well-known/jwks.json.Reduced installation size while preserving OpenTelemetry instrumentation attributes.
For detailed changes, see
CHANGELOGBug Fixes
Fixed OpenAPI schemas for
/oauth2/consentand/oauth2/continueto match their redirect responses.Fixed CIMD metadata refreshes overwriting concurrent administrator changes to discovered clients.
For detailed changes, see
CHANGELOGBug Fixes
Fixed Expo social account linking with ID tokens when a session cookie is present.
For detailed changes, see
CHANGELOG1.7.10
15 Oct 2026Fixed Captcha and OpenAPI plugin subpath imports and type resolution for
better-auth/plugins/multi-sessionandbetter-auth/test.Fixed Google One Tap sign-ins not recording
googleas the last login method.Fixed imports from
better-auth/plugins/one-tapfailing with a not-exported error.
better-authBug Fixes
Fixed Captcha and OpenAPI plugin subpath imports and type resolution for
better-auth/plugins/multi-sessionandbetter-auth/test.Fixed Google One Tap sign-ins not recording
googleas the last login method.Fixed imports from
better-auth/plugins/one-tapfailing with a not-exported error.Fixed Google One Tap ignoring plugin-level
autoSelectandcancelOnTapOutsidesettings.Fixed Google One Tap sign-ins ignoring configured Google profile mapping and user info updates.
Fixed Google One Tap calling a prompt method unsupported by FedCM and retrying skipped prompts.
For detailed changes, see
CHANGELOGFeatures
Added optional self-service signing key rotation for
private_key_jwtclients.
Bug Fixes
Fixed valid DPoP proofs being rejected for parameterized routes and URLs with trailing slashes.
Fixed native clients with HTTP loopback callbacks being rejected when application type inference is enabled.
Fixed device-code clients being unable to refresh access for approved resources omitted from a token poll.
Fixed OAuth consent without a reference overwriting or reusing consent for a different reference.
Fixed dynamic client registration rejecting requests that include both supported and unsupported grants.
Fixed attempts to change cached trusted clients returning HTTP 500 instead of HTTP 400.
Improved errors for unknown OAuth
client_idvalues to sayclient not found.Fixed PKCE verifier errors to return the specified HTTP 400 response and error code.
Fixed unknown OAuth clients receiving
invalid_grantinstead ofinvalid_clientat the token endpoint.Fixed unverifiable JWT access tokens causing server errors at
/oauth2/userinfo,/oauth2/introspect, and/oauth2/revoke.
For detailed changes, see
CHANGELOGBug Fixes
Limited JWKS refetches for unknown key IDs to protect servers from repeated requests.
Fixed unreachable JWKS endpoints being reported as invalid tokens instead of server errors.
For detailed changes, see
CHANGELOGBug Fixes
Fixed MongoDB
findManyignoring ID sorting and potentially repeating or skipping paginated results.
For detailed changes, see
CHANGELOG1.7.9
15 Oct 2026Fixed
onAPIError.onErrorhandlers to finish before responses are sent and log failures without interrupting requestsAdded
isContinuationtopostLogin.shouldRedirectso users can complete authorization after selecting an organizationFixed DPoP validation for token and UserInfo requests behind proxies that rewrite request URLs
better-authBug Fixes
Fixed
onAPIError.onErrorhandlers to finish before responses are sent and log failures without interrupting requests
For detailed changes, see
CHANGELOGBug Fixes
Added
isContinuationtopostLogin.shouldRedirectso users can complete authorization after selecting an organizationFixed DPoP validation for token and UserInfo requests behind proxies that rewrite request URLs
For detailed changes, see
CHANGELOG1.7.8
14 Oct 2026Aligned cookie security attributes with RFC 6265bis
Added SvelteKit 3 to the supported peer dependency range
Fixed stale cookies persisting after
customSessionsessions expire or are revoked
better-authBug Fixes
Aligned cookie security attributes with RFC 6265bis
Added SvelteKit 3 to the supported peer dependency range
Fixed stale cookies persisting after
customSessionsessions expire or are revokedFixed
customSessiontreating session lookup errors as signed-out usersFixed sign-in failures, replay checks, and SIWE email handling with
generateId: "uuid"Removed non-returned custom fields from inferred output schema types
Fixed Kysely migration foreign keys when custom table names match another model key
Reduced organization plugin server bundles by excluding unused Zod locales
Aligned
@better-auth/utilsandbetter-calldependency versions across official packagesFixed generic OAuth providers remaining unavailable after discovery fails at startup by retrying on subsequent requests
Fixed
lastLoginMethodsign-in failures whencookieNameormaxAgeis explicitlyundefinedFixed magic link verification changing percent-encoded characters in callback URLs
Fixed type errors when using
oauthProvider()ormcp()withexactOptionalPropertyTypesFixed organization plugin type errors in projects with
skipLibCheck: falseFixed failed password resets consuming reset links or OTPs before the new password is saved
Fixed password resets and changes retaining the new password when database session revocation fails
Fixed phone number password resets to use a configured
verifyOTPcallbackFixed secondary-storage session failures leaving sign-ups, passkey registrations, or SSO sign-ins in an inconsistent state
Fixed missing device sessions and failed automatic session switching after cache eviction
For detailed changes, see
CHANGELOGBug Fixes
Fixed ID token sign-in failures caused by up to 30 seconds of clock difference with identity providers
Fixed Atlassian user info errors being mislabeled as Figma errors in logs
Fixed authentication requests hanging after interrupted async context initialization
Fixed recognition of valid
AsyncLocalStorageinitialized after importing Better Auth but before first use, and corrected misleading errors for invalid runtime-provided values in edge and browser fallbacks.Fixed after hooks being skipped for writes retained after a failed request on adapters without transactions
Fixed Microsoft Entra ID sign-in and account linking with ID tokens when signing keys omit
algFixed sign-in failures, replay checks, and SIWE email handling with
generateId: "uuid"Aligned
@better-auth/utilsandbetter-calldependency versions across official packagesFixed concurrent MongoDB writes failing with transaction conflicts by retrying transient errors
Added support for host-bearing custom-scheme redirect URIs for native OAuth clients
Fixed password resets and changes retaining the new password when database session revocation fails
Fixed secondary-storage session failures leaving sign-ups, passkey registrations, or SSO sign-ins in an inconsistent state
Fixed missing device sessions and failed automatic session switching after cache eviction
For detailed changes, see
CHANGELOGFeatures
Added support for pausing and resuming OAuth clients through
adminUpdateOAuthClientAdded support for linking protected resources when registering a client with
adminCreateOAuthClientAdded resource identifier validation for
adminCreateOAuthResourceand configuredresourcesAdded
clientIdtoclientPrivilegeschecks for actions on existing OAuth clients
Bug Fixes
Fixed sign-in failures, replay checks, and SIWE email handling with
generateId: "uuid"Aligned
@better-auth/utilsandbetter-calldependency versions across official packagesAdded support for host-bearing custom-scheme redirect URIs for native OAuth clients
Fixed startup resource-seeding failures from blocking authentication routes until restart
Fixed type errors when using
oauthProvider()ormcp()withexactOptionalPropertyTypes
For detailed changes, see
CHANGELOGBug Fixes
Aligned
@better-auth/utilsandbetter-calldependency versions across official packagesFixed SSO provider registration and updates rejecting partial
mappingvalues that omit defaulted fieldsFixed SAML providers with linked accounts rejecting IdP signing certificate rotation
Rejected OIDC SSO providers whose configured issuer does not match discovery, preventing later sign-in failures
Fixed SAML providers with linked accounts rejecting changes to their post-login
callbackUrlFixed OIDC sign-in when the mapped email claim contains a list of addresses
For detailed changes, see
CHANGELOGBug Fixes
Fixed SCIM reprovisioning of inactive users with an existing
externalIdAligned
@better-auth/utilsandbetter-calldependency versions across official packagesFixed SCIM requests silently ignoring attribute names with different letter case
Fixed concurrent SCIM updates to return
409instead of500when a unique value is already claimedFixed SCIM User and Group requests rejecting nullable attributes sent as JSON
null
For detailed changes, see
CHANGELOGBug Fixes
Aligned
@better-auth/utilsandbetter-calldependency versions across official packagesFixed failed MongoDB transaction commits leaving transactions open and blocking other writes
Fixed concurrent MongoDB writes failing with transaction conflicts by retrying transient errors
Fixed MongoDB authentication errors masking the original transaction commit failure
For detailed changes, see
CHANGELOGBug Fixes
Aligned
@better-auth/utilsandbetter-calldependency versions across official packagesFixed Drizzle sign-in, session refresh, and date filtering with string-based date columns
For detailed changes, see
CHANGELOGBug Fixes
Aligned
@better-auth/utilsandbetter-calldependency versions across official packagesFixed cross-request I/O errors from concurrent D1 queries on a shared auth instance with Kysely 0.29
For detailed changes, see
CHANGELOGBug Fixes
Fixed
@better-auth/mcptype errors withexactOptionalPropertyTypesandskipLibCheck: falseFixed type errors when using
oauthProvider()ormcp()withexactOptionalPropertyTypes
For detailed changes, see
CHANGELOGBug Fixes
Aligned
@better-auth/utilsandbetter-calldependency versions across official packages
For detailed changes, see
CHANGELOGBug Fixes
Added support for host-bearing custom-scheme redirect URIs for native OAuth clients
For detailed changes, see
CHANGELOGBug Fixes
Aligned
@better-auth/utilsandbetter-calldependency versions across official packages
For detailed changes, see
CHANGELOGBug Fixes
Aligned
@better-auth/utilsandbetter-calldependency versions across official packages
For detailed changes, see
CHANGELOGBug Fixes
Aligned
@better-auth/utilsandbetter-calldependency versions across official packages
For detailed changes, see
CHANGELOGBug Fixes
Aligned
@better-auth/utilsandbetter-calldependency versions across official packages
For detailed changes, see
CHANGELOGBug Fixes
Stopped reporting completed one-time Stripe checkouts as webhook failures
For detailed changes, see
CHANGELOGBug Fixes
Aligned
@better-auth/utilsandbetter-calldependency versions across official packages
For detailed changes, see
CHANGELOG1.7.7
30 Sep 2026- security
Fixed a critical Magic Link account-takeover vulnerability.
Fixed ID-token sign-in ignoring the social provider’s
disableSignUpsetting.Fixed OAuth Proxy accepting sign-in state as a provider profile.
Upgrade all OAuth Proxy participants together; see the OAuth Proxy upgrade guidance.
better-authMagic Link upgrade: Upgrade servers sharing verification storage together, request new Magic Links, and restart pending OAuth/SAML sign-ins. No database migration is required. See the critical advisory for affected configurations and custom storage changes.
Bug Fixes
- security
Fixed a critical Magic Link account-takeover vulnerability.
Fixed ID-token sign-in ignoring the social provider’s
disableSignUpsetting.Fixed OAuth Proxy accepting sign-in state as a provider profile.
Upgrade all OAuth Proxy participants together; see the OAuth Proxy upgrade guidance.Fixed CAPTCHA errors missing the JSON
Content-Typeheader.Fixed the active organization failing to refresh after sign-in when a session hook selects the initial organization.
Fixed rate-limit errors missing the JSON
Content-Typeheader.
For detailed changes, see
CHANGELOGFeatures
Added optional
validateRedirectUrivalidation for trusted deployments with dynamic OAuth redirect URIs.Added
verifyOAuthQueryParamsto verify signed authorization queries before rendering a custom consent page.
For detailed changes, see
CHANGELOGBug Fixes
Fixed concurrent PostgreSQL requests exceeding database-backed rate limits.
For detailed changes, see
CHANGELOGBug Fixes
Fixed
consumeOnedeleting a record after a concurrent write invalidates its original condition.
For detailed changes, see
CHANGELOG1.7.6
24 Sep 2026Added support for a
bannedUserMessagefunction that receives the banned user, allowing sign-in errors to include details such as the ban reason.Added Vercel BotID as a captcha provider for protected authentication routes.
Passwords over
maxPasswordLengthare now rejected withPASSWORD_TOO_LONGbefore hashing or verification.
better-authFeatures
Added support for a
bannedUserMessagefunction that receives the banned user, allowing sign-in errors to include details such as the ban reason.Added Vercel BotID as a captcha provider for protected authentication routes.
Bug Fixes
Passwords over
maxPasswordLengthare now rejected withPASSWORD_TOO_LONGbefore hashing or verification.Fixed React hydration mismatches when session or plugin auth queries resolve before streamed components hydrate.
Prevented older auth-query responses from overwriting newer results when requests overlap.
Fixed model identity when a custom model name matches another schema key.
Fixed schema validation for Cloudflare D1 when the Kysely dialect cannot introspect the database.
Fixed social account linking through the OAuth Proxy plugin.
For detailed changes, see
CHANGELOGBug Fixes
Fixed model identity when a custom model name matches another schema key.
Fixed schema validation for SQLite-generated primary keys, including
INTEGER PRIMARY KEYcolumns withoutAUTOINCREMENT.Fixed schema validation for Cloudflare D1 when the Kysely dialect cannot introspect the database.
For detailed changes, see
CHANGELOGBug Fixes
Fixed model identity when a custom model name matches another schema key.
Fixed schema validation for capitalized custom Prisma model names.
For detailed changes, see
CHANGELOGBug Fixes
Fixed model identity when a custom model name matches another schema key.
For detailed changes, see
CHANGELOGBug Fixes
Fixed model identity when a custom model name matches another schema key.
For detailed changes, see
CHANGELOGBug Fixes
Fixed model identity when a custom model name matches another schema key.
For detailed changes, see
CHANGELOGBug Fixes
Fixed model identity when a custom model name matches another schema key.
For detailed changes, see
CHANGELOGBug Fixes
Fixed automatic seat updates after an organization resubscribes, even when canceled subscriptions remain in its history.
For detailed changes, see
CHANGELOGauthFeatures
Added read-only
checkandcheck schemacommands to validate your configured adapter schema on demand.
For detailed changes, see
CHANGELOG1.7.5
14 Sep 2026Added
database.schemaNamesupport for direct PostgreSQL connections.Improved server-side logging for Cloudflare Turnstile verification failures.
Fixed PostgreSQL migrations incorrectly identifying tables and views across schemas.
better-authFeatures
Added
database.schemaNamesupport for direct PostgreSQL connections.
Bug Fixes
Improved server-side logging for Cloudflare Turnstile verification failures.
Fixed PostgreSQL migrations incorrectly identifying tables and views across schemas.
Fixed MySQL index validation for existing string columns.
Removed the unused optional
better-sqlite3peer dependency to prevent installation conflicts.
For detailed changes, see
CHANGELOGFeatures
Added
database.schemaNamesupport for direct PostgreSQL connections.
Bug Fixes
Fixed database option type inference outside Cloudflare Workers.
For detailed changes, see
CHANGELOGBug Fixes
Fixed unnecessary pacing of consecutive CIMD OAuth requests when metadata cannot be cached.
For detailed changes, see
CHANGELOGBug Fixes
Fixed lazy database initialization when using Drizzle relations.
For detailed changes, see
CHANGELOGFeatures
Added
database.schemaNamesupport for direct PostgreSQL connections.
For detailed changes, see
CHANGELOG1.7.3
6 Sep 2026Improved adapter test suite performance by avoiding redundant cleanup queries.
Updated adapter test suites to remove issuer-specific account requirements.
Bug Fixes
Improved adapter test suite performance by avoiding redundant cleanup queries.
Updated adapter test suites to remove issuer-specific account requirements.
For detailed changes, see
CHANGELOG1.7.2
26 Aug 2026Fixed permanent user bans to clear expiration dates from previous temporary bans.
Fixed client types with more plugins being assignable to types declaring fewer plugins.
Added warnings for invalid signed session data in the cookie cache.
better-authBug Fixes
Fixed permanent user bans to clear expiration dates from previous temporary bans.
Fixed client types with more plugins being assignable to types declaring fewer plugins.
Added warnings for invalid signed session data in the cookie cache.
Fixed disabled MyISAM indexes from satisfying migration index checks.
Fixed programmatic migrations on Cloudflare D1 while preserving existing-index validation.
Allowed
~in relative callback URLs validated by trusted-origin checks.Improved validation of relative callback and redirect URLs with paths, queries, and fragments.
Allowed same-origin form submissions with
Referrer-Policy: no-referrerwhile continuing to reject untrusted origins.Improved
getTestInstanceperformance with a faster default password hasher.Standardized built-in placeholder emails to the namespaced
{identifier}@{namespace}.placeholder.invalidformat.
For detailed changes, see
CHANGELOGBug Fixes
Fixed async context loss in Cloudflare Workers bundles with multiple runtime conditions.
Fixed auth request logs to respect the configured logger, log level, and disabled setting.
Improved validation of relative callback and redirect URLs with paths, queries, and fragments.
Standardized built-in placeholder emails to the namespaced
{identifier}@{namespace}.placeholder.invalidformat.Added synchronous and optional access to the current auth endpoint context.
For detailed changes, see
CHANGELOGBug Fixes
Fixed Client ID Metadata Document registration when clients share at least one supported grant with the server.
Improved validation of relative callback and redirect URLs with paths, queries, and fragments.
Fixed relative redirect URLs containing fragments.
For detailed changes, see
CHANGELOGBug Fixes
Fixed one-to-one Drizzle relations when
usePluralis enabled.Added validation for missing Drizzle schema fields in compound
whereclauses.
For detailed changes, see
CHANGELOGBug Fixes
Fixed programmatic migrations on Cloudflare D1 while preserving existing-index validation.
For detailed changes, see
CHANGELOGBug Fixes
Improved validation of relative callback and redirect URLs with paths, queries, and fragments.
For detailed changes, see
CHANGELOG1.7.1
18 Aug 2026Added native database transaction support to test instances for PostgreSQL and MySQL.
Updated bundled dependencies (
jose, nanostores, noble crypto packages, SimpleWebAuthn) to their latest compatible releases, with no changes required to existing projects.Fixed case-insensitive parsing of string Boolean values for SCIM User
activeand theprimarysub-attribute ofemails,phoneNumbers,addresses,roles, andentitlementsat the HTTP ingress, improving Microsoft Entra interoperability.
better-authBug Fixes
Added native database transaction support to test instances for PostgreSQL and MySQL.
Updated bundled dependencies (
jose, nanostores, noble crypto packages, SimpleWebAuthn) to their latest compatible releases, with no changes required to existing projects.
For detailed changes, see
CHANGELOGBug Fixes
Fixed case-insensitive parsing of string Boolean values for SCIM User
activeand theprimarysub-attribute ofemails,phoneNumbers,addresses,roles, andentitlementsat the HTTP ingress, improving Microsoft Entra interoperability.Added an optional SCIM-owned connection and credential catalog: configure
managedConnectionsto allow trusted server code to create runtime tenant connections and issue, rotate, and revoke bearer credentials through server-onlyauth.apimethods, without a code-defined connection or an application-owned verifier.Fixed an issue where trusted server code could not retain a terminal connection binding before a dynamic SCIM connection's first authenticated request when supplying a provisioning domain during decommissioning.
For detailed changes, see
CHANGELOGBug Fixes
Fixed SSO provider registration to allow reusing a SCIM connection ID, as SCIM connections no longer participate in the authentication provider namespace.
Fixed SAML assertion signature verification to validate signatures on the raw assertion instead of trusting an already-parsed response, and enforced signing policy and size limits on SP metadata.
wantAssertionsSignednow correctly controls whether the SP requires signed assertions, matching real-world IdP signing behavior.
For detailed changes, see
CHANGELOGBug Fixes
Fixed Client ID Metadata Document caching to follow shared-cache freshness rules: the plugin now prefers
s-maxageovermax-ageandExpires, honorss-maxage=0, conditionally revalidates withETagorLast-Modified, and treats invalid or duplicate freshness directives as immediately stale. Concurrent refreshes now converge on a single client-resource link instead of failing on a unique constraint.
For detailed changes, see
CHANGELOGBug Fixes
Fixed native adapter transactions for raw database instances (better-sqlite3,
node:sqlite,bun:sqlite,mysql2,pg) passed directly asdatabase, matching the behavior of the explicit{ db }/{ dialect }config shapes. Plugins requiring native transactions (such as@better-auth/scim) now work correctly when using the quickstartdatabase: new Database(...)form.
For detailed changes, see
CHANGELOGBug Fixes
Fixed scope error responses so MCP clients now receive a
403with an RFC 6750insufficient_scopeWWW-Authenticatechallenge naming every missing scope, allowing clients to request all needed scopes in a single authorization request.
For detailed changes, see
CHANGELOGauthBug Fixes
Fixed the CLI to refuse adding required columns without default values to already-populated tables
For detailed changes, see
CHANGELOG1.7.0
18 Aug 2026- breaking
Moved database joins out of
experimentalinto the stableadvanced.database.joinsoptionMigration: Replace
experimental: { joins: true }withadvanced: { database: { joins: true } }. Drizzle and Prisma users should regenerate their schema (npx auth@latest generate) so it includes the required relations. - breaking
Scoped account identity by trusted issuer, keying accounts on
(issuer, accountId)Migration: Accounts now require
Account.issuer. Read provider identity fromaccountInfo.account.accountId, dropmapping.idfrom SSO configs, and give themicrosoftEntraIdhelper a concrete tenant GUID. Apply the account-identity backfill in the 1.7 upgrade guide before deploying. - breaking
Required captcha endpoint entries to match full auth paths, with wildcard support
Migration: Replace partial paths such as
/sign-inwith explicit wildcards like/sign-in/*or/sign-in/**.
Blog post: Better Auth 1.7
better-authBreaking Changes
- breaking
Moved database joins out of
experimentalinto the stableadvanced.database.joinsoptionMigration: Replace
experimental: { joins: true }withadvanced: { database: { joins: true } }. Drizzle and Prisma users should regenerate their schema (npx auth@latest generate) so it includes the required relations. - breaking
Scoped account identity by trusted issuer, keying accounts on
(issuer, accountId)Migration: Accounts now require
Account.issuer. Read provider identity fromaccountInfo.account.accountId, dropmapping.idfrom SSO configs, and give themicrosoftEntraIdhelper a concrete tenant GUID. Apply the account-identity backfill in the 1.7 upgrade guide before deploying. - breaking
Required captcha endpoint entries to match full auth paths, with wildcard support
Migration: Replace partial paths such as
/sign-inwith explicit wildcards like/sign-in/*or/sign-in/**. - breaking
Moved the MCP plugin into its own
@better-auth/mcppackage built on the OAuth providerMigration: Install
@better-auth/mcpand@better-auth/cimd, add the now-requiredjwt()plugin, and move options nested underoidcConfigto flatmcp({ ... })options. RenamewithMcpAuthtorequireMcpAuthandmcpHandlertocreateMcpProtectedRequestHandler. Regenerate the schema (npx auth migrate):oauthApplicationbecomesoauthClient, plus newoauthRefreshTokenandoauthClientAssertiontables. - breaking
Added OIDC back-channel logout so ending a session cuts off every connected app's API access
Migration: Introspecting an access token whose session has ended now returns
{ active: false }, and/oauth2/userinforejects it. Clients opt into notifications by registeringbackchannel_logout_uri. Run the schema migration for the newoauthClientandoauthAccessTokencolumns. - breaking
Modeled OAuth protected resources explicitly, with per-resource TTLs, scopes, claims, and signing pins
Migration:
validAudiencesis removed: move each resource identifier intoresourcesand link restricted clients throughoauthClientResource.@better-auth/mcpnow requires an explicitresource. Runnpx @better-auth/cli generateand apply the migration before deploying. - breaking
Decoupled SCIM provisioning from the organization plugin
Migration: SCIM configuration, client APIs, database schema, and the Group model are all replaced, and provisioning state cannot migrate in place. Follow the SCIM cutover in the 1.7 upgrade guide, including a full directory reprovision, before resuming traffic.
- breaking
Added OTP-only two-factor enablement with a discriminated
enableTwoFactorresponseMigration:
enableTwoFactornow returns amethodfield ("otp"or"totp"); narrow on it before readingtotpURIandbackupCodes. Passmethod: "otp"for OTP enrollment, which requiresotpOptions.sendOTP. - breaking
Resolved the auth origin from
Hostby default when using a dynamicbaseURLMigration: If your proxy exposes the public hostname only through
x-forwarded-host, setadvanced.trustedProxyHeaders: true. Deployments where the proxy rewritesHost(nginx default, Vercel, Cloudflare, Netlify) are unaffected. - breaking
Added unique lookup indexes for the device authorization
deviceCodeanduserCodecolumnsMigration: Resolve duplicate code values before applying the migration. MySQL and SQL Server installations must also convert both columns to bounded strings and clean up values longer than 191 characters.
- breaking
Enforced S256 PKCE in the Electron sign-in flow and hardened custom-scheme origin checks
Migration: Upgrade the
@better-auth/electronclient and server together and add your app's scheme totrustedOrigins. Thecode_challenge_methodparameter anddisableOriginOverrideoption are removed, and host-bearing custom-scheme entries now match that host exactly. - breaking
Identified Microsoft Entra accounts by the stable
oidclaimMigration: Migrate existing Microsoft account rows created from
subbefore upgrading. Tokens without a validoidare rejected. - breaking
Required a Google client ID before Google One Tap verifies ID tokens
Migration: Configure
oneTap({ clientId })orsocialProviders.google.clientId. - breaking
Removed the deprecated
oidcProviderpluginMigration: Move OIDC authorization-server integrations to
@better-auth/oauth-provider. - breaking
Rewrote the generic OAuth plugin as a first-class social provider with OAuth 2.1 defaults
Migration: Replace
signIn.oauth2({ providerId })withsignIn.social({ provider }),oauth2.link()withlinkSocial(), and dropgenericOAuthClient(). Callbacks move to/api/auth/callback/:id,pkcenow defaults totrue, andissuerandrequireIssuerValidationare removed in favor of OIDC discovery. - breaking
Separated OAuth device grant ownership into
oauthDeviceAuthorization()Migration: The OAuth integration replaces the optional
resourcecolumn withoauthClientIdandresources, so regenerate and apply the schema. Let pending device codes expire before upgrading from an earlier 1.7 prerelease. - breaking
Verified provider
id_tokenswith a single shared verifierMigration: Custom
UpstreamProviderimplementations replace the removedverifyIdTokenmethod with anidTokenconfig carrying a JWKS source, issuer, and audience. PayPal clientid_tokensign-in now returnsID_TOKEN_NOT_SUPPORTED; its redirect flow is unchanged.
Features
Added
clientAssertionsupport to the Microsoft Entra ID social providerMade the
Authinstance directly fetchableAdded per-provider
requireEmailVerificationfor social sign-inAdded a
user.validateUserInfogate for rejecting an identity before a user is created or linkedAdded
hydrateSessionsouseSessionreturns server-fetched data on the first renderAdded compound table indexes to plugin database schemas
Added
allowIdpInitiatedsupport for IdP-initiated flows through a secure server-side bounceAdded RP-initiated logout so
signOut()can also sign users out of the OpenID providerAdded
refreshTokenParamsfor forwarding extra parameters on generic OAuth token refreshVerified discovery
id_tokensagainst the provider JWKS and enabledid_tokensign-in for generic OAuthAdded the OAuth device authorization grant (RFC 8628)
Added DPoP sender-constrained access tokens (RFC 9449)
Added the
at_hashclaim to ID tokens issued alongside an access token, per OIDC Core §3.1.3.6Added
private_key_jwtclient authentication for token endpoint requests (RFC 7523)Sent
Cache-Control: no-storeon every OAuth response that carries credentialsAdded per-request
additionalParamsandloginHintacrosssignIn.social,linkSocial, andsignIn.ssoAdded a server-trusted OAuth state channel, fixing anonymous account linking in in-app browsers
Allowed passing
userIdandorganizationIdto thelistUserTeamsAPIAdded
organization.getOrganization()for metadata-only fetchesAdded a server-only
consumePhoneNumberOTPAPI for custom phone OTP flowsAdded JWKS-backed asymmetric JWTs for the session cookie cache
Added transactional OIDC user resolution for SSO sign-ins
Added an immutable username option
Allowed omitting the username plugin's separate
displayUsernamefield
Bug Fixes
Allowed test instances to enable native database transactions for PostgreSQL and MySQL.
Refreshed bundled dependencies (jose, nanostores, the noble crypto packages, and SimpleWebAuthn) to their latest compatible releases. These updates are backward compatible and require no changes to existing projects.
Widened the
drizzle-kitpeer dependency rangeDecoupled the session cookie cache from JWT plugin internals
Allowed
auth migrateto add required columns with static defaults and nullable unique columns to existing tablesBound the ID token
nonceto the authorization request in the generic OAuth redirect flowFixed a sign-up deadlock when JWT cookie caching ran on a single-connection SQLite database with native transactions
Created new OAuth accounts inside the user creation transaction
Derived the OAuth
redirect_urifrom the per-request base URL in multi-host deploymentsPreserved previously granted
account.scopevalues across re-authentication and token refreshPreserved the resolved OAuth user when
overrideUserInforeturnsnullFired session-delete hooks and revoked bound OAuth tokens for preserved sessions on
secondaryStorageIssued SIWE nonces before the wallet address and chain ID are known
Fixed client plugin composition so One Tap, Electron, and Expo type-check with
createAuthClientSingle-sourced HTTP Basic credential encoding and decoding for OAuth client authentication
For detailed changes, see
CHANGELOGBreaking Changes
- breaking
Moved the MCP plugin into its own
@better-auth/mcppackage built on the OAuth providerMigration: Install
@better-auth/mcpand@better-auth/cimd, add the now-requiredjwt()plugin, and move options nested underoidcConfigto flatmcp({ ... })options. RenamewithMcpAuthtorequireMcpAuthandmcpHandlertocreateMcpProtectedRequestHandler. Regenerate the schema (npx auth migrate):oauthApplicationbecomesoauthClient, plus newoauthRefreshTokenandoauthClientAssertiontables. - breaking
Added OIDC back-channel logout so ending a session cuts off every connected app's API access
Migration: Introspecting an access token whose session has ended now returns
{ active: false }, and/oauth2/userinforejects it. Clients opt into notifications by registeringbackchannel_logout_uri. Run the schema migration for the newoauthClientandoauthAccessTokencolumns. - breaking
Aligned OAuth client registration and metadata with MCP authorization 2026-07-28
Migration: Add
applicationTypeand nullableclientDiscoveryIdcolumns, deduplicate existing(clientId, resourceId)links before the new compound unique index, then drop the legacytypeandpubliccolumns. ReplaceclientCredentialGrantDefaultScopeswith per-clientclientCredentialsScopes, backfilling every client to `` and reassigning approved machine scopes after an audit.mcp()no longer enables unauthenticated DCR: compose it withcimd()or enable both DCR flags explicitly. - breaking
Enforced the
max_ageauthorization request parameterMigration: Users who authenticated longer ago than the requested
max_ageare now sent back to log in, and the ID token'sauth_timereflects the fresh login. Flows that relied onmax_agebeing ignored will prompt again. - breaking
Made ID token claim authority explicit, reserving OIDC protocol claims for the provider
Migration:
customIdTokenClaims, extension claims, and per-issuanceidTokenClaimscan no longer set protocol claims such as issuer, subject, audience,nonce,auth_time,acr,amr, orazp; use namespaced custom claims instead. ID tokens now carryacr: "0"and discovery advertises only"0". - breaking
Modeled OAuth protected resources explicitly, with per-resource TTLs, scopes, claims, and signing pins
Migration:
validAudiencesis removed: move each resource identifier intoresourcesand link restricted clients throughoauthClientResource.@better-auth/mcpnow requires an explicitresource. Runnpx @better-auth/cli generateand apply the migration before deploying. - breaking
Bound OAuth client authentication to the grant being issued
Migration: Remove
grantTypefromprovider.authenticateClient(...), and return{ clientId, confirmation? }from a customOAuthClientAuthenticationStrategy.authenticateinstead of a client record. - breaking
Bound RFC 8707 resource indicators to the authorization grant
Migration: Token and refresh requests may only narrow the authorization's
resource; a broader request returnsinvalid_target.customAccessTokenClaimsnow receives aresourcesarray in place of theresourcestring. Run the schema migration to add the new resource columns. - breaking
Returned RFC-compliant OAuth error envelopes from validation failures
Migration: Authorization errors now redirect to a registered client's trusted redirect URI with
stateandissinstead of rendering the server error page, and confidential clients must use their registeredtoken_endpoint_auth_method. - breaking
Rewrote the generic OAuth plugin as a first-class social provider with OAuth 2.1 defaults
Migration: Replace
signIn.oauth2({ providerId })withsignIn.social({ provider }),oauth2.link()withlinkSocial(), and dropgenericOAuthClient(). Callbacks move to/api/auth/callback/:id,pkcenow defaults totrue, andissuerandrequireIssuerValidationare removed in favor of OIDC discovery. - breaking
Separated OAuth device grant ownership into
oauthDeviceAuthorization()Migration: The OAuth integration replaces the optional
resourcecolumn withoauthClientIdandresources, so regenerate and apply the schema. Let pending device codes expire before upgrading from an earlier 1.7 prerelease.
Features
Added token endpoint client authentication configuration across the OAuth stack
Added the
@better-auth/cimdClient ID Metadata Document pluginAdded the OAuth device authorization grant (RFC 8628)
Added DPoP sender-constrained access tokens (RFC 9449)
Added an extension surface for registering grants, client authentication methods, discovery metadata, and claim contributors
Added a refresh token reuse interval that replays the same response for duplicate refresh requests
Allowed confidential DCR clients to complete authorization-code flows without PKCE
Added the
at_hashclaim to ID tokens issued alongside an access token, per OIDC Core §3.1.3.6Made token introspection consistent across opaque and JWT tokens and scoped it to the audience
Exposed the issuing
sessionIdtoid_tokenclaim contributorsHonored requested UserInfo claims through a claim registry
- removed
Removed the
silenceWarningsoption and the well-known endpoint warnings it suppressed Added protected dynamic client registration using RFC 7591 initial access tokens
Added
private_key_jwtclient authentication for token endpoint requests (RFC 7523)Sent
Cache-Control: no-storeon every OAuth response that carries credentialsAdded a server-trusted OAuth state channel, fixing anonymous account linking in in-app browsers
Bug Fixes
Enforced RFC 8628 request cardinality and client authentication on the device flow endpoints
Accepted the OpenID Provider issuer as the
audofprivate_key_jwtclient assertionsAccepted UserInfo bearer tokens sent in a form-encoded request body
Allowed nonce-bound confidential clients to request
offline_accesswithout PKCEReturned a
401 invalid_tokenchallenge from/oauth2/userinfofor invalid tokensCompleted the RP-initiated logout flow with form-encoded
POSTsupport and confirmation pagesDeferred token revocation and back-channel logout delivery until the session deletion commits
Accepted form-encoded POST authorization requests and rejected unsupported OIDC request objects
Handled voluntary and essential
acrclaim requests per OIDC CoreKept
profileandemailscope claims on the UserInfo response instead of ID tokensMade the
private_key_jwtjtisingle-use atomic across processesMade
redirect_uriconditional at the token endpoint, required only when the authorization included onePreserved client key metadata and the requested authentication method during dynamic client registration
Redirected authorization requests missing
response_typeto the verified client redirect URIRejected authorization code replay with
invalid_grantand revoked tokens issued from that codeReported
unsupported_token_typewhen revoking a JWT access tokenRequired the
openidscope for authorization requests that use theclaimsparameterReturned
invalid_grantwhen a client presents a refresh token issued to another clientMCP clients that hit a scope wall now learn exactly which scopes to ask for: missing protected scopes produce a
403with an RFC 6750insufficient_scopechallenge naming every one of them, so clients can request them in a single authorization redirect.Single-sourced HTTP Basic credential encoding and decoding for OAuth client authentication
For detailed changes, see
CHANGELOGBreaking Changes
- breaking
Moved database joins out of
experimentalinto the stableadvanced.database.joinsoptionMigration: Replace
experimental: { joins: true }withadvanced: { database: { joins: true } }. Drizzle and Prisma users should regenerate their schema (npx auth@latest generate) so it includes the required relations. - breaking
Scoped account identity by trusted issuer, keying accounts on
(issuer, accountId)Migration: Accounts now require
Account.issuer. Read provider identity fromaccountInfo.account.accountId, dropmapping.idfrom SSO configs, and give themicrosoftEntraIdhelper a concrete tenant GUID. Apply the account-identity backfill in the 1.7 upgrade guide before deploying. - breaking
Decoupled SCIM provisioning from the organization plugin
Migration: SCIM configuration, client APIs, database schema, and the Group model are all replaced, and provisioning state cannot migrate in place. Follow the SCIM cutover in the 1.7 upgrade guide, including a full directory reprovision, before resuming traffic.
- breaking
Identified Microsoft Entra accounts by the stable
oidclaimMigration: Migrate existing Microsoft account rows created from
subbefore upgrading. Tokens without a validoidare rejected. - breaking
Verified provider
id_tokenswith a single shared verifierMigration: Custom
UpstreamProviderimplementations replace the removedverifyIdTokenmethod with anidTokenconfig carrying a JWKS source, issuer, and audience. PayPal clientid_tokensign-in now returnsID_TOKEN_NOT_SUPPORTED; its redirect flow is unchanged.
Features
Added
clientAssertionsupport to the Microsoft Entra ID social providerAdded per-provider
requireEmailVerificationfor social sign-inAdded a
user.validateUserInfogate for rejecting an identity before a user is created or linkedAdded compound table indexes to plugin database schemas
Added
allowIdpInitiatedsupport for IdP-initiated flows through a secure server-side bounceAdded RP-initiated logout so
signOut()can also sign users out of the OpenID providerAdded
refreshTokenParamsfor forwarding extra parameters on generic OAuth token refreshAdded an
includeGrantedScopesoption to the Google providerAdded DPoP sender-constrained access tokens (RFC 9449)
Added
private_key_jwtclient authentication for token endpoint requests (RFC 7523)Sent
Cache-Control: no-storeon every OAuth response that carries credentialsAdded per-request
additionalParamsandloginHintacrosssignIn.social,linkSocial, andsignIn.ssoAdded JWKS-backed asymmetric JWTs for the session cookie cache
Added transactional OIDC user resolution for SSO sign-ins
Bug Fixes
- security
Routed CIMD
client_idSSRF checks through the shared host classifier, which now rejects IPv4-compatible IPv6, the 6to4 relay prefix, and site-local addresses Derived the OAuth
redirect_urifrom the per-request base URL in multi-host deploymentsPreserved previously granted
account.scopevalues across re-authentication and token refreshFixed client plugin composition so One Tap, Electron, and Expo type-check with
createAuthClientSingle-sourced HTTP Basic credential encoding and decoding for OAuth client authentication
For detailed changes, see
CHANGELOGBreaking Changes
- breaking
Scoped account identity by trusted issuer, keying accounts on
(issuer, accountId)Migration: Accounts now require
Account.issuer. SSO subjects are protocol-defined (subfor OIDC, signedNameIDfor SAML) andmapping.idis removed; a manual SAML config without metadata XML must setidpMetadata.entityID. Apply the account-identity backfill in the 1.7 upgrade guide before deploying. - breaking
Added rolling SAML certificate rotation by accepting an array of IdP signing certificates
Migration:
getSSOProvider,listSSOProviders, andupdateSSOProvidernow always returnsamlConfig.certificateas an array, so drop anyArray.isArraybranching. Registration rejects SAML configs with no signing-cert source withCERT_SOURCE_MISSING. - breaking
Hardened the
validateUserInfosource contract so it cannot be bypassed or spoofedMigration:
createUsernow fails closed whenvalidateUserInfois configured but no endpoint context or provisioning source is available. Read SSO metadata fromsource.ssoinstead ofsource.oauth, and handle thesource.methodvaluessso-oidcandsso-saml. - breaking
Hardened SAML response validation for
InResponseTo, audience restriction, andSessionIndexMigration:
allowIdpInitiatednow defaults tofalse. Setsaml.allowIdpInitiated: trueto keep accepting unsolicited SAML responses. - breaking
Consolidated the SAML ACS endpoint, made
spMetadataoptional, and fixed Single LogoutMigration: Point your IdP's ACS URL at
/sso/saml2/sp/acs/:providerId;/sso/saml2/callback/:providerIdis removed.callbackUrlis now the post-auth redirect only, and the unuseddecryptionPvk,additionalParams,idpMetadata.entityURL, andidpMetadata.redirectURLfields are gone.
Features
Added a
user.validateUserInfogate for rejecting an identity before a user is created or linkedAdded
allowIdpInitiatedsupport for IdP-initiated flows through a secure server-side bounceAdded
private_key_jwtclient authentication for token endpoint requests (RFC 7523)Added per-request
additionalParamsandloginHintacrosssignIn.social,linkSocial, andsignIn.ssoAdded a server-trusted OAuth state channel, fixing anonymous account linking in in-app browsers
Added transactional OIDC user resolution for SSO sign-ins
Extended
resolveUserto SAML sign-ins and hardened the provider lifecycleAdded
additionalFieldssupport onssoProvider
Bug Fixes
Allowed an SSO provider registration to reuse a SCIM connection ID, since SCIM connections no longer share the authentication provider namespace.
Rejected redirecting OIDC discovery, token, userinfo, and JWKS endpoints so SSO works on Cloudflare Workers
Updated samlify to 2.13.1 for a signed-assertion XML injection fix
Upgraded samlify to 2.12.0 with XPath injection and XXE fixes
Single-sourced HTTP Basic credential encoding and decoding for OAuth client authentication
Verified SAML assertion signatures directly instead of trusting an already-parsed response, and applied the same signing policy and size limit to SP metadata as to IdP metadata.
wantAssertionsSignednow controls whether signed assertions are required rather than signed response messages, matching how IdPs sign SAML responses in practice.
For detailed changes, see
CHANGELOGBreaking Changes
- breaking
Decoupled SCIM provisioning from the organization plugin
Migration: SCIM configuration, client APIs, database schema, and the Group model are all replaced, and provisioning state cannot migrate in place. Follow the SCIM cutover in the 1.7 upgrade guide, including a full directory reprovision, before resuming traffic.
- breaking
Isolated SCIM provider connections from the organization and SSO plugins
Migration: Define connections statically, resolve them with
authentication.verifyBearerToken, or use the optionalmanagedConnectionscatalog, and connect SCIM resources to users and roles through identity and projection callbacks. Legacy SCIM state is not migrated: back it up, issue new credentials, and fully reprovision Users and Groups. - breaking
Bound personal SCIM connections to their creator and removed user-session connection management
Migration: The legacy connection management endpoints and
providerOwnershipare gone, so authorize SCIM administration in your own application. LegacyscimProviderrows and credentials are not migrated: follow the 1.7 SCIM upgrade guide, issue new credentials, and reprovision Users and Groups.
Features
Added a
user.validateUserInfogate for rejecting an identity before a user is created or linkedAdded durable SCIM Group resources with connection-scoped membership and lifecycle endpoints
Added the SCIM Enterprise User extension and the standard User attributes for interop conformance
Added a managed connection catalog and runtime connection resolution
Added
acquireActiveSCIMUserLinkfor transaction-safe authentication of provisioned users
Bug Fixes
Accepted exact case-insensitive string boolean values for SCIM User
activeand theprimarysub-attribute ofemails,phoneNumbers,addresses,roles, andentitlementsat the HTTP ingress, for Microsoft Entra interoperability.Added an optional SCIM-owned connection and credential catalog. Configure
managedConnectionsso trusted server code can create runtime tenant connections and issue, rotate, and revoke their bearer credentials through server-onlyauth.apimethods, without a code-defined connection or an application-owned verifier.Allowed trusted server code to retain a terminal connection binding before a dynamic SCIM connection's first authenticated request by supplying its provisioning domain during decommissioning.
Created filtered SCIM PATCH values when no target matches instead of rejecting the request
For detailed changes, see
CHANGELOGBreaking Changes
- breaking
Moved the MCP plugin into its own
@better-auth/mcppackage built on the OAuth providerMigration: Install
@better-auth/mcpand@better-auth/cimd, add the now-requiredjwt()plugin, and move options nested underoidcConfigto flatmcp({ ... })options. RenamewithMcpAuthtorequireMcpAuthandmcpHandlertocreateMcpProtectedRequestHandler. Regenerate the schema (npx auth migrate):oauthApplicationbecomesoauthClient, plus newoauthRefreshTokenandoauthClientAssertiontables. - breaking
Aligned OAuth client registration and metadata with MCP authorization 2026-07-28
Migration: Add
applicationTypeand nullableclientDiscoveryIdcolumns, deduplicate existing(clientId, resourceId)links before the new compound unique index, then drop the legacytypeandpubliccolumns. ReplaceclientCredentialGrantDefaultScopeswith per-clientclientCredentialsScopes, backfilling every client to `` and reassigning approved machine scopes after an audit.mcp()no longer enables unauthenticated DCR: compose it withcimd()or enable both DCR flags explicitly. - breaking
Modeled OAuth protected resources explicitly, with per-resource TTLs, scopes, claims, and signing pins
Migration:
mcp()now requires an explicitresourceidentifier, for exampleresource: "https://api.example.com/mcp".validAudiencesis removed: move each resource identifier intoresources. Runnpx @better-auth/cli generateand apply the migration before deploying.
Features
Added DPoP sender-constrained access tokens (RFC 9449)
Added a refresh token reuse interval that replays the same response for duplicate refresh requests
For detailed changes, see
CHANGELOGBreaking Changes
- breaking
Enforced S256 PKCE in the Electron sign-in flow and hardened custom-scheme origin checks
Migration: Upgrade the
@better-auth/electronclient and server together and add your app's scheme totrustedOrigins. Thecode_challenge_methodparameter anddisableOriginOverrideoption are removed, and host-bearing custom-scheme entries now match that host exactly. - breaking
Rewrote the generic OAuth plugin as a first-class social provider with OAuth 2.1 defaults
Migration: Replace
signIn.oauth2({ providerId })withsignIn.social({ provider }),oauth2.link()withlinkSocial(), and dropgenericOAuthClient(). Callbacks move to/api/auth/callback/:id,pkcenow defaults totrue, andissuerandrequireIssuerValidationare removed in favor of OIDC discovery.
Bug Fixes
Fixed client plugin composition so One Tap, Electron, and Expo type-check with
createAuthClient
For detailed changes, see
CHANGELOGBreaking Changes
- breaking
Switched Expo secure storage to async access so apps no longer crash when the iOS Keychain is unavailable
Migration:
getCookie()now returns a promise, and custom storage implementations must provide both synchronous and asynchronous SecureStore methods.storageAdapter.setItem()stays synchronous, so usesetItemAsync()when the write must be awaited. - breaking
Rewrote the generic OAuth plugin as a first-class social provider with OAuth 2.1 defaults
Migration: Replace
signIn.oauth2({ providerId })withsignIn.social({ provider }),oauth2.link()withlinkSocial(), and dropgenericOAuthClient(). Callbacks move to/api/auth/callback/:id,pkcenow defaults totrue, andissuerandrequireIssuerValidationare removed in favor of OIDC discovery.
Bug Fixes
Fixed client plugin composition so One Tap, Electron, and Expo type-check with
createAuthClient
For detailed changes, see
CHANGELOGBreaking Changes
- breaking
Made the
eventparameter ofonSubscriptionCancelrequiredMigration: Declare
eventas a required parameter in your callback and remove anyundefinedguards around it. - breaking
Removed the optional marker from the
onSubscriptionCanceleventparameterMigration:
eventis always supplied, so dropundefinedhandling from the callback.
For detailed changes, see
CHANGELOGauthBreaking Changes
- breaking
Accumulated OAuth granted scopes in a new
grantedScopesstring arrayMigration:
grantedScopesreplaces the comma-joinedaccount.scopestring with no read-time fallback, so backfill it from the existing values. The client provider contract is renamed fromOAuthProvidertoUpstreamProvider.
Features
Added a
create-admincommand for creating an initial admin userAdded compound table indexes to plugin database schemas
Bug Fixes
Preserved issuer-scoped account identities by restoring
Account.accountIdalongside the requiredissuerExported the generated
pgSchemabinding so drizzle-kit can emitCREATE SCHEMAfor custom PostgreSQL namespacesLoaded the auth config through c12 v4
resolveModule, adding support for theexport default { auth }shapeReverted the granted scopes architecture, restoring the previous
account.scopestorage
For detailed changes, see
CHANGELOGBreaking Changes
- breaking
Moved database joins out of
experimentalinto the stableadvanced.database.joinsoptionMigration: Replace
experimental: { joins: true }withadvanced: { database: { joins: true } }, and regenerate the schema (npx auth@latest generate) so it includes the required relations.
Features
Added compound table indexes to plugin database schemas
Added a
relations-v2entry point for projects using Drizzle Relations v2Added a
schemaNameoption that generates the Drizzle schema inside apgSchemanamespace
Bug Fixes
Exported the generated
pgSchemabinding so drizzle-kit can emitCREATE SCHEMAfor custom PostgreSQL namespaces
For detailed changes, see
CHANGELOGBreaking Changes
- breaking
Aligned OAuth client registration and metadata with MCP authorization 2026-07-28
Migration: Add
applicationTypeand nullableclientDiscoveryIdcolumns, deduplicate existing(clientId, resourceId)links before the new compound unique index, then drop the legacytypeandpubliccolumns. Client ID Metadata Documents preserve an omittedapplication_typeasnulland cannot assignclientCredentialsScopes. Composemcp()withcimd()for metadata-document clients.
Features
Added the
@better-auth/cimdClient ID Metadata Document plugin
Bug Fixes
Client ID Metadata Documents now follow shared-cache freshness rules and fail closed when freshness is ambiguous. The plugin prefers
s-maxageovermax-ageandExpires, honorss-maxage=0, revalidates conditionally with ETag or Last-Modified, and treats invalid or duplicate freshness directives as immediately stale. Concurrent refreshes converge on one client-resource link instead of failing on its unique constraint.
For detailed changes, see
CHANGELOGBreaking Changes
- breaking
Hardened atomic state transitions so concurrent requests cannot race past single-use or rate-limit guards
Migration: Custom adapters must implement native
consumeOneandincrementOne, secondary storage needs atomic consume and increment operations, and custom rate-limit storage makes oneconsumedecision per request. The read-then-delete and read-then-update fallbacks are removed.
For detailed changes, see
CHANGELOGBug Fixes
Fixed SQLite dialect bundles on Kysely 0.29 by mirroring the migration-table constants locally
Raw database instances (better-sqlite3,
node:sqlite,bun:sqlite,mysql2,pg) passed directly asdatabasenow get native adapter transactions automatically, matching the behavior of the explicit{ db }and{ dialect }config shapes. This unblocks plugins that require native transactions (such as@better-auth/scim) when the database is provided in the quickstartdatabase: new Database(...)shape.
For detailed changes, see
CHANGELOGFeatures
Added built-in translations for 22 languages
For detailed changes, see
CHANGELOGFeatures
Added compound table indexes to plugin database schemas
For detailed changes, see
CHANGELOGFeatures
Added an optional
createSessionsetting that signs the user in on successful passkey registration
For detailed changes, see
CHANGELOG1.6.31
10 Sep 2026Added an option to disable OpenTelemetry span creation for individual auth instances
Added an option to disable OpenTelemetry span creation for individual auth instances
1.6.30
17 Aug 2026Fixed concurrent cold-start requests from intermittently losing authentication or transaction context due to an async storage initialization race
Fixed automatic organization assignment via email domain to require both a verified provider domain and a verified stored user email, preventing social sign-in from joining an organization whose SSO provider merely claims that domain.
Fixed domain verification to snapshot the provider's domains at request start, returning
409withSSO_PROVIDER_CHANGEDif the provider changes during DNS resolution so callers can reload and retry.
better-authBug Fixes
Fixed concurrent cold-start requests from intermittently losing authentication or transaction context due to an async storage initialization race
For detailed changes, see
CHANGELOGBug Fixes
Fixed automatic organization assignment via email domain to require both a verified provider domain and a verified stored user email, preventing social sign-in from joining an organization whose SSO provider merely claims that domain.
Fixed domain verification to snapshot the provider's domains at request start, returning
409withSSO_PROVIDER_CHANGEDif the provider changes during DNS resolution so callers can reload and retry.
For detailed changes, see
CHANGELOG1.6.29
14 Aug 2026Improved
deleteSessionsperformance by running deletes in parallel instead of sequentiallyFixed automatic email-domain organization assignment to require both a verified provider domain and a verified user email, preventing social sign-in from granting access when an SSO provider merely claims a domain.
Fixed domain verification to snapshot the provider's domains at request start, returning
409withSSO_PROVIDER_CHANGEDif the provider changes during DNS verification so callers can reload and retry.
better-authBug Fixes
Improved
deleteSessionsperformance by running deletes in parallel instead of sequentially
For detailed changes, see
CHANGELOGBug Fixes
Fixed automatic email-domain organization assignment to require both a verified provider domain and a verified user email, preventing social sign-in from granting access when an SSO provider merely claims a domain.
Fixed domain verification to snapshot the provider's domains at request start, returning
409withSSO_PROVIDER_CHANGEDif the provider changes during DNS verification so callers can reload and retry.
For detailed changes, see
CHANGELOG1.6.28
13 Aug 2026Prevented duplicate session requests during React Suspense retries while preserving revalidation for interrupted refreshes
Restored client plugin declaration compatibility for downstream TypeScript consumers
Restored client plugin declaration compatibility for downstream TypeScript consumers
better-authBug Fixes
Prevented duplicate session requests during React Suspense retries while preserving revalidation for interrupted refreshes
Restored client plugin declaration compatibility for downstream TypeScript consumers
For detailed changes, see the
CHANGELOG.Bug Fixes
Restored client plugin declaration compatibility for downstream TypeScript consumers
For detailed changes, see the
CHANGELOG.Bug Fixes
Restored client plugin declaration compatibility for downstream TypeScript consumers
For detailed changes, see the
CHANGELOG.1.6.27
11 Aug 2026Fixed duplicate session requests being made across Suspense retries
Fixed auth endpoint types to align with
better-callFixed the CLI to align installed packages with the running CLI version
better-authBug Fixes
Fixed duplicate session requests being made across Suspense retries
For detailed changes, see
CHANGELOGBug Fixes
Fixed auth endpoint types to align with
better-call
For detailed changes, see
CHANGELOGauthBug Fixes
Fixed the CLI to align installed packages with the running CLI version
For detailed changes, see
CHANGELOG1.6.26
4 Aug 2026Fixed session cleanup on user deletion to also remove sessions from secondary storage
Fixed
findSessionsto skip invalid secondary-storage session entries without discarding other valid sessionsFixed email OTP sign-up to pass the verification type to custom OTP generators
better-authBug Fixes
Fixed session cleanup on user deletion to also remove sessions from secondary storage
Fixed
findSessionsto skip invalid secondary-storage session entries without discarding other valid sessionsFixed email OTP sign-up to pass the verification type to custom OTP generators
Fixed email OTP password reset to allow retrying after entering an invalid password
Fixed email OTP verification to no longer reveal whether an email is registered before the OTP is verified
Fixed
jwtClient()collapsingcreateAuthClienttype inference when combined with other client pluginsFixed JWT key minting inside database transactions to use the transaction-scoped adapter, preventing deadlocks on SQLite and ensuring keys commit with their surrounding transaction on Postgres and MySQL
Fixed
oAuthProxyto preserve Apple user data fromform_postcallbacksFixed
oneTapClient()collapsingcreateAuthClienttype inference when combined with other client pluginsFixed database rate-limit cleanup to complete when no background task handler is configured
Improved
nextCookiesperformance in instrumented Next.js applications by reusing thenext/headersimport promise
For detailed changes, see
CHANGELOGFeatures
Added a utility for creating stable, namespaced placeholder emails on the reserved
placeholder.invaliddomain
For detailed changes, see
CHANGELOGBug Fixes
Fixed
listKeys()andclear()to useSCANinstead ofKEYSso large keyspaces no longer block the Redis server
For detailed changes, see
CHANGELOG1.6.25
23 Jul 2026Reading the note…
1.6.24
22 Jul 2026Reading the note…
1.6.23
29 Jun 2026Reading the note…
1.6.22
26 Jun 2026Reading the note…
1.6.21
26 Jun 2026Reading the note…
1.6.20
20 Jun 2026Reading the note…
1.6.19
16 Jun 2026Reading the note…
1.6.18
12 Jun 2026Reading the note…
1.6.17
12 Jun 2026Reading the note…
1.6.16
10 Jun 2026Reading the note…
1.6.15
8 Jun 2026Reading the note…
1.6.14
2 Jun 2026Reading the note…
1.6.13
31 May 2026Reading the note…
1.6.12
29 May 2026Reading the note…
1.6.11
12 May 2026Reading the note…
1.6.10
9 May 2026Reading the note…
1.6.9
24 Apr 2026Reading the note…
1.6.8
23 Apr 2026Reading the note…
1.6.7
22 Apr 2026Reading the note…
1.6.6
21 Apr 2026Reading the note…
1.6.5
16 Apr 2026Reading the note…
1.6.4
15 Apr 2026Reading the note…
1.6.3
14 Apr 2026Reading the note…
1.6.2
9 Apr 2026Reading the note…
1.6.1
8 Apr 2026Reading the note…
1.6.0
6 Apr 2026Reading the note…
1.5.6
22 Mar 2026Reading the note…
1.5.5
11 Mar 2026Reading the note…
1.5.4
6 Mar 2026Reading the note…