Open
Conversation
8960427 to
ab9cf2b
Compare
ab9cf2b to
fb6a9d7
Compare
fb6a9d7 to
efd42cf
Compare
efd42cf to
03ee1ea
Compare
03ee1ea to
970e0a9
Compare
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.7Release Notes
better-auth/better-auth (better-auth)
v1.6.7Compare Source
Patch Changes
#9211
307196aThanks @stewartjarod! - PreserveSet-Cookieheaders accumulated onctx.responseHeaderswhen an endpoint throwsAPIError. Cookie side-effects fromdeleteSessionCookie(and anyctx.setCookie/ctx.setHeadercalls before the throw) are no longer silently discarded on the error path.#9292
4f373eeThanks @gustavovalverde! - Accept an array of Client IDs on providers that verify ID tokens by audience (Google, Apple, Microsoft Entra, Facebook, Cognito). The first entry is used for the authorization code flow; all entries are accepted when verifying an ID token'saudclaim, so a single backend can serve Web, iOS, and Android clients with their platform-specific Client IDs.Passing a single string keeps working; no migration needed.
Also exports
getPrimaryClientIdfrom@better-auth/core/oauth2for provider authors: it returns the primary Client ID (the raw string, or the entry at array index 0), paired withclientSecretfor the authorization code flow. Providers now reject empty arrays, empty strings, and missing config at sign-in time instead of silently producing a malformed authorization URL. Google, Apple, and Facebook require bothclientIdandclientSecretbecause each of those providers mandates a client secret for their server-side code exchange. Microsoft Entra and Cognito only requireclientId, since both support public-client flows with PKCE alone (no secret).#9293
e1b1cfcThanks @gustavovalverde! - Guard againstc.bodybeing undefined inparseState. Callback requests that arrive as GET leavec.bodyunset in some runtimes, which causedc.body.stateto throw aTypeErrorbefore the existing error redirect could run. The state lookup now short-circuits on the query parameter and falls back toc.body?.statesafely, so a callback without a state parameter redirects to the error page instead of crashing.#4894
d053a45Thanks @Kinfe123! - FirecallbackOnVerificationwhen a phone number is verified withupdatePhoneNumber: true. The callback previously only ran on initial verification, so consumers relying on it (e.g. to sync verified numbers to an external system) would miss the event when an authenticated user changed their number.Updated dependencies [
307196a,4a180f0,4f373ee]:v1.6.6Compare Source
Patch Changes
#9214
4debfb6Thanks @ping-maxwell! - fix(custom-session): use coerced boolean for disableRefresh query param validation#9235
9ea7eb1Thanks @bytaesu! - Preserve thePartitionedattribute when thecustomSessionplugin and framework integrations forwardSet-Cookieheaders.#9266
ab4c10fThanks @ping-maxwell! - fix(organization): infer team additional fields correctly#9219
a61083eThanks @bytaesu! - Allow removing a phone number withupdateUser({ phoneNumber: null }). The verified flag is reset atomically. Changing to a different number still requires OTP verification throughverify({ updatePhoneNumber: true }).#9226
e64ff72Thanks @gustavovalverde! - Consolidate host/IP classification behind@better-auth/core/utils/hostand close several loopback/SSRF bypasses that the previous per-package regex checks missed.Electron user-image proxy: SSRF bypasses closed (
@better-auth/electron).fetchUserImagepreviously gated outbound requests with a bespoke IPv4/IPv6 regex that missed multiple vectors. All of the following were reachable in production and are now blocked:http://tenant.localhost/and other*.localhostnames (RFC 6761 reserves the entire TLD for loopback).http://[::ffff:169.254.169.254]/(IPv4-mapped IPv6 to AWS IMDS, the classic SSRF bypass).http://metadata.google.internal/,http://metadata.goog/(GCP instance metadata).http://instance-data/,http://instance-data.ec2.internal/(AWS IMDS alternate FQDNs).http://100.100.100.200/(Alibaba Cloud IMDS; lives in RFC 6598 shared address space100.64/10, which the old regex did not cover).http://0.0.0.0:PORT/(the Linux/macOS kernel routes the unspecified address to loopback: Oligo's "0.0.0.0 Day").http://[fc00::...]/,http://[fd00::...]/(IPv6 ULA per RFC 4193) and IPv6 link-localfe80::/10, neither of which the regex recognized.Documentation ranges (RFC 5737 / RFC 3849), benchmarking (
198.18/15), multicast, and broadcast are also now rejected.better-auth:0.0.0.0is no longer treated as loopback. The previousisLoopbackHostimplementation inpackages/better-auth/src/utils/url.tsclassified0.0.0.0alongside127.0.0.1/::1/localhost.0.0.0.0is the unspecified address, not loopback; treating it as such lets browser-origin requests reach localhost-bound dev services (Oligo's "0.0.0.0 Day"). The helper now accepts the full127.0.0.0/8range and any*.localhostname, and rejects0.0.0.0.better-auth: trusted-origin substring hardening.getTrustedOriginspreviously usedhost.includes("localhost") || host.includes("127.0.0.1")when deciding whether to add anhttp://variant for a dynamicbaseURL.allowedHostsentry. Misconfigurations likeevil-localhost.comor127.0.0.1.nip.iowould incorrectly gain an HTTP origin in the trust list. The check now uses the shared classifier, so only real loopback hosts get the HTTP variant.@better-auth/oauth-provider: RFC 8252 compliance.127.0.0.0/8range (not just127.0.0.1) plus[::1], with port-flexible comparison. Port-flexible matching is limited to IP literals; DNS names such aslocalhostcontinue to use exact-string matching per §8.3 ("NOT RECOMMENDED" for loopback).validateIssuerUrluses the shared loopback check rather than a two-hostname literal comparison.New module:
@better-auth/core/utils/host. ExposesclassifyHost,isLoopbackIP,isLoopbackHost, andisPublicRoutableHost. One RFC 6890 / RFC 6761 / RFC 8252 implementation that handles IPv4, IPv6 (including bracketed literals, zone IDs, IPv4-mapped addresses, and 6to4 / NAT64 / Teredo tunnel forms with embedded-IPv4 recursion), and FQDNs, with a curated cloud-metadata FQDN set. All bespoke loopback/private/link-local checks across the monorepo now route through it.Updated dependencies [
b5742f9,a844c7d,e64ff72]:v1.6.5Compare Source
Patch Changes
#9119
938dd80Thanks @GautamBytes! - clarify recommended production usage for the test utils plugin#9087
0538627Thanks @ramonclaudio! - fix(client): refetch session after/change-passwordand/revoke-other-sessionsUpdated dependencies []:
v1.6.4Compare Source
Patch Changes
#9205
9aed910Thanks @gustavovalverde! - fix(two-factor): revert enforcement broadening from #9122Restores the pre-#9122 enforcement scope. 2FA is challenged only on
/sign-in/email,/sign-in/username, and/sign-in/phone-number, matching the behavior that shipped through v1.6.2. Non-credential sign-in flows (magic link, email OTP, OAuth, SSO, passkey, SIWE, one-tap, phone-number OTP, device authorization, email-verification auto-sign-in) are no longer gated by a 2FA challenge by default.A broader enforcement scope with per-method opt-outs and alignment to NIST SP 800-63B-4 authenticator assurance levels is planned for a future minor release.
#9068
acbd6efThanks @GautamBytes! - Fix forced UUID user IDs from create hooks being ignored on PostgreSQL adapters whenadvanced.database.generateIdis set to"uuid".#9165
39d6af2Thanks @gustavovalverde! - chore(adapters): require patcheddrizzle-ormandkyselypeer versionsNarrows the
drizzle-ormpeer to^0.45.2and thekyselypeer to^0.28.14. Both new ranges track the minor line that carries the vulnerability fix and nothing newer, so the adapters only advertise support for versions that have actually been tested against. Consumers on older ORM releases see an install-time warning and can upgrade alongside the adapter; the peer is marked optional, so installs do not hard-fail.Updated dependencies [
39d6af2]:v1.6.3Compare Source
Patch Changes
#9131
5142e9cThanks @gustavovalverde! - harden dynamicbaseURLhandling for directauth.api.*calls and plugin metadata helpersDirect
auth.api.*callsAPIErrorwith a clear message when the baseURL can't be resolved (no source and nofallback), instead of leavingctx.context.baseURL = ""for downstream plugins to crash on.allowedHostsmismatches on the direct-API path toAPIError.advanced.trustedProxyHeaderson the dynamic path (defaulttrue, unchanged). Previouslyx-forwarded-host/-protowere unconditionally trusted withallowedHosts; they now go through the same gate as the static path. The default flip tofalseships in a follow-up PR.resolveRequestContextrehydratestrustedProvidersand cookies per call (in addition totrustedOrigins). User-definedtrustedOrigins(req)/trustedProviders(req)callbacks receive aRequestsynthesized from forwarded headers when no fullRequestis available.httpfor loopback hosts (localhost,127.0.0.1,[::1],0.0.0.0) on the headers-only protocol fallback, so local-dev calls don't silently resolve tohttps://localhost:3000.hasRequestusesisRequestLike, which now rejects objects that spoofSymbol.toStringTagwithout a realurl/headers.getshape.Plugin metadata helpers
oauthProviderAuthServerMetadata,oauthProviderOpenIdConfigMetadata,oAuthDiscoveryMetadata, andoAuthProtectedResourceMetadataforward the incoming request to their chainedauth.apicalls, soissuerand discovery URLs reflect the request host on dynamic configs.withMcpAuthforwards the incoming request togetMcpSession, threadstrustedProxyHeaders, and emits a bareBearerchallenge whenbaseURLcan't be resolved (instead ofBearer resource_metadata="undefined/...").metadataResponsein@better-auth/oauth-providernormalizes headers vianew Headers()so callers can passHeaders, tuple arrays, or records without silently dropping entries.#9122
484ce6aThanks @gustavovalverde! - fix(two-factor): enforce 2FA on all sign-in pathsThe 2FA after-hook now triggers on any endpoint that creates a new session, covering magic-link, OAuth, passkey, email-OTP, SIWE, and all future sign-in methods. Authenticated requests (session refreshes, profile updates) are excluded.
#7231
f875897Thanks @Byte-Biscuit! - fix(two-factor): preserve backup codes storage format after verificationAfter using a backup code, remaining codes are now re-saved using the same
storeBackupCodesstrategy (plain, encrypted, or custom) configured by the user. Previously, codes were always re-encrypted with the built-in symmetric encryption, breaking subsequent verifications for plain or custom storage modes.#9072
6ce30cfThanks @ramonclaudio! - fix(api): align top-leveloperationIdonrequestPasswordResetCallbackwith the OpenAPIresetPasswordCallback#8389
f6428d0Thanks @Oluwatobi-Mustapha! - fix(open-api): correct get-session nullable schema for OAS 3.1#9078
9a6d475Thanks @ping-maxwell! - fix(client): prevent isMounted race condition causing many rps#9113
513dabbThanks @bytaesu! - resolve dynamicbaseURLfrom request headers on directauth.apicalls#8926
c5066feThanks @bytaesu! - omit quantity for metered prices in checkout and upgrades#9084
5f84335Thanks @bytaesu! - support Stripe SDK v21 and v22Updated dependencies []:
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
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.