Skip to content

fix(deps): update dependency better-auth to v1.6.11#284

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/better-auth
Open

fix(deps): update dependency better-auth to v1.6.11#284
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/better-auth

Conversation

@renovate
Copy link
Copy Markdown
Contributor

@renovate renovate Bot commented Apr 24, 2026

This PR contains the following updates:

Package Change Age Confidence
better-auth (source) 1.6.51.6.11 age confidence

Release Notes

better-auth/better-auth (better-auth)

v1.6.11

Compare Source

Patch Changes
  • #​9568 0cbddb8 Thanks @​gustavovalverde! - Add internalAdapter.consumeVerificationValue(identifier): atomically consume a verification row keyed by identifier. The first concurrent caller receives the row; later racers receive null. Backed by a new DBAdapter.consumeOne primitive implemented natively per adapter (memory, mongo, drizzle, kysely, prisma), with a transaction(findMany + delete) factory fallback. SecondaryStorage.getAndDelete is added as an optional companion; Redis ships it via an atomic Lua get-and-delete operation for compatibility with Redis versions before 6.2.

  • #​9162 a26333b Thanks @​ping-maxwell! - fix: cleanup sessions when admin, anonymous, or SCIM deletes a user

  • #​9573 99a254a Thanks @​gustavovalverde! - fix(device-authorization): require verify-time ownership claim for approve/deny

    Pending device codes were not bound to the user who entered the code on the verification page until approval, leaving a window where any authenticated user could approve or deny another user's pending code by knowing the user_code. GET /device now claims the pending row for the calling session, and POST /device/approve and POST /device/deny require the calling session to match the claimed owner. Custom verification pages must be served to an authenticated session for the flow to succeed.

  • #​8948 ee93485 Thanks @​ping-maxwell! - fix: add error code to change-email-disabled

  • #​9572 5f09d56 Thanks @​gustavovalverde! - Fix race condition in the magic-link plugin's verify handler that allowed two concurrent requests to mint two sessions from the same single-use token. The handler now consumes the verification row atomically via internalAdapter.consumeVerificationValue, so a given magic link mints at most one session regardless of concurrency. The allowedAttempts option is retained for backward compatibility but no longer multiplies successful redemptions; tokens are single-use. The second-redeem error code changes from ATTEMPTS_EXCEEDED to INVALID_TOKEN (the token no longer exists after consumption).

  • b4bc65a Thanks @​gustavovalverde! - Fix race condition in the OAuth authorization-code grant: two concurrent token-exchange requests sharing the same code could both pass the find step before either delete completed and each mint an independent access/refresh/id token set. The authorization_code handler in @better-auth/oauth-provider, plus the legacy oidc-provider and mcp plugins in better-auth, now consume the verification row atomically via internalAdapter.consumeVerificationValue. The first caller mints tokens; concurrent racers receive invalid_grant (RFC 6749 §5.2). Malformed-verification-value branches in @better-auth/oauth-provider previously returned a project-specific invalid_verification code; those are now invalid_grant so spec-compliant clients can branch on the standard code.

  • #​9578 da7e50b Thanks @​gustavovalverde! - handleOAuthUserInfo (used by every social provider, generic-oauth, oauth-proxy, SSO OIDC and SAML, and idToken sign-in) implicitly linked a returning OAuth identity into a local user row whenever the IdP's email_verified claim was true or the provider was trusted. The local row's own emailVerified flag was read only to flip it after linking, never as a precondition. POST /sign-up/email creates rows with emailVerified: false for any caller, so an attacker who pre-registered a victim's email at the application could wait for the legitimate user's first OAuth sign-in: the IdP's verified claim was treated as ownership proof, and the victim's IdP identity was linked into the attacker-owned row.

    The implicit-link gate now requires dbUser.user.emailVerified === true in addition to the provider trust check by default. A new account.accountLinking.requireLocalEmailVerified option (default true) is the public surface for this gate. Apps whose users sign up via OAuth without verifying their email locally can opt back into the legacy behavior with account: { accountLinking: { requireLocalEmailVerified: false } }; understand the takeover risk before doing so. The option is @deprecated; a FIXME at each gate site points at the next-minor follow-up on next that drops the option and makes the gate unconditional.

    The one-tap plugin honored its own copy of the gate and was updated identically: requireLocalEmailVerified and accountLinking.disableImplicitLinking both apply on /one-tap/callback. The email_verified claim from the Google ID token is now normalized via toBoolean so a string "false" is treated as falsy.

    Test fixtures across admin, oidc-provider, mcp, generic-oauth, last-login-method, and oauth-provider suites now mark users emailVerified: true via a databaseHooks.user.create.before hook (or the disableTestUser opt-in on the oauth-provider RP) so the suites continue to exercise their role/flow logic rather than the new gate.

  • #​9507 a1c9f3c Thanks @​GautamBytes! - Preserve exact access-control role statement types so predefined organization roles expose only their configured permissions in TypeScript.

  • #​9577 23094a6 Thanks @​gustavovalverde! - The organization plugin's invitation recipient endpoints (acceptInvitation, rejectInvitation, getInvitation, listUserInvitations) treated invitation.email.toLowerCase() === session.user.email.toLowerCase() as proof that the calling user owned the invited address. A session-authenticated user whose email matched but was never verified passed the gate, so anyone who could pre-register an unverified account at a victim's email could accept invitations addressed to that email. The requireEmailVerificationOnInvitation opt-in option closed the gap only when explicitly enabled and did not protect getInvitation or listUserInvitations at all.

    The gate is now applied on all four recipient endpoints and the requireEmailVerificationOnInvitation option default flips from false to true so existing apps are secure by default. Apps that intentionally accept invitations from unverified accounts can keep the legacy permissive behavior with organization({ requireEmailVerificationOnInvitation: false }), but they should understand the takeover risk before doing so. Server-side calls to listUserInvitations with ctx.query.email and no session continue to bypass the gate (the caller is trusted).

    The option is @deprecated. The next-minor release on next removes it entirely and makes the gate unconditional.

  • #​9548 142b86c Thanks @​dipan-ck! - anonymous plugin now correctly calls onLinkAccount when email verification triggers auto sign-in

  • #​9576 1f2ff42 Thanks @​gustavovalverde! - fix(oidc-provider, mcp): authenticate confidential clients on refresh_token grant and harden secret comparison

    Refresh-token grants on the legacy oidc-provider and mcp plugins now require the registered client_secret from confidential clients, matching the authorization_code path. Public clients (where code_verifier substitutes for the secret on the auth-code grant) continue to skip secret validation. Secret comparisons across both plugins now use constant-time equality. The /mcp/token endpoint no longer emits a wildcard CORS Access-Control-Allow-Origin: * header.

    These plugins are deprecated in favor of @better-auth/oauth-provider, which is unaffected. New deployments should adopt the replacement; this patch keeps existing deployments protected while migrating.

  • #​9575 699b09a Thanks @​gustavovalverde! - fix(oidc-provider, mcp): drop "none" from advertised signing algorithms, default allowPlainCodeChallengeMethod to false, and reject missing PKCE method

    The legacy oidc-provider and mcp plugins now follow OAuth 2.1 (RFC 9700) on three protocol gates:

    • id_token_signing_alg_values_supported (oidc-provider, mcp) and resource_signing_alg_values_supported (mcp) no longer include "none". Relying parties that negotiate from this list will no longer be steered toward unsigned tokens.
    • allowPlainCodeChallengeMethod defaults to false. Callers who need plain PKCE must opt in explicitly.
    • Under the secure default the authorize endpoint no longer silently rewrites a missing code_challenge_method to "plain" before the allowlist check. A request that provides code_challenge without code_challenge_method is now rejected with invalid_request; the inverse case (code_challenge_method without code_challenge) is also rejected so no inconsistent PKCE state is persisted on the authorization code record.

    Non-breaking for callers who never relied on "none" advertisement or the plain default. Callers who explicitly set allowPlainCodeChallengeMethod: true keep plain on the allowlist and retain the legacy "missing method defaults to plain" behavior for backward compatibility, so existing integrations that opted into plain PKCE continue to work. The next-minor on next will drop both the plain allowlist entry and this fallback; until then, the option is the single explicit knob for legacy behavior. Migrate to @better-auth/oauth-provider for the canonical, spec-aligned implementation.

  • Updated dependencies [0cbddb8, c6918ec, da7e50b, b0ef96f, e21d744]:

v1.6.10

Compare Source

Patch Changes

v1.6.9

Compare Source

Patch Changes

v1.6.8

Compare Source

Patch Changes
  • #​9253 856ab24 Thanks @​baptisteArno! - fix(organization): allow passing id through beforeCreateTeam and beforeCreateInvitation

    Mirrors #​4765 for teams and invitations: adapter.createTeam and adapter.createInvitation now pass forceAllowId: true, so ids returned from the respective hooks survive the DB insert.

  • #​9331 9aa8e63 Thanks @​gustavovalverde! - fix(oauth): support mapProfileToUser fallback for providers that may omit email

    Social sign-in with OAuth providers that may return no email address (Discord phone-only accounts, Apple subsequent sign-ins, GitHub private emails, Facebook, LinkedIn, and Microsoft Entra ID managed users) can now be unblocked by synthesizing an email inside mapProfileToUser. Rejection logger messages now point at this workaround and at the new "Handling Providers Without Email" docs section.

    Provider profile types now reflect where email can be null or absent:

    • DiscordProfile.email is string | null and optional (absent when the email scope is not granted)
    • AppleProfile.email is optional
    • GithubProfile.email is string | null
    • FacebookProfile.email is optional
    • FacebookProfile.email_verified is optional (Meta's Graph API does not include this field)
    • LinkedInProfile.email is optional
    • LinkedInProfile.email_verified is optional
    • MicrosoftEntraIDProfile.email is optional

    TypeScript consumers who previously dereferenced profile.email directly inside mapProfileToUser will see a compile error that matches the runtime reality; use a nullish-coalescing fallback (profile.email ?? ...) or null-check the field.

    Sign-in still rejects with error=email_not_found (social callback) or error=email_is_missing (Generic OAuth plugin) when neither the provider nor mapProfileToUser produces an email. First-class support for users without an email, keyed on (providerId, accountId) per OpenID Connect Core §5.7, is tracked in #​9124.

  • Updated dependencies [9aa8e63]:

v1.6.7

Compare Source

Patch Changes
  • #​9211 307196a Thanks @​stewartjarod! - Preserve Set-Cookie headers accumulated on ctx.responseHeaders when an endpoint throws APIError. Cookie side-effects from deleteSessionCookie (and any ctx.setCookie / ctx.setHeader calls before the throw) are no longer silently discarded on the error path.

  • #​9292 4f373ee Thanks @​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's aud claim, so a single backend can serve Web, iOS, and Android clients with their platform-specific Client IDs.

    socialProviders: {
      google: {
        clientId: [
          process.env.GOOGLE_WEB_CLIENT_ID!,
          process.env.GOOGLE_IOS_CLIENT_ID!,
          process.env.GOOGLE_ANDROID_CLIENT_ID!,
        ],
        clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
      },
    }

    Passing a single string keeps working; no migration needed.

    Also exports getPrimaryClientId from @better-auth/core/oauth2 for provider authors: it returns the primary Client ID (the raw string, or the entry at array index 0), paired with clientSecret for 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 both clientId and clientSecret because each of those providers mandates a client secret for their server-side code exchange. Microsoft Entra and Cognito only require clientId, since both support public-client flows with PKCE alone (no secret).

  • #​9293 e1b1cfc Thanks @​gustavovalverde! - Guard against c.body being undefined in parseState. Callback requests that arrive as GET leave c.body unset in some runtimes, which caused c.body.state to throw a TypeError before the existing error redirect could run. The state lookup now short-circuits on the query parameter and falls back to c.body?.state safely, so a callback without a state parameter redirects to the error page instead of crashing.

  • #​4894 d053a45 Thanks @​Kinfe123! - Fire callbackOnVerification when a phone number is verified with updatePhoneNumber: 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.6

Compare Source

Patch Changes
  • #​9214 4debfb6 Thanks @​ping-maxwell! - fix(custom-session): use coerced boolean for disableRefresh query param validation

  • #​9235 9ea7eb1 Thanks @​bytaesu! - Preserve the Partitioned attribute when the customSession plugin and framework integrations forward Set-Cookie headers.

  • #​9266 ab4c10f Thanks @​ping-maxwell! - fix(organization): infer team additional fields correctly

  • #​9219 a61083e Thanks @​bytaesu! - Allow removing a phone number with updateUser({ phoneNumber: null }). The verified flag is reset atomically. Changing to a different number still requires OTP verification through verify({ updatePhoneNumber: true }).

  • #​9226 e64ff72 Thanks @​gustavovalverde! - Consolidate host/IP classification behind @better-auth/core/utils/host and close several loopback/SSRF bypasses that the previous per-package regex checks missed.

    Electron user-image proxy: SSRF bypasses closed (@better-auth/electron). fetchUserImage previously 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 *.localhost names (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 space 100.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-local fe80::/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.0 is no longer treated as loopback. The previous isLoopbackHost implementation in packages/better-auth/src/utils/url.ts classified 0.0.0.0 alongside 127.0.0.1 / ::1 / localhost. 0.0.0.0 is 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 full 127.0.0.0/8 range and any *.localhost name, and rejects 0.0.0.0.

    better-auth: trusted-origin substring hardening. getTrustedOrigins previously used host.includes("localhost") || host.includes("127.0.0.1") when deciding whether to add an http:// variant for a dynamic baseURL.allowedHosts entry. Misconfigurations like evil-localhost.com or 127.0.0.1.nip.io would 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.

    • §7.3 redirect URI matching now accepts the full 127.0.0.0/8 range (not just 127.0.0.1) plus [::1], with port-flexible comparison. Port-flexible matching is limited to IP literals; DNS names such as localhost continue to use exact-string matching per §8.3 ("NOT RECOMMENDED" for loopback).
    • validateIssuerUrl uses the shared loopback check rather than a two-hostname literal comparison.

    New module: @better-auth/core/utils/host. Exposes classifyHost, isLoopbackIP, isLoopbackHost, and isPublicRoutableHost. 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]:


Configuration

📅 Schedule: (in timezone Asia/Makassar)

  • Branch creation
    • "every weekend"
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
domus Ready Ready Preview May 12, 2026 6:06pm

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 24, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 54.06%. Comparing base (b531327) to head (65fd0cc).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #284   +/-   ##
=======================================
  Coverage   54.06%   54.06%           
=======================================
  Files         128      128           
  Lines        4056     4056           
  Branches      828      828           
=======================================
  Hits         2193     2193           
  Misses       1860     1860           
  Partials        3        3           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 24, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
domus-cron 65fd0cc Commit Preview URL

Branch Preview URL
May 12 2026, 06:03 PM

@renovate renovate Bot force-pushed the renovate/better-auth branch from 959e4a4 to 7b221bc Compare April 24, 2026 18:19
@renovate renovate Bot force-pushed the renovate/better-auth branch from 7b221bc to b1b93e2 Compare April 29, 2026 11:10
@renovate renovate Bot force-pushed the renovate/better-auth branch from b1b93e2 to 67742f9 Compare May 9, 2026 16:53
@renovate renovate Bot changed the title fix(deps): update dependency better-auth to v1.6.9 fix(deps): update dependency better-auth to v1.6.10 May 9, 2026
@renovate renovate Bot changed the title fix(deps): update dependency better-auth to v1.6.10 fix(deps): update dependency better-auth to v1.6.11 May 12, 2026
@renovate renovate Bot force-pushed the renovate/better-auth branch from 67742f9 to 65fd0cc Compare May 12, 2026 18:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants