fix(deps): update dependency better-auth to v1.6.2 [security]#1046
Open
renovate[bot] wants to merge 1 commit into
Open
fix(deps): update dependency better-auth to v1.6.2 [security]#1046renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
SafeDep Report SummaryPackage Details
This report is generated by SafeDep Github App |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.




This PR contains the following updates:
1.4.18→1.6.2Better Auth: OAuth callback accepts mismatched
statewhen cookie-backed state storage is used without PKCEGHSA-wxw3-q3m9-c3jr
More information
Details
Am I affected?
Users are affected if all of the following are true:
better-authat a version below1.6.2(or@better-auth/ssopaired with such a version).betterAuth({ account: { storeStateStrategy } })is set to"cookie". The default"database"is not affected.genericOAuth({ config })withpkce: false, or it supplies a customgetTokenortokenUrlthat does not require the storedcodeVerifier. Stock social providers with PKCE on are not affected.codevalues to the configured callback URL.If users are on
better-auth@1.6.2or later, they are not affected.Fix:
better-auth@1.6.2or later (current stable is1.6.10).Summary
In
parseGenericState, the cookie branch decrypted theoauth_statecookie and validated expiry, but did not compare the incoming OAuthstatequery parameter to the nonce thatgenerateGenericStateissued at sign-in. Any callback to/api/auth/oauth2/callback/<providerId>that arrived with a forgedstateand anycodewas therefore accepted as long as the browser still held a liveoauth_statecookie. Withpkce: false(or anygetTokenpath that does not enforce a code-verifier round-trip), an attacker who forced the victim to deliver an attacker-controlled authorization code to the callback would mint a session bound to the attacker's external identity in the victim's browser. Account-linking flows behaved the same way, binding the attacker's external account to an authenticated victim row.Details
The cookie branch of
parseGenericStatedid not compare the cookie's stored nonce to the incomingstateparameter. The database branch (the default) was not affected because the verification row is keyed bystateand the lookup itself enforces equality.The fix re-binds the cookie to the nonce:
generateGenericStatewritesoauthState: stateinto the encrypted payload before storage, andparseGenericStaterejects whenparsedData.oauthState !== state. The same primitive covers every caller (generic-oauth, social, account-link, oauth-proxy passthrough, OIDC SSO, SAML relay state).Patches
Fixed in
better-auth@1.6.2via PR #8949 (commit9deb7936a, merged 2026-04-09). The cookie branch ofparseGenericStatenow rejects when the encrypted payload's nonce does not match the incomingstateparameter; the database branch gained a defense-in-depth equality check.Workarounds
If users cannot upgrade immediately:
storeStateStrategyback to"database"(the default). This closes the cookie-only bypass without a code change.pkce: trueon every affectedgenericOAuthprovider. ThecodeVerifieris the missing primitive that the attacker cannot supply.Impact
Credit
Reported by @Jvr2022 via private advisory disclosure, and by @alavesa (PatchPilots audit) via the public duplicate issue #8897.
Resources
Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Release Notes
better-auth/better-auth (better-auth)
v1.6.2Compare Source
Patch Changes
#8949
9deb793Thanks @ping-maxwell! - security: verify OAuth state parameter against cookie-stored nonce to prevent CSRF on cookie-backed flows#8983
2cbcb9bThanks @jaydeep-pipaliya! - fix(oauth2): prevent cross-provider account collision in link-social callbackThe link-social callback used
findAccount(accountId)which matched by account ID across all providers. When two providers return the same numeric ID (e.g. both Google and GitHub assign99999), the lookup could match the wrong provider's account, causing a spuriousaccount_already_linked_to_different_usererror or silently updating the wrong account's tokens.Replaced with
findAccountByProviderId(accountId, providerId)to scope the lookup to the correct provider, matching the pattern already used in the generic OAuth plugin.#9059
b20fa42Thanks @gustavovalverde! - fix(next-js): replace cookie probe with header-based RSC detection innextCookies()to prevent infinite router refresh loops and eliminate leaked__better-auth-cookie-storecookie. Also fix two-factor enrollment flows to set the new session cookie before deleting the old session.#9058
608d8c3Thanks @gustavovalverde! - fix(sso): include RelayState in signed SAML AuthnRequests per SAML 2.0 Bindings §3.4.4.1authnRequestsSigned: truewithout a private key now throws instead of silently sending unsigned requests.#8772
8409843Thanks @aarmful! - feat(two-factor): include enabled 2fa methods in sign-in redirect responseThe 2FA sign-in redirect now returns
twoFactorMethods(e.g.["totp", "otp"]) so frontends can render the correct verification UI without guessing. TheonTwoFactorRedirectclient callback receivestwoFactorMethodsas a context parameter.otpOptions.sendOTPis configured.#8711
e78a7b1Thanks @aarmful! - fix(two-factor): prevent unverified TOTP enrollment from gating sign-inAdds a
verifiedboolean column to thetwoFactortable that tracks whether a TOTP secret has been confirmed by the user.enableTwoFactorcreates the row withverified: false. The row is promoted toverified: trueonly afterverifyTOTPsucceeds with a valid code.enableTwoFactorwhen TOTP is already verified): the new row preservesverified: true, so the user is never locked out of sign-in while rotating their TOTP secret.verifyTOTPrejects rows whereverified === false, preventing abandoned enrollments from blocking authentication. Backup codes and OTP are unaffected and work as fallbacks during unfinished enrollment.Migration: The new column defaults to
true, so existingtwoFactorrows are treated as verified. No data migration is required.skipVerificationOnEnable: trueis also unaffected — the row is created asverified: truein that mode.Updated dependencies []:
v1.6.1Compare Source
Patch Changes
#9023
2e537dfThanks @jonathansamines! - Update endpoint instrumentation to always use endpoint routes#8902
f61ad1cThanks @ping-maxwell! - useINVALID_PASSWORDfor allcheckPasswordfailures#9017
7495830Thanks @bytaesu! - restore getSession accessibility in generic Auth contextUpdated dependencies []:
v1.6.0Compare Source
Minor Changes
#8836
5dd9e44Thanks @gustavovalverde! - Add case-insensitive query support for database adapters#8836
5dd9e44Thanks @gustavovalverde! - Add optional version field to the plugin interface and expose version from all built-in pluginsPatch Changes
#8985
dd537cbThanks @gustavovalverde! - deprecateoidc-providerplugin in favor of@better-auth/oauth-providerThe
oidc-providerplugin now emits a one-time runtime deprecation warning when instantiated and is marked as@deprecatedin TypeScript. It will be removed in the next major version. Migrate to@better-auth/oauth-provider.#8843
bd9bd58Thanks @gustavovalverde! - enforce role-based authorization on SCIM management endpoints and normalize passkey ownership checks via shared authorization middleware#8836
5dd9e44Thanks @gustavovalverde! - Return additional user fields and session data from the magic-link verify endpoint#8836
5dd9e44Thanks @gustavovalverde! - Allow passwordless users to enable, disable, and manage two-factor authentication#8836
5dd9e44Thanks @gustavovalverde! - Prevent updateUser from overwriting unrelated username or displayUsername fields#8836
5dd9e44Thanks @gustavovalverde! - Use non-blocking scrypt for password hashing to avoid blocking the event loop#8836
5dd9e44Thanks @gustavovalverde! - Enforce username uniqueness when updating a user profile#8836
5dd9e44Thanks @gustavovalverde! - Align session fresh age calculation with creation time instead of update time#8836
5dd9e44Thanks @gustavovalverde! - Compare account cookie by provider accountId instead of internal id#8836
5dd9e44Thanks @gustavovalverde! - Trigger session signal after requesting email change in email-otp plugin#8836
5dd9e44Thanks @gustavovalverde! - Rethrow sendOTP failures in phone-number plugin instead of silently swallowing them#8836
5dd9e44Thanks @gustavovalverde! - Read OAuth proxy callback parameters from request body when using form_post response mode#8980
469eee6Thanks @bytaesu! - fix oauth state double-hashing when verification storeIdentifier is set to hashed#8981
560230fThanks @bytaesu! - Preventanyfrom collapsingauth.$Inferandauth.$ERROR_CODES. Preserve client query typing when body isany.Updated dependencies [
5dd9e44,5dd9e44,5dd9e44]:v1.5.6Compare Source
🚀 Features
resendStrategyoption to reuse existing OTP - by @bytaesu in #8560 (98c8e)organizationIdin team endpoints - by @xiaoyu2er and @himself65 in #5062 (8f470)prorationBehaviorper plan - by @bytaesu in #8525 (98cea)@better-auth/test-utils/adapter- by @bytaesu in #8564 (6be0f)twoFactorPagein config - by @wuzgood98 in #5329 (4f41b)🐞 Bug Fixes
skipOriginCheckarray - by @jslno in #8582 (331c4)throw:truein session refresh - by @bytaesu in #8610 (275ca){CHECKOUT_SESSION_ID}placeholder in success callbackURL - by @bytaesu in #8568 (32704)View changes on GitHub
v1.5.5Compare Source
🚀 Features
🐞 Bug Fixes
userfield through idToken sign-in body for Apple name support - by @bytaesu and Copilot in #8417 (d364e)autoSignIn: falsewithoutrequireEmailVerification- by @himself65 in #8521 (e3e66)CREATE INDEXfor postgres migration - by @himself65 in #8538 (b9e54)View changes on GitHub
v1.5.4Compare Source
🐞 Bug Fixes
View changes on GitHub
v1.5.3Compare Source
🐞 Bug Fixes
View changes on GitHub
v1.5.2Compare Source
🐞 Bug Fixes
View changes on GitHub
v1.5.1Compare Source
🐞 Bug Fixes
require- by @himself65 in #8253 (977bf)View changes on GitHub
v1.5.0Compare Source
Better Auth 1.5 Release
We’re excited to announce the release of Better Auth 1.5! 🎉
This is our biggest release yet, with over 600 commits, 70 new features, 200 bug fixes, and 7 entirely new packages. From MCP authentication to Electron desktop support, this release brings Better Auth to new platforms and use cases.
We’re also announcing our new Infrastructure product. It lets you use a full user management and analytics dashboard, security and protection tooling, audit logs, a self-service SSO UI, and more, all with your own Better Auth instance.
Starting with this release, the self-service SSO dashboard — which lets your enterprise customers onboard their own SAML providers without support tickets — is powered by Better Auth Infrastructure. If you’re using the SSO plugin in production, we recommend upgrading to the Pro or Business tier to get access to the dashboard and streamline your enterprise onboarding.
And soon, you’ll be able to host your Better Auth instance on our infrastructure as well, so you can own your auth at scale without worrying about infrastructure needs.
Sign up now: https://better-auth.com/sign-in 🚀
To upgrade, run:
🚀 Highlights
New Better Auth CLI
We’re introducing a new standalone CLI:
npx auth. This replaces the previous@better-auth/clipackage, which will be deprecated in a future release.With a single interactive command,
npx auth initscaffolds a complete Better Auth setup — configuration file, database adapter, and framework integration.All existing commands like
migrateandgenerateare available through the new CLI as well:The
generatecommand now also supports a--adapterflag, letting you generate schema output tailored to your specific database adapter without needing a full Better Auth config file:Remote MCP Auth Client
The MCP plugin now ships a framework-agnostic remote auth client. If your MCP server is separate from your Better Auth instance, you can verify tokens and protect resources without duplicating auth logic.
👉 Read more about MCP authentication
It also comes with built-in framework adapters for Hono and Express-like servers:
OAuth 2.1 Provider
The new
@better-auth/oauth-providerplugin turns your Better Auth instance into a full OAuth 2.1 authorization server with OIDC compatibility. Issue access tokens, manage client registrations, and let third-party apps authenticate against your API — including MCP agents.👉 Read more about the OAuth Provider
Key features:
authorization_code,refresh_token, andclient_credentialsgrants withopenidscope support./jwksendpoint.Note:
The OAuth 2.1 Provider replaces the previous OIDC Provider plugin, which will be deprecated in a future release. The MCP plugin will also transition to use the OAuth 2.1 Provider as its foundation. See the migration guide for upgrading from the OIDC Provider plugin.
Electron Integration
Full desktop authentication support for Electron apps. The plugin handles the complete OAuth flow — opening the system browser, exchanging authorization codes via custom protocol, and managing cookies securely.
👉 Read more about Electron integration
Internationalization (i18n)
The new i18n plugin provides type-safe error message translations with automatic locale detection from headers, cookies, or sessions.
👉 Read more about i18n
Error codes are fully typed — your IDE will autocomplete all available error codes from every registered plugin.
Typed Error Codes
Every error response now includes a machine-readable
codefield. All first-party plugins define their own typed error codes usingdefineErrorCodes, and theAPIErrorclass supports them natively.Error responses now look like:
{ "code": "USER_NOT_FOUND", "message": "User not found" }This is the foundation that the i18n plugin builds on — every error code from every plugin is discoverable at compile time, so translation dictionaries are fully type-checked.
SSO — Production Ready
The SSO plugin has received extensive hardening to be production-ready, with 23+ commits improving security and compliance.
Self-Service SSO Dashboard
As part of our new Infrastructure product, the SSO plugin is now accompanied by a self-service dashboard for onboarding enterprise customers. Organization admins can generate a shareable link that walks enterprise customers through configuring their SAML identity provider — no back-and-forth support tickets required.
The dashboard is available at:
From there, you can generate onboarding links, monitor SSO connection status, and manage provider configurations for each organization.
SAML Single Logout (SLO)
Full support for both SP-initiated and IdP-initiated SAML Single Logout:
Additional SSO Improvements
audclaim validation: Verify audience in OpenID Connect flows.Unified Before & After Hooks
Plugin hooks and global hooks now share the same
AuthMiddlewaretype, making the hooks system consistent and composable across the entire auth pipeline.Plugins use the same middleware type with matchers for targeted interception:
Dynamic Base URL
Better Auth can now resolve the base URL dynamically from incoming requests, making it work seamlessly with Vercel preview deployments, multi-domain setups, and reverse proxies.
👉 Read more about dynamic base URL
Verification on Secondary Storage
Verification tokens can now be stored in secondary storage (e.g., Redis) instead of — or in addition to — the database. Identifiers can be hashed for extra security.
You can also configure per-identifier overrides:
Rate Limiter Improvements
The rate limiter has been improved with separate request/response handling, hardened defaults, and IPv6 support.
Non-Destructive Secret Key Rotation
Better Auth now supports rotating
BETTER_AUTH_SECRETwithout invalidating existing sessions, tokens, or encrypted data. When you need to rotate your secret — whether for scheduled rotation or incident response — you can introduce a new key while keeping old keys available for decryption.Or via environment variable:
BETTER_AUTH_SECRETS="2:new-secret-key,1:old-secret-key"New data is always encrypted with the latest key (first in the array), while decryption automatically tries all configured keys. This lets you roll secrets gradually without downtime or data loss.
Seat-Based Billing (Stripe)
The Stripe plugin now supports per-seat billing for organizations. Member changes automatically sync seat quantity with Stripe.