npm better-auth 1.6.2
v1.6.2

13 hours ago

Blog post: Better Auth 1.6

better-auth

Features

  • feat(two-factor): include enabled 2fa methods in sign-in redirect response

The 2FA sign-in redirect now returns twoFactorMethods (e.g. ["totp", "otp"]) so frontends can render the correct verification UI without guessing. The onTwoFactorRedirect client callback receives twoFactorMethods as a context parameter.

  • TOTP is included only when the user has a verified TOTP secret and TOTP is not disabled in config.
  • OTP is included when otpOptions.sendOTP is configured.
  • Unverified TOTP enrollments are excluded from the methods list. (#8772)

Bug Fixes

  • fix(next-js): replace cookie probe with header-based RSC detection in nextCookies() to prevent infinite router refresh loops and eliminate leaked __better-auth-cookie-store cookie. Also fix two-factor enrollment flows to set the new session cookie before deleting the old session. (#9059)
  • fix(oauth2): prevent cross-provider account collision in link-social callback

The 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 assign 99999), the lookup could match the wrong provider's account, causing a spurious account_already_linked_to_different_user error 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. (#8983)

  • security: verify OAuth state parameter against cookie-stored nonce to prevent CSRF on cookie-backed flows (#8949)

auth

⚠️ Breaking Changes

BREAKING: fix(two-factor): prevent unverified TOTP enrollment from gating sign-in

Adds a verified boolean column to the twoFactor table that tracks whether a TOTP secret has been confirmed by the user.

  • First-time enrollment: enableTwoFactor creates the row with verified: false. The row is promoted to verified: true only after verifyTOTP succeeds with a valid code.
  • Re-enrollment (calling enableTwoFactor when TOTP is already verified): the new row preserves verified: true, so the user is never locked out of sign-in while rotating their TOTP secret.
  • Sign-in: verifyTOTP rejects rows where verified === 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 existing twoFactor rows are treated as verified. No data migration is required. skipVerificationOnEnable: true is also unaffected — the row is created as verified: true in that mode. (#8711)


@better-auth/oauth-provider

Bug Fixes

  • fix(oauth-provider): preserve multi-valued query params through prompt redirects

  • serializeAuthorizationQuery now uses params.append() for array values instead of String(array) which collapsed them into a single comma-joined entry.

  • deleteFromPrompt return type widens from Record<string, string> to Record<string, string | string[]>. The previous type was incorrect — Object.fromEntries() silently dropped duplicate keys, so the narrower type only held because the data was being corrupted. (#9060)

  • Typescript specifies skip_consent type never and errors through zod (#8998)


@better-auth/sso

Bug Fixes

  • fix(sso): include RelayState in signed SAML AuthnRequests per SAML 2.0 Bindings §3.4.4.1

  • RelayState is now passed to samlify's ServiceProvider constructor so it is included in the redirect binding signature. Previously it was appended after the signature, causing spec-compliant IdPs to reject signed AuthnRequests.

  • authnRequestsSigned: true without a private key now throws instead of silently sending unsigned requests. (#9058)

  • fix(sso): strip whitespace from SAMLResponse before base64 decoding

Some SAML IDPs send SAMLResponse with line-wrapped base64 (per RFC 2045), which caused decoding failures. Whitespace is now stripped at the request boundary before any processing. (#8968)


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

Don't miss a new better-auth release

NewReleases is sending notifications on new releases.