Update dependency better-auth to v1.7.25 #126

Open
renovate-bot wants to merge 1 commit from renovate/better-auth-monorepo into main
Collaborator

This PR contains the following updates:

Package Change Age Adoption Passing Confidence
better-auth (source) 1.6.23 → 1.7.25 age adoption passing confidence

Release Notes

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

v1.7.25

Compare Source

Patch Changes

v1.7.24

Compare Source

Patch Changes

v1.7.23

Compare Source

Patch Changes

v1.7.22

Compare Source

Patch Changes

v1.7.21

Compare Source

Patch Changes
  • #​12126 4739122 Thanks @​hardikguptaofficialgit! - On Prisma with MongoDB, when your id columns use @db.ObjectId, magic link and email OTP sign-in for users with an unverified email no longer fail with a 500 (Malformed ObjectID). SAML sign-in, DPoP-bound OAuth requests, and private_key_jwt client authentication are fixed the same way, and Sign-In with Ethereum now keeps the email the user supplied instead of a placeholder. Setups with plain String id columns and other databases are unaffected.

  • #​12392 a277020 Thanks @​gustavovalverde! - Fixed the device approval page showing a valid code as invalid when the same signed-in user looked it up twice at once, such as under React Strict Mode in development.

  • #​12052 1c3410a Thanks @​lakshya-dhariwal! - If you set advanced.backgroundTasks.handler and your database adapter runs transactions (the built-in Kysely connection, MongoDB with client, or Prisma or Drizzle with transaction: true), email sign-up now keeps the database writes made while sending the verification email or running onExistingUserSignUp. Before, those writes failed because the sign-up had already been saved. With emailOTP({ overrideDefaultEmailVerification: true }), the code was never stored, so new users could not verify their email.

    Whatever adapter or background handler you use, a sign-up verification email now goes out after the new user is saved, and onExistingUserSignUp, when a duplicate sign-up triggers it, runs after the request's database work completes. If sendVerificationEmail throws, the sign-up succeeds and the error is logged; before, the sign-up failed. To reject a sign-up, use a databaseHooks.user.create.before hook. If onExistingUserSignUp throws, the error is logged and the request still returns the generic response.

  • #​12179 9105261 Thanks @​ghazy2001! - Generic OAuth providers configured with discoveryHeaders now send those headers when fetching the provider's signing keys from a jwks_uri on the same origin as discoveryUrl. Before, the keys request went out without them. Providers whose key endpoint requires the headers rejected it, so every ID token failed verification and sign-in failed. A jwks_uri on another origin still gets no headers.

    With discoveryHeaders set, the discovery request now follows redirects only within the origin of discoveryUrl, so the headers no longer reach another host. If your discoveryUrl redirects to another origin, discovery now fails and Better Auth logs an error. Set discoveryUrl to the final URL. Providers without discoveryHeaders are unaffected.

  • #​12401 c3a94c5 Thanks @​gustavovalverde! - Generic OAuth providers configured with authorizationHeaders now send those headers when refreshing an access token, not only when exchanging the authorization code. Before, a provider whose token endpoint requires one of those headers accepted the sign-in but rejected every refresh. Providers without authorizationHeaders are unaffected.

    refreshAccessToken and refreshAccessTokenRequest from @better-auth/core/oauth2 accept an optional headers input for providers that need extra headers on refresh. Client authentication headers still take precedence over it.

    Extra headers whose names differ only in case from a client authentication header, such as Authorization with authentication: "basic", no longer produce a combined, invalid header on the code exchange or refresh. Client authentication replaces them, including headers a custom tokenEndpointAuth hook sets in any casing.

  • #​12404 9b40ac6 Thanks @​bytaesu! - Plugin packages no longer import from the better-auth root, so bundlers that split code stop emitting unused Kysely chunks when used with better-auth/minimal.

  • #​12395 b0ced9d Thanks @​gustavovalverde! - On Cloudflare Workers, sign-ins with a genericOAuth provider that uses discoveryUrl, and OAuth provider requests that use a configured resource (resources), no longer hang. An auth instance created at module scope retries discovery and resource seeding on later requests. Previously, if the request running that retry ended first, every later request that needed it waited forever. Each request now retries on its own until one succeeds.

    On other runtimes, concurrent requests may send a few more discovery requests or resource lookups while the identity provider or database is unreachable.

  • #​11912 5c12074 Thanks @​gustavovalverde! - If you use PostgreSQL and other schemas in the same database are dropped while Better Auth reads the database, for example one schema per tenant or parallel test runs, the startup schema check and npx auth@latest migrate no longer fail intermittently with schema "…" does not exist or relation "…" does not exist. The startup schema check now reads only the schemas on the connection's search_path, plus any schema set with schemaName or a schema plugin, and migrate reads only the schema it migrates. Before, both read every schema in the database. If no other schemas change while Better Auth reads the database, nothing changes for you.

  • #​12403 e64f624 Thanks @​bytaesu! - SIWE verification no longer includes the internal error message (for example a database error) in its 401 response. The original error is now logged on the server instead.

  • Updated dependencies [4739122, c3a94c5, 4080630, 5c12074, b241ffc, 57fa843, 1a79da5, 2d10ae7, ef4d4f7]:

v1.7.20

Compare Source

Patch Changes

v1.7.19

Compare Source

Patch Changes
  • #​12323 a10121d Thanks @​bytaesu! - Fixed the active-sessions list in secondary storage being stored without an expiry when the last remaining session had less than a second left.

  • #​12335 c41f8ff Thanks @​bytaesu! - Fixed /admin/create-user leaving a user without a credential account when the password was rejected (for example by the Have I Been Pwned plugin), which made retrying with the same email fail with USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL.

  • #​12338 e786566 Thanks @​bytaesu! - Fixed /admin/create-user leaving a user without a credential account when creating that account failed, which made retrying with the same email fail with USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL.

  • #​12336 ac778b7 Thanks @​bytaesu! - Client actions now send the HTTP method passed in the second-argument fetch options, the same as when it is nested under fetchOptions. Passing a lowercase get or HEAD method no longer fails because a request body was attached.

  • #​12246 5eb69d8 Thanks @​AryaKesharwani! - /device/token now returns invalid_client instead of invalid_grant when validateClient rejects the client_id, matching /device/code and RFC 6749 section 5.2.

  • #​12319 4b0327b Thanks @​bytaesu! - Plugin schema overrides (modelName and field names) no longer leak into other instances of the same plugin in one process.

  • #​12320 2d5ef64 Thanks @​bytaesu! - Fix OAuth sign-in failing with state_mismatch during the daylight saving time fall-back hour on servers that do not run in UTC. The stored OAuth state is no longer created already expired.

  • #​12337 5f0a0fc Thanks @​bytaesu! - The OpenAPI schema now documents query parameters for endpoints whose query schema is wrapped in z.optional(), such as /get-session and Stripe's /subscription/list.

  • #​12332 89e74b8 Thanks @​bytaesu! - Fixed organization.getInvitation returning a missing, expired, or non-pending invitation error without a code. It now returns INVITATION_NOT_FOUND like the other invitation endpoints.

  • #​12318 525bd7a Thanks @​bytaesu! - Fix listMembers failing with a syntax error on PostgreSQL and MSSQL when the filter or offset matches no members.

  • #​12366 23a60a3 Thanks @​bytaesu! - Fixed SIWE sign-in leaving a user behind when creating its wallet address or account failed, which blocked that wallet from signing in again.

  • #​12340 29e45f8 Thanks @​bytaesu! - Fixed social sign-up hanging on single-connection databases when the username plugin is enabled and the provider profile maps a username.

  • Updated dependencies [8412a5f, 34ee100]:

v1.7.18

Compare Source

Patch Changes

v1.7.17

Compare Source

Patch Changes

v1.7.16

Compare Source

Patch Changes
  • #​12198 f25081a Thanks @​bytaesu! - /admin/update-user now requires the new user:set-phone permission to change phoneNumber or phoneNumberVerified. If you use a custom access control, add set-phone to your statements and grant it to roles that should change phone numbers.

  • #​12198 f25081a Thanks @​bytaesu! - Anonymous users are no longer linked or deleted on a sign-in that is still waiting for a second factor.

  • #​12198 f25081a Thanks @​bytaesu! - Email verification and change email links now only work for the account they were issued to. Opening a change email link no longer signs the user in, and it verifies the new email only if the current one was verified.

  • #​12198 f25081a Thanks @​bytaesu! - Wildcard trusted origins now match custom-scheme URLs by their parsed scheme, host, and path, and no longer match a URL with userinfo or one that cannot be parsed as an origin. A wildcard host made only of digits, dots, and wildcards, such as 192.168.*.*, now matches only IPv4 addresses in that range.

  • #​12198 f25081a Thanks @​bytaesu! - Looking up a user by email now returns only an account whose stored address matches the requested one, ignoring case, even on databases with accent-insensitive collations. The email OTP password reset routes now validate the address and send the code to the address on the account.

  • #​12198 f25081a Thanks @​bytaesu! - Email change codes from the email OTP plugin are now bound to the requesting account and the new address. Codes sent before the upgrade must be requested again.

  • #​12198 f25081a Thanks @​bytaesu! - Fixed the email OTP checkVerificationOtp and resetPassword endpoints and the phone number resetPassword endpoint allowing more OTP guesses than allowedAttempts when requests arrive concurrently. A correct check still leaves the OTP usable for the follow-up call.

  • #​12198 f25081a Thanks @​bytaesu! - The Expo authorization proxy now sets the signed state cookie only for an unexpired state issued by the server. getAuthStateVerificationIdentifier is now exported for plugins that look up OAuth state records.

  • #​12198 f25081a Thanks @​bytaesu! - Passkey registration, device authorization, two-factor enrollment and verification, social account linking, and phone number changes now reject a session revoked in the database or secondary storage, even when cookie caching is enabled. Sessions created from API keys are refused on these routes, except two-factor enrollment and phone number changes.

  • #​12198 f25081a Thanks @​bytaesu! - Generic OAuth providers whose discovery document has a jwks_uri now reject a user profile whose sub differs from the ID token's sub. Direct ID-token sign-in and linking with a generic OAuth provider now resolve the profile from the verified ID token only and ignore a submitted access or refresh token.

  • #​12198 f25081a Thanks @​bytaesu! - Pending organization invitations are now only returned to members whose role can create invitations, and creator-role invitations only to members who hold that role.

  • #​12198 f25081a Thanks @​bytaesu! - Organization invitations use random IDs when advanced.database.generateId is not set, including on adapters with sequential default IDs such as MongoDB. On MongoDB, new invitation IDs are strings instead of ObjectIds.

  • #​12198 f25081a Thanks @​bytaesu! - Dynamic access control roles reused through hasPermission's useMemoryCache option are now cached per request and per auth instance instead of in a process-global cache, so role changes apply on the next request.

  • #​12198 f25081a Thanks @​bytaesu! - Organization role lists are now trimmed and split on commas wherever they are stored or compared, and invitations with an empty role are rejected.

  • #​12198 f25081a Thanks @​bytaesu! - With the phone number plugin, updateUserInfoOnLink no longer copies an unverified phoneNumber from the linked provider's profile. An existing verified number is kept.

  • #​12198 f25081a Thanks @​bytaesu! - With requireVerification, phone number password reset now works only for a verified number. No reset code is sent to an unverified number, and resetPassword returns PHONE_NUMBER_PENDING_VERIFICATION without adding or changing a password.

  • #​12198 f25081a Thanks @​bytaesu! - With the phone number plugin, a new user no longer stores a phoneNumber that was not verified. Sign-up and sign-in routes reject one sent in the request, and a number from a social profile or in data passed to createUser is dropped unless it is marked verified.

  • #​12198 f25081a Thanks @​bytaesu! - Phone number verification and password reset codes are now stored under their own namespaces, so a phone number request can no longer create, consume or delete verification codes that belong to other flows. Codes sent before the upgrade must be requested again.

  • #​12198 f25081a Thanks @​bytaesu! - Account linking and new OAuth users now count an email as verified only when the provider claim is true or the string "true", which covers generic OAuth, Cognito, and Microsoft Entra ID claims sent as strings. Any other emailVerified value, including a non-boolean value returned from mapProfileToUser such as 1, now reads as unverified.

  • #​12198 f25081a Thanks @​bytaesu! - With secondary storage, revoking all of a user's sessions or deleting the user now also ends sessions missing from the cached session list.

  • Updated dependencies [f25081a, f25081a, f25081a, f25081a, f25081a]:

v1.7.15

Compare Source

Patch Changes

v1.7.14

Compare Source

Patch Changes

v1.7.13

Compare Source

Patch Changes

v1.7.12

Compare Source

Patch Changes

v1.7.11

Compare Source

Patch Changes

v1.7.10

Compare Source

Patch Changes
  • #​11393 eebd47a Thanks @​breken-ai! - If you use the lastLoginMethod plugin with oneTap, signing in through Google One Tap now records "google", the same value as signing in with the Google button. Before, a One Tap sign-in recorded nothing: the better-auth.last_used_login_method cookie kept its previous value or stayed unset, and so did user.lastLoginMethod with storeInDatabase.

    If your customResolveMethod includes a /one-tap/callback → "google" workaround, remove that mapping; keep the resolver if it handles other routes.

  • #​11821 09d07e6 Thanks @​gustavovalverde! - The Google One Tap prompt (authClient.oneTap() without button) no longer calls getSkippedReason(), which Google's FedCM migration guide says to remove. Before, it called it under any promptOptions.fedCM setting.

    A skipped prompt now ends the flow and calls onPromptNotification right away instead of retrying. Google doesn't report why a prompt was skipped, and a prompt the user closed stays hidden for Google's cooldown period, so the retries could not bring it back. Dismissed prompts still retry as before.

    Google Identity Services still logs its FedCM migration warning, because it does so whenever prompt() receives a listener. The plugin needs the listener to track dismissals and skips, so the warning is expected and safe to ignore.

  • #​11819 eaecb72 Thanks @​gustavovalverde! - Google One Tap now applies your socialProviders.google.mapProfileToUser and custom getUserInfo, the same as the standard Google sign-in. Before, One Tap ignored both and always stored the token's name, email, email_verified, and picture claims.

    If you configured either one, users created through One Tap now get the fields it returns, including any extra fields such as a first name from given_name. Returning users are updated from it on each One Tap sign-in when socialProviders.google.overrideUserInfoOnSignIn is on, and so are existing users that One Tap links when account.accountLinking.updateUserInfoOnLink is on. Check that your mapper suits One Tap users too.

    If you set socialProviders.google.overrideUserInfoOnSignIn, One Tap sign-ins now update returning users' name, image, and email from Google, as the redirect sign-in already does.

    Setups that configure none of these, including oneTap({ clientId }) with no Google provider, are unaffected.

  • #​11820 88c9a37 Thanks @​gustavovalverde! - If you set autoSelect or cancelOnTapOutside on oneTapClient(), the One Tap prompt and the Sign in with Google button now use them. Before, only values passed to authClient.oneTap() took effect, so with autoSelect: true on the plugin, returning users still had to pick an account. Values passed to oneTap() still override the plugin's. Apps that set these options only on oneTap(), or not at all, are unaffected.

  • #​11696 0cf1f7c Thanks @​drewsephski! - You can now import the One Tap plugin from better-auth/plugins/one-tap, as you can for other plugins. Previously this import failed with a "not exported" error. Imports from better-auth/plugins and better-auth/client/plugins are unchanged.

  • #​11822 414e845 Thanks @​gustavovalverde! - You can now import the Captcha and OpenAPI plugins from better-auth/plugins/captcha and better-auth/plugins/open-api, as you can for other plugins. Previously these imports failed with a "not exported" error. Imports from better-auth/plugins are unchanged.

    If your TypeScript config uses "moduleResolution": "node" (or node10), better-auth/plugins/multi-session and better-auth/test now resolve their types. Previously TypeScript reported that it could not find their declarations. Projects using bundler, node16, or nodenext resolution are unaffected.

  • Updated dependencies [fedd055, bbd0a19, 99cdfe7]:

v1.7.9

Compare Source

Patch Changes

v1.7.8

Compare Source

Patch Changes
  • #​11512 4175b34 Thanks @​bytaesu! - Align the installed @better-auth/utils version with better-call while accepting ^0.4.2 || ^0.5.0 across official package peers.

  • #​11565 b1cfc1e Thanks @​bytaesu! - Omit non-returned custom fields from inferred output schema types.

  • #​11540 678c555 Thanks @​bytaesu! - Align cookie security attributes with RFC 6265bis.

  • #​10591 193efff Thanks @​kdelay! - Expired or revoked sessions now clear stale cookies when using customSession.

  • #​11625 369176e Thanks @​gustavovalverde! - Fixed single-use checks for apps that set advanced.database.generateId: "uuid". Default id generation is unaffected, and no migration is needed.

    • On SQLite and MySQL, SAML sign-in, DPoP-bound requests checked against the database replay store, private_key_jwt client authentication, and magic link or email OTP sign-ins that adopt an unverified account failed with a NOT NULL error on the id column. They now work. SIWE sign-in ignored the email field and used the wallet-derived address; it now uses the supplied email when no other account has it.
    • On Postgres and MongoDB, a reused SAML assertion, DPoP proof, or client assertion was accepted. Each one is now accepted only once.

    With generateId: "serial", behavior is unchanged: these checks need string ids, and Better Auth now logs a warning the first time one of these flows runs.

  • #​11391 352d04c Thanks @​breken-ai! - Fix customSession treating session lookup failures as signed-out users. Server-side getSession now throws an API error, and HTTP clients receive an error response.

  • #​11750 e9d9921 Thanks @​gustavovalverde! - Generic OAuth providers configured with discoveryUrl now recover without a restart when discovery fails at startup. Since 1.7.3, a provider whose discovery request failed at boot (for example, because the identity provider was still starting) was skipped, and every sign-in returned PROVIDER_NOT_FOUND until the app restarted. Better Auth now retries discovery on the next sign-in, callback, or account-linking request for that provider, and the provider works as soon as discovery succeeds. While discovery keeps failing, sign-in and account linking still return PROVIDER_NOT_FOUND and callbacks redirect with error=oauth_provider_not_found, and each request may wait up to 10 seconds for the retry.

  • #​11549 1bd5c37 Thanks @​bytaesu! - Fix lastLoginMethod failing sign-in when cookieName or maxAge is explicitly undefined.

  • #​11174 766cfb3 Thanks @​Bekacru! - Stop double-decoding callbackURL, newUserCallbackURL and errorCallbackURL on magic link verify so percent-encoded characters in callback query params are preserved.

  • #​11505 c46992f Thanks @​bytaesu! - Preserve the referenced model and field keys when generating Kysely migrations, so custom table names that match another model key produce the intended foreign keys.

  • #​11718 1d95ebf Thanks @​bytaesu! - Fix missing device sessions and failed automatic session switching after cache eviction when storeSessionInDatabase is enabled and preserveSessionInDatabase is disabled. Automatic switching now follows the order of held sessions, and large lists or sessions whose users no longer exist no longer cause valid sessions to disappear.

  • #​10266 c68b2cd Thanks @​shiminshen! - Projects that enable exactOptionalPropertyTypes can use the oauthProvider() and mcp() plugins again. Since 1.7.0, adding either plugin to betterAuth() failed to type-check with a TS2322 error.

  • #​11637 2627237 Thanks @​gustavovalverde! - Projects using the organization plugin with skipLibCheck: false no longer get TS2536: Type '"role"' cannot be used to index type ... from crud-members.d.mts. The role returned by getActiveMemberRole is now typed as the organization's role names directly. Projects with skipLibCheck: true (the default in most templates) were unaffected.

  • #​10866 3d1f008 Thanks @​heliohm! - The organization plugin no longer pulls every zod locale into server bundles.

  • #​11626 67743ed Thanks @​gustavovalverde! - Password resets and password changes no longer keep the new password when revoking the user's sessions fails. With revokeSessionsOnPasswordReset or revokeOtherSessions, database sessions are revoked and the password is saved in one transaction when transactions are enabled for the adapter, so a failure leaves the old password and sessions in place. Sessions in secondary storage are still removed after commit, on a best-effort basis. This covers /reset-password, the email OTP and phone number reset endpoints, and /change-password. onPasswordReset now runs after sessions are revoked, so a throwing callback no longer leaves them active, and a callback that lists the user's sessions no longer sees the revoked ones.

  • #​10698 f0ce580 Thanks @​jashkarangiya! - If you set the phone number plugin's verifyOTP option, /phone-number/reset-password now checks the code with it, as /phone-number/verify already does. Before, password reset always compared the code with the one Better Auth generated, so apps that verify codes through a provider such as Twilio Verify could not reset passwords. If your sendPasswordResetOTP sends the code Better Auth generates, make sure verifyOTP accepts that code. Apps that don't set verifyOTP are unaffected.

  • #​11646 07a0938 Thanks @​gustavovalverde! - A failed password reset no longer spends the reset link or OTP. /reset-password, /email-otp/reset-password and /phone-number/reset-password now check the token or code without consuming it, validate and hash the new password, then consume it in the same transaction as the session revocation and password write. A rejected password, such as one refused by haveIBeenPwned, always leaves the link or OTP usable. A later failure, such as a failed session revocation or password write, also leaves it usable when the adapter's transactions are enabled and verification values are stored in the database (the default without secondary storage, or verification.storeInDatabase: true). Wrong codes still count against the OTP attempt limit.

  • #​11643 92dfcea Thanks @​gustavovalverde! - A sign-up that fails after its session is created no longer leaves that session in secondary storage. With storeSessionInDatabase enabled and preserveSessionInDatabase off, the secondary-storage copy is written only after the sign-up commits, and listing a user's sessions now reads the database, so a session whose cached copy failed to write is still listed and revoked. With other secondary-storage settings, sign-up, passkey registration and SSO sign-in with resolveUser keep the session write inside their transaction, so a storage failure rolls back the new user, passkey or account instead of keeping them without a session. A session revoked by a session.create.after database hook also stays revoked.

  • #​11488 e15625f Thanks @​Bekacru! - SvelteKit 3 projects can now install Better Auth without --legacy-peer-deps. The @sveltejs/kit peer range now includes ^3.0.0; SvelteKit 2 projects are unaffected.

  • Updated dependencies [4175b34, 435645e, 369176e, 0cfe07d, 9904b29, e78f15a, 60525e0, 8133265, ea85d13, 916ef5c, 1d95ebf, 6214610, 67743ed, c46d2a6, 92dfcea, a9f455d, 9725f1b]:

v1.7.7

Compare Source

Patch Changes
  • #​11476 4186e36 Thanks @​bytaesu! - Return CAPTCHA errors with the correct JSON Content-Type header.

  • #​11469 8620aa9 Thanks @​aryan1306! - Return rate limit errors with a JSON Content-Type header.

  • #​11491 55cb92e Thanks @​bytaesu! - Respect social provider disableSignUp when signing in with an ID token.

  • #​11375 69defbc Thanks @​bytaesu! - Refresh the active organization after sign-in when a session hook selects the initial organization.

  • #​11494 ac54bfd Thanks @​gustavovalverde! - Isolate OAuth state cookies and each OAuth Proxy payload with purpose-specific encryption keys. The oAuthProxy options and supported configuration remain unchanged.

    Upgrade all Better Auth nodes that handle the same cookie-backed OAuth or SAML relay-state flow together. Upgrade every OAuth Proxy participant, including production and preview or development deployments, in the same cutover. OAuth sign-in, account-linking, and cookie-backed SAML sign-in flows started before the upgrade must be restarted. Mixed old and new participants cannot exchange existing state or proxy payloads, and there is no fallback to the previous shared key.

  • #​11494 ac54bfd Thanks @​gustavovalverde! - Magic Link verification now accepts only records issued for Magic Link. Magic
    Link records and database-backed OAuth or SAML state use separate verification
    identifier prefixes. Links and database-backed sign-ins started before the
    upgrade cannot complete; request new Magic Links and restart those sign-ins.
    Upgrade servers sharing verification storage together, and update
    verification.storeIdentifier.overrides rules for these flows to match the new
    magic-link: and auth-state: prefixes. The link token, callback state,
    endpoints, and public option types are unchanged.

    Upgrade installed Better Auth adapters, plugins, and integrations released
    with better-auth alongside it so participating packages use the same release
    version.

  • Updated dependencies [35d7cd3, 07bdf7e]:

v1.7.6

Compare Source

Patch Changes

v1.7.5

Compare Source

Patch Changes

v1.7.4

Compare Source

Patch Changes

v1.7.3

Compare Source

Patch Changes
  • #​11060 3660f06 Thanks @​bytaesu! - Handle malformed custom-scheme callback URLs without excessive processing.

  • #​11037 5bd7096 Thanks @​bytaesu! - Prevent repeated TOTP enrollment from replacing an active authenticator and its backup codes.

  • #​11120 7ec7146 Thanks @​onmax! - Prevent getSession from failing when cookie caching is disabled while clients still have cached session cookies.

  • #​9908 76d311f Thanks @​harshil1712! - Add Cloudflare as a built-in social provider, with support for client-secret authentication and PKCE clients without a secret.

  • #​11188 c47b765 Thanks @​bytaesu! - Normalize Auth0 domains without a potentially slow trailing-slash regular expression.

  • #​11084 2d5c63d Thanks @​bytaesu! - Prevent duplicate session requests and hydration mismatches when using the Vue client with Nuxt useFetch.

  • #​11147 a9d8c12 Thanks @​bytaesu! - Add isPasswordCompromised for checking passwords against Have I Been Pwned in custom server-side flows, while ignoring padded response entries with zero occurrences.

  • #​10988 9fc7498 Thanks @​bytaesu! - Run callback hooks after proxied OAuth sign-ins and preserve server state when callback cookies are unavailable. The legacy /oauth-proxy-callback endpoint is deprecated and will be removed in the next minor release.

  • #​11178 be0e007 Thanks @​bytaesu! - Report missing tables, missing columns, and required columns Better Auth never writes during initialization, with guidance for fixing them. Kysely checks the live database schema. Authentication requests await the same check and are rejected if the schema does not match.

    Validation is enabled by default, including in production. Set advanced.database.validateSchema: false to disable runtime validation. auth migrate refuses to apply changes when required unwritten columns need manual repair.

  • #​11069 0bb0dbf Thanks @​bytaesu! - Improve dynamic organization role permission check performance.

  • #​11153 2220ee7 Thanks @​bytaesu! - Restore sign-in compatibility with 1.6 databases by identifying accounts with (providerId, accountId) and removing the issuer requirement introduced in 1.7.0. Upgrading from 1.6 no longer requires an account schema migration. Ambiguous account keys are rejected instead of selecting an arbitrary account.

    If you applied the 1.7.0 through 1.7.2 account schema, remove its issuer unique index before upgrading. For SQL databases, also make issuer nullable or remove the column so sign-ups and account linking can succeed. auth migrate does not perform this cleanup. Follow the upgrade guide for database-specific steps.

  • #​10978 5fe5bc2 Thanks @​BetterAndBetterII! - Skip a generic OAuth provider when discovery fails instead of taking down the rest of the auth API.

  • #​10963 74a7369 Thanks @​thisismert! - Track email OTP sign-ins in the last login method plugin.

  • #​11085 e16b40a Thanks @​bytaesu! - Provide type-safe Nuxt useFetch integration for the Vue client's useSession hook.

  • #​11066 c0444dc Thanks @​bytaesu! - Upgrade the packaged Zod dependency to 4.5. Generated OpenAPI schemas now mark required request fields consistently with runtime validation, including passkey registration responses.

  • Updated dependencies [352d012, 76d311f, 3e9e197, 157ec8d, baa08f4, 9e36635, be0e007, a2bae0c, 1a1b7d5, 2220ee7]:

v1.7.2

Compare Source

Patch Changes

v1.7.1

Patch Changes
  • #​12323 a10121d Thanks @​bytaesu! - Fixed the active-sessions list in secondary storage being stored without an expiry when the last remaining session had less than a second left.

  • #​12335 c41f8ff Thanks @​bytaesu! - Fixed /admin/create-user leaving a user without a credential account when the password was rejected (for example by the Have I Been Pwned plugin), which made retrying with the same email fail with USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL.

  • #​12338 e786566 Thanks @​bytaesu! - Fixed /admin/create-user leaving a user without a credential account when creating that account failed, which made retrying with the same email fail with USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL.

  • #​12336 ac778b7 Thanks @​bytaesu! - Client actions now send the HTTP method passed in the second-argument fetch options, the same as when it is nested under fetchOptions. Passing a lowercase get or HEAD method no longer fails because a request body was attached.

  • #​12246 5eb69d8 Thanks @​AryaKesharwani! - /device/token now returns invalid_client instead of invalid_grant when validateClient rejects the client_id, matching /device/code and RFC 6749 section 5.2.

  • #​12319 4b0327b Thanks @​bytaesu! - Plugin schema overrides (modelName and field names) no longer leak into other instances of the same plugin in one process.

  • #​12320 2d5ef64 Thanks @​bytaesu! - Fix OAuth sign-in failing with state_mismatch during the daylight saving time fall-back hour on servers that do not run in UTC. The stored OAuth state is no longer created already expired.

  • #​12337 5f0a0fc Thanks @​bytaesu! - The OpenAPI schema now documents query parameters for endpoints whose query schema is wrapped in z.optional(), such as /get-session and Stripe's /subscription/list.

  • #​12332 89e74b8 Thanks @​bytaesu! - Fixed organization.getInvitation returning a missing, expired, or non-pending invitation error without a code. It now returns INVITATION_NOT_FOUND like the other invitation endpoints.

  • #​12318 525bd7a Thanks @​bytaesu! - Fix listMembers failing with a syntax error on PostgreSQL and MSSQL when the filter or offset matches no members.

  • #​12366 23a60a3 Thanks @​bytaesu! - Fixed SIWE sign-in leaving a user behind when creating its wallet address or account failed, which blocked that wallet from signing in again.

  • #​12340 29e45f8 Thanks @​bytaesu! - Fixed social sign-up hanging on single-connection databases when the username plugin is enabled and the provider profile maps a username.

  • Updated dependencies [8412a5f, 34ee100]:

v1.7.0

Minor Changes
  • #​8733 4e8e4c7 Thanks @​bytaesu! - Add hydrateSession to seed the client with a server-fetched session so useSession returns data on the first render.

  • #​9930 0cbaf81 Thanks @​gustavovalverde! - Anonymous account linking now works after social and generic OAuth sign-in in Expo and other in-app browsers, where the OAuth callback returns without the session cookie. onLinkAccount fires and the anonymous user is migrated; before, it was silently skipped.

    Plugins can now carry server-trusted data across an OAuth redirect with the new addOAuthServerContext API, read back on the callback via getOAuthState().serverContext. Unlike additionalData, it cannot be set from the request body, so it is the right place for values the server must trust.

    For @better-auth/oauth-provider, the post-login authorization query now travels through that server-only channel, so it can no longer be injected through additionalData.

  • #​10004 b36c38f Thanks @​bytaesu! - The captcha plugin now requires endpoint entries to match full auth paths unless they use wildcard patterns. This prevents requests like /sign-in//email from bypassing captcha while preserving trailing-slash matches like /sign-in/email/. To protect multiple routes, replace partial paths like /sign-in with explicit wildcards such as /sign-in/* or /sign-in/**.

  • #​10746 6782647 Thanks @​gustavovalverde! - OAuth device grants now use oauthDeviceAuthorization() alongside oauthProvider() or mcp(). This single integration replaces both the standalone deviceCodeGrant() plugin and the shared-grant configuration. Standalone Device Authorization no longer accepts or stores RFC 8707 resources, and onDeviceAuthRequest receives only clientId and scope. The OAuth integration rejects resource indicators that are not absolute, fragment-free URIs.

    The OAuth integration replaces the optional resource column with oauthClientId and resources. Regenerate and apply the schema when using it. Before upgrading from an earlier 1.7 prerelease, let pending OAuth device codes expire or delete them because they cannot be exchanged through the new integration.

  • #​10402 763a267 Thanks @​gustavovalverde! - Plugin database schemas can now define named or generated table-level indexes across multiple fields. SQL migrations and generated Drizzle or Prisma schemas resolve configured table and column names consistently, while the MongoDB adapter creates the same indexes before the first index-enforcing write.

  • #​9766 bf39cbf Thanks @​GautamBytes! - Add a server-only auth.api.consumePhoneNumberOTP API for custom phone OTP flows that need to verify and consume a code without creating or updating users or sessions.

  • #​10330 081d3c3 Thanks @​ping-maxwell! - Allow the username plugin's separate displayUsername field to be omitted by
    setting displayUsername: false on both the server and client plugins.

  • #​10059 49b5cf6 Thanks @​GautamBytes! - Device Authorization now creates unique database indexes for deviceCode and userCode, so each generated code must be unique in its column. Existing installations on every adapter must resolve duplicate 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 before running it.

    Generated codes are limited to 191 characters. Issuance makes up to 3 attempts to overcome unique-key collisions, then returns server_error if it cannot create a unique deviceCode and userCode. Default-generated user codes accept case changes and readability separators during verification, approval, and denial; custom codes outside the default alphabet are matched exactly. The /device limiter allows 5 requests over a window equal to the configured code lifetime, while /device/token polling keeps its separate interval behavior.

  • #​9645 e014029 Thanks @​ping-maxwell! - Harden the Electron OAuth flow and tighten custom-scheme trusted-origin matching.

    The Electron sign-in flow now mandates PKCE S256. Plain PKCE is rejected: the code_challenge_method parameter is gone and every authorization code is verified by hashing the verifier with SHA-256. The server no longer trusts an electron-origin header to set the request Origin. The Electron client now sends a real Origin (for example myapp:/), so upgrade the @better-auth/electron client and server together and make sure your app's scheme is in trustedOrigins. The unused disableOriginOverride option is removed.

    Custom-scheme entries in trustedOrigins now match by scheme and authority instead of string prefix. A host-less entry such as myapp:// or exp:// still trusts every host of that scheme, but a host-bearing entry such as myapp://callback matches that host exactly, so it is no longer satisfied by myapp://callback.attacker.tld.

  • #​9948 3d04fab Thanks @​yordis! - feat(generic-oauth): add refreshTokenParams config to forward extra params on token refresh

    Multi-tenant OIDC providers (Zitadel multi-org, Auth0 with audience) need to send extra body params on the refresh call to rescope tokens without a full authorization redirect. The generic-oauth plugin now accepts a refreshTokenParams option (object or sync/async function) that is merged into the refresh request body, with grant_type and refresh_token protected from override. The function form receives request metadata for the request that triggered the refresh, so request-scoped data (headers, cookies) is available without out-of-band state like AsyncLocalStorage.

    UpstreamProvider.refreshAccessToken now accepts an optional second ctx argument; the change is backwards compatible because existing implementations that take only refreshToken remain valid. See #​7554.

  • #​9069 c7d2253 Thanks @​gustavovalverde! - Rewrite the generic OAuth plugin as a first-class social provider with OAuth 2.1 security defaults. Providers now use signIn.social + callback/:id instead of dedicated plugin endpoints, with PKCE required by default (OAuth 2.1), RFC 9207 issuer validation, OIDC auto-discovery with openid scope injection, and typed provider IDs.

    Breaking changes:

    • signIn.oauth2({ providerId }) replaced by signIn.social({ provider })
    • oauth2.link() replaced by linkSocial()
    • Callback URL changed from /api/auth/oauth2/callback/:id to /api/auth/callback/:id
    • genericOAuthClient() removed; generic OAuth providers now use the standard social client APIs
    • pkce defaults to true (was false); set pkce: false for providers that reject PKCE
    • authorizationUrlParams and tokenUrlParams only accept Record<string, string>
    • issuer and requireIssuerValidation config fields removed; issuer validation is automatic via OIDC discovery
    • mapProfileToUser profile typed as OAuth2UserInfo & Record<string, unknown>
  • #​9966 ec8a38c Thanks @​gustavovalverde! - genericOAuth providers configured with a discoveryUrl now verify the provider's id_token against its published JWKS (signature, issuer, audience, and advertised algorithms) and bind it to the authorization request with a server-generated OIDC nonce. A sign-in whose id_token fails verification, or does not echo the expected nonce, is rejected.

    Set disableIdTokenNonceBinding: true on a provider that does not return the nonce claim in the authorization-code flow.

    These providers also accept client-submitted id_token sign-in through signIn.social({ idToken }), which previously returned ID_TOKEN_NOT_SUPPORTED.

    Providers configured with explicit endpoints instead of discoveryUrl are unchanged.

  • #​9368 430c895 Thanks @​GautamBytes! - Generic OAuth users can now sign out from the configured OpenID provider when they call authClient.signOut(). When a provider exposes a discovered or configured logout endpoint, Better Auth redirects to it and includes the stored id_token_hint when available. Pass callbackURL or configure postLogoutRedirectURI for the return flow, with optional state, or set disableRedirect to handle the returned url yourself. When multiple linked providers support logout, Better Auth selects the most recently updated account. Set disableProviderLogout: true to keep sign-out local.

  • #​9431 523f95c Thanks @​pi0! - feat: make Auth instance fetchable

  • #​10577 5c45abc Thanks @​gustavovalverde! - MCP clients that hit a scope wall now learn exactly which scopes to ask for. Missing protected scopes produce a 403 with an RFC 6750 insufficient_scope WWW-Authenticate challenge that names every missing scope. Clients can union those scopes into one authorization request instead of opening one browser redirect per scope.

    • Configure protected scopes with requiredScopes through RequireMcpAuthOptions or the matching createMcpProtectedRequestHandler verifier option. Exact membership remains the default; isScopeSatisfied can define hierarchical policies.
    • Use createInsufficientScopeError when an operation determines its required scopes dynamically. createResourceServerChallenge converts that signal and recognized token failures into safe RFC 6750 challenges.
    • Use challengeScopes only as the unauthenticated challenge hint.

    Handler-produced responses, ordinary permission denials, configuration failures, and unrelated thrown values keep their original status and identity.

  • #​10403 dbd302e Thanks @​gustavovalverde! - Scope account identity by trusted issuer instead of provider configuration. Accounts now use the unique (issuer, accountId) key, so aliases for one OpenID Connect issuer deduplicate one external identity while equal subjects from different issuers remain separate. This identity deduplication does not introduce independent grant or provider lifecycle records for aliases.

    This release requires Account.issuer but preserves Account.accountId as the provider-assigned account identifier. Account-specific APIs select the local Account.id through the accountId request property; token and provider-profile APIs can instead select the signed account cookie with useAccountCookie: true. Credential accounts use local:credential and the linked user's stable id as their provider identity.

    OAuth provider identity now comes from raw verified profiles. OpenID Connect discovery uses sub, plain OAuth uses id, and providers can declare accountSubject for another immutable field; Better Auth no longer switches between sub and id at runtime. getUserInfo().user no longer carries provider identity, and mapProfileToUser cannot return id. Read the selected identity from accountInfo.account.accountId instead of accountInfo.user.id. The generic microsoftEntraId helper now requires a concrete tenant GUID; use the built-in Microsoft provider for multi-tenant authorities.

    SSO account subjects are now protocol-defined. OIDC uses the verified sub claim, and SAML uses the signed NameID; mapping.id is removed from both configurations. A manual SAML configuration without metadata XML must set idpMetadata.entityID, because samlConfig.issuer identifies the service provider and no longer acts as the IdP identity.

    Apply the reviewed account-identity backfill in the Better Auth 1.7 upgrade guide before deploying. The generated schema migration cannot assign trusted issuers or resolve existing identity collisions automatically.

  • #​10359 8784c1c Thanks @​ping-maxwell! - Database joins have moved out of experimental into a stable option at advanced.database.joins (default: false).

    If you previously set experimental: { joins: true }, update your config to:

    advanced: {
      database: {
        joins: true,
      },
    }
    

    Adapters that support native joins use them when enabled. If an adapter cannot return joined data for a query, Better Auth falls back to additional queries and combines the results. Drizzle and Prisma users should ensure their schema includes the required relations (npx auth@latest generate).

  • #​9992 e53582c Thanks @​gustavovalverde! - The MCP plugin moves out of better-auth into its own package, @better-auth/mcp, built on @better-auth/oauth-provider. Import the authorization plugin and protected-request helpers from the package root. The in-core MCP client (createMcpAuthClient and its adapters) is removed; MCP protocol and transport clients come from the official version 2 @modelcontextprotocol/client and @modelcontextprotocol/server packages. The OAuth endpoints move from /mcp/* to /oauth2/*, with discovery at /.well-known/oauth-authorization-server and protected resource metadata at /.well-known/oauth-protected-resource. Discovery-based MCP clients pick up the new locations on their own.

    The shared-auth route helper is renamed from withMcpAuth to requireMcpAuth. The standalone protected-resource factory is renamed from mcpHandler to createMcpProtectedRequestHandler; pass one flat McpProtectedRequestHandlerOptions object with issuer, a single audience, optional jwtVerifyOptions, token-verification fields, and challenge fields. Its callback receives accessTokenClaims. requireMcpAuth verifies the access token against the published JWKS, validates DPoP proofs for DPoP-bound tokens, and passes the verified access-token claims to your handler.

    createInsufficientScopeError now validates a custom description against the RFC 6750 error_description character set when the error is constructed. Invalid descriptions throw TypeError("invalid error_description") before an error can reach resource-challenge serialization.

    MCP 2026-07-28 uses a stateless request and response transport. Serve MCP routes with version 2 of @modelcontextprotocol/server, configure createMcpHandler with legacy: "reject", wrap it with requireMcpAuth, and export only POST. Remove MCP-route GET and DELETE exports and session-store options such as redisUrl. OAuth clients, consent, authorization codes, refresh tokens, and security records remain durable authorization state.

    To migrate, install @better-auth/mcp, @better-auth/cimd, and the official version 2 MCP client or server package needed by your application; add the jwt() plugin, which is now required for token signing; and move options that were nested under oidcConfig to flat options on mcp({ ... }). The database models change: oauthApplication becomes oauthClient, with new oauthRefreshToken and oauthClientAssertion tables. Regenerate or migrate your schema with npx auth migrate or npx auth generate.

  • #​10204 0683a5f Thanks @​GautamBytes! - Microsoft sign-in now identifies Entra accounts with the stable oid claim in both the built-in microsoft provider and the Generic OAuth microsoftEntraId helper. Tokens without a valid oid are rejected, and the Generic OAuth helper refuses to initialize unless Microsoft discovery provides ID-token verification metadata. Existing Microsoft account rows created from sub must be migrated before upgrading.

  • #​9305 e7eb45b Thanks @​gustavovalverde! - feat(oauth): per-request additionalParams and loginHint parity across signIn.social, linkSocial, and signIn.sso

    Unified escape hatch for customizing the provider authorization URL on a per-request basis. Previously, dynamic parameters like Google's access_type=offline / prompt=consent, Cognito's identity_provider=Google, or Microsoft's domain_hint could only be set as static server configuration.

New capabilities
  • signIn.social, linkSocial, and signIn.sso accept additionalParams: Record<string, string>. Values are appended to the authorization URL as query parameters.
  • linkSocial also accepts loginHint, matching the surface of signIn.social and signIn.sso.
  • OAuthProvider.createAuthorizationURL gains additionalParams in its input contract; every built-in provider forwards it to the shared helper.
  • Generic-OAuth providers merge call-time additionalParams with the config-level authorizationUrlParams; call-time wins on key collision.
  • Cognito exposes a typed identityProvider?: string config option that maps to the identity_provider query parameter, avoiding magic strings.
Security
  • The shared createAuthorizationURL helper silently drops any caller-supplied key in RESERVED_AUTHORIZATION_PARAMS (state, client_id, redirect_uri, response_type, code_challenge, code_challenge_method, nonce, scope). The request-body Zod schema rejects the same keys with 400, so misuse is visible at the edge rather than silently overriding security-critical parameters. nonce is reserved so a caller cannot replace the OIDC nonce Better Auth generates when binding a discovery provider's id_token to the authorization request.
  • Providers that use non-standard client identifiers (wechat → appid, tiktok → client_key) additionally filter those keys so a caller cannot swap the configured OAuth app.
  • Provider protocol constants that are required for the integration to function (atlassian → audience, notion → owner) are merged last so caller-supplied additionalParams cannot override them. Configured defaults that represent operator intent (e.g. Google include_granted_scopes, Cognito identityProvider) remain caller-overridable.
  • signIn.sso rejects additionalParams with 400 when the resolved provider is SAML; the SAML AuthnRequest is signed and cannot carry caller-supplied query parameters, so silently dropping them would mislead integrators.
OpenAPI
  • Added ZodRecord handling to the OpenAPI generator so z.record() fields emit type: object with typed additionalProperties. Incidentally fixes a long-standing bug where additionalData was rendered as type: string.
Refactors
  • discord, roblox, zoom, and slack providers now delegate to the shared createAuthorizationURL helper and inherit its RFC behavior and reserved-key guard.
  • tiktok and wechat keep their manual URL construction (non-standard OAuth2 parameter names and URL fragment requirements) but thread additionalParams with the same reserved-key filter.

Closes #​2351.
Closes #​5441.
Closes #​5592.
Closes #​5604.
Supersedes #​4992 and #​5443.

  • #​10127 7c7313c Thanks @​gustavovalverde! - OAuth sign-in, account linking, callback, and proxy flows now build redirect_uri from the current request base URL when baseURL.allowedHosts is configured. Built-in social providers and generic OAuth providers now use the resolved request host for redirects in multi-host deployments.

    Custom OAuthProvider implementations can omit callbackPath when using the shared /callback/<provider-id> route. Set callbackPath only for custom callback routes.

  • #​10039 aedcb97 Thanks @​gustavovalverde! - feat(oauth-provider)!: DPoP-bound access tokens (RFC 9449)

    OAuth provider integrations can issue and verify DPoP sender-constrained tokens. Clients request them with dpop_bound_access_tokens at registration, dpop_jkt on the authorization request, or by targeting a resource configured with dpopBoundAccessTokensRequired. Issued tokens carry cnf.jkt, return token_type: "DPoP", and stay bound through refresh-token rotation, introspection, and userinfo.

    Resource servers verify DPoP requests with verifyAccessTokenRequest, which checks the Authorization: DPoP scheme, the proof, the request target, the access-token hash, and proof replay. The MCP package advertises DPoP in protected resource metadata and verifies DPoP-bound requests. Proof replay is rejected through the database-backed verification store, so anti-replay holds across instances. verifyAccessTokenRequest and requireMcpAuth use that store by default; build one with createDpopReplayStore(internalAdapter) or pass a custom dpop.replayStore. This needs database-backed verification storage: a secondary-storage-only deployment rejects DPoP requests rather than skipping replay protection.

    Breaking: the raw-token verifier verifyAccessToken is renamed to verifyBearerToken, both in better-auth/oauth2 and as the oauthProviderResourceClient action, and it rejects DPoP-bound tokens. Use verifyAccessTokenRequest on any endpoint that may receive them. The resource-request input type is renamed from AccessTokenRequestInput to ResourceRequestInput, and the DPoP algorithm option is signingAlgorithms everywhere.

    Run a schema migration for the DPoP token-binding fields: the confirmation column on the access-token and refresh-token tables. DPoP-bound clients also gain dpopBoundAccessTokens and resources dpopBoundAccessTokensRequired. No dedicated replay table is added; proof replay reuses the verification store.

  • #​9828 4f53b61 Thanks @​gustavovalverde! - Verify social-provider id_tokens with a single shared verifier.

    Client-submitted id_token sign-in (signIn.social({ idToken }) and account linking) is verified by one function instead of a per-provider verifyIdToken method. Each provider declares an idToken config with a JWKS source, issuer, and audience, and the core verifier runs the signature, issuer, audience, and nonce checks. A provider that declares no config rejects the client id_token path.

    PayPal previously accepted any decodable id_token without verifying its signature. PayPal derives identity from the access token, so it now declares no idToken config, and the client id_token path returns ID_TOKEN_NOT_SUPPORTED. PayPal sign-in through the redirect flow is unchanged.

    Custom providers that implement UpstreamProvider directly replace the removed verifyIdToken method with an idToken config:

    idToken: {
    	jwks: createRemoteJWKSet(new URL("https://issuer.example/.well-known/jwks.json")),
    	issuer: "https://issuer.example",
    	audience: clientId,
    },
    

    For verification that cannot use a local JWKS, pass idToken: { verify: async (token, nonce) => boolean }. The verifyIdToken and disableIdTokenSignIn provider options are unchanged.

  • #​9079 6f2948e Thanks @​gustavovalverde! - feat(oauth-provider): compute at_hash in ID tokens per OIDC Core §3.1.3.6

    ID tokens issued alongside an access token now include the at_hash claim, which cryptographically binds the two tokens to prevent token substitution attacks. The hash algorithm is selected based on the actual signing key's algorithm (EdDSA/Ed25519 uses SHA-512, RS/ES/PS384 uses SHA-384, RS/ES/PS512 uses SHA-512, all others use SHA-256).

    A new resolveSigningKey() export is available from better-auth/plugins to resolve the current JWKS signing key (including its algorithm). When using a custom jwt.sign callback, the signed ID token's header is validated against the declared algorithm to prevent at_hash mismatches.

  • #​10135 f68044d Thanks @​brentmitchell25! - Registered OAuth clients can now use the RFC 8628 device flow to obtain OAuth access tokens. Add oauthDeviceAuthorization() alongside oauthProvider() or mcp(), request a code at /device/code, and exchange it at /oauth2/token after the user approves it. OAuth and OpenID discovery advertise the device_authorization_endpoint.

    Device authorization requests can bind RFC 8707 resource indicators. GET /device returns the requested client, scopes, and resources to the authenticated user who owns the request. Token requests can reuse or narrow the approved resources, but cannot add new ones. Existing first-party device clients continue to receive Better Auth session tokens from /device/token.

    Enabling oauthDeviceAuthorization() adds nullable oauthClientId and resources fields to deviceCode. Regenerate and apply the database schema after adding the integration.

    Confidential clients authenticate at /device/code with their registered method, while public clients send client_id. Empty client_id, scope, user_id, and authentication values are treated as omitted; multiple non-empty values for any of these parameters return invalid_request, while multiple resource values remain supported. Unknown OAuth client IDs enter the standalone device flow only when oauthDeviceAuthorization({ validateClient }) accepts them.

  • #​9929 91f235f Thanks @​gustavovalverde! - Add requireEmailVerification to OAuth provider options, for built-in social providers and the Generic OAuth plugin. When a provider reports an unverified email, the user and account are still created or linked, but no session is issued: the OAuth callback redirects with ?error=email_not_verified, and ID token and One Tap sign-in return 403 EMAIL_NOT_VERIFIED. Verification emails follow the existing emailVerification.sendOnSignUp / sendOnSignIn settings.

    It is opt-in per provider and does not inherit emailAndPassword.requireEmailVerification, so existing social logins keep working. The gate checks the local user's verification state, so a user verified through another method keeps access. Only enable it for providers that report a trustworthy email_verified signal.

  • #​9648 d2a79ba Thanks @​brentmitchell25! - OAuth provider now models protected resources explicitly. Configure them with resources or create them through the oauthResource admin API. Each resource can define token TTLs, allowed scopes, custom JWT claims, and JWT signing pins.

    validAudiences is removed. Move each existing resource identifier into resources; link clients that should be limited to specific resources through oauthClientResource or Dynamic Client Registration resources.

    Access-token issuance now applies resource policy to the requested RFC 8707 resource values. The OAuth provider narrows scopes to resource allowlists, uses the shortest configured TTL, strips reserved RFC 9068 claim names from custom claims, emits jti, and keeps repeated resource form parameters.

    Refresh-token TTLs now use the shortest applicable lifetime. Deployments with a per-resource refreshTokenTtl longer than refreshTokenExpiresIn will see refresh tokens expire at the provider default instead of the longer resource value.

    JWT signing can now honor per-resource pins. signJWT() accepts signingKeyId and signingAlgorithm; JWKS adapters expose getKeyById() and getLatestKeyByAlg(). The jwks table adds nullable alg and crv columns, and keyPairConfigs can provision multiple algorithms in one keyring.

    After upgrading, run npx auth generate and apply the migration before deploying. The migration adds oauthResource, oauthClientResource, and the new jwks columns. Without it, resources using signingAlgorithm cannot find matching keys.

    Resource servers should publish RFC 9728 protected-resource metadata at their own origin. The OAuth provider exposes challenge helpers that point clients at that metadata.

    @better-auth/mcp now requires an explicit resource option. The plugin stores that identifier as an OAuth resource, publishes RFC 9728 protected-resource metadata for it, and binds issued access tokens to that resource. Existing mcp({ loginPage, consentPage }) setups should add a protected MCP resource identifier, for example resource: "https://api.example.com/mcp".

  • #​10397 bb6c102 Thanks @​ping-maxwell! - Add organization.getOrganization() to fetch organization metadata without members or invitations.

  • #​8931 34558bc Thanks @​GautamBytes! - Add opt-in JWKS-backed asymmetric JWT support for session_data cookie cache tokens, so services can verify cookie-cache JWTs with public keys instead of shared secrets. Enable it with jwt({ sessionCookieCache: true }) alongside session.cookieCache.strategy = "jwt".

  • #​8977 954b664 Thanks @​ruban-s! - allow passing userId and organizationId to the listUserTeams API. userId lets callers list teams for another member of an organization (gated behind the member:update permission). organizationId scopes the result to a specific organization without needing to switch the session's active organization, matching the pattern used by addTeamMember/removeTeamMember.

  • #​9969 76a3342 Thanks @​gustavovalverde! - Signing out with secondaryStorage and session.preserveSessionInDatabase now runs your configured session.delete hooks and marks the preserved session row as ended. OAuth Provider access and refresh tokens bound to that session are revoked and back-channel logout is dispatched on sign-out. Previously these hooks were skipped in this setup, so the tokens stayed valid until they expired.

  • #​9657 1e5b808 Thanks @​gustavovalverde! - Harden private_key_jwt and token endpoint client authentication, and add the helpers that make the fix structural.

    @better-auth/core/oauth2 now exposes encodeBasicCredentials and decodeBasicCredentials, a round-trip-tested pair that follows RFC 6749 §2.3.1 (application/x-www-form-urlencoded each value, split on the first : only). The decoder accepts the scheme case-insensitively and tolerates one or more spaces before the credentials per RFC 7235 §2.1. client_secret_basic on the client side and the Better Auth OAuth provider on the server side both go through these helpers, so credentials containing reserved characters round-trip cleanly across the stack and headers like basic xxx or Basic xxx are accepted.

    createPrivateKeyJwtClientAssertionGetter validates options eagerly. Unsupported algorithms (HS256, none), a JWK with no key material, and disagreement between an explicit algorithm and the JWK-embedded alg all throw at construction rather than on the first token request. signPrivateKeyJwtClientAssertion enforces the same checks for direct callers. Breaking: configurations that paired an unsupported JWK alg with a different explicit algorithm used to silently sign with the explicit option; they now fail at construction.

    Breaking: @better-auth/oauth-provider accepts client jwks metadata only as an RFC 7517 JWK Set object with a non-empty keys array. Replace jwks: [key] with jwks: { keys: [key] } in DCR payloads, administrative and user client creation, Client ID Metadata Documents, test fixtures, and generated client code. Remotely fetched jwks_uri responses must use the same object shape. EC keys must use P-256, P-384, or P-521; OKP keys must use Ed25519. When a key declares alg, it must be a supported private_key_jwt algorithm that matches the key type and curve; omit alg when the client chooses the algorithm in its assertion header. OAuth client rows previously written through oauthToSchema are already stored as JWK Set objects, so this is a request, configuration, and type migration rather than another database rewrite; audit rows written outside Better Auth separately.

    The SSO private_key_jwt flow redirects with error_description=no_private_key_available when a resolvePrivateKey callback returns no privateKeyJwk or privateKeyPem. The redirect path previously short-circuited only when the resolver was absent entirely; an empty resolver return fell through into an internal signing error.

    better-auth/test adds getHttpTestInstance, a counterpart to getTestInstance that binds a real HTTP listener on an OS-assigned port and constructs the auth instance against the discovered URL. It removes the temp-server-then-rebind race that test files have been individually copy-pasting.

  • #​8836 93d3871 Thanks @​gustavovalverde! - Add client authentication configuration for token endpoint requests across the stack, including private_key_jwt (RFC 7523).

    Generic OAuth providers now accept tokenEndpointAuth for token endpoint client authentication. Use tokenEndpointAuth: { method: "private_key_jwt", getClientAssertion } for JWT client assertions, { method: "none" } for public clients, and { method: "client_secret_basic" } or { method: "client_secret_post" } with clientSecret for explicit secret-based client authentication. The existing authentication: "basic" | "post" option remains available for secret-based token requests.

    Use createPrivateKeyJwtClientAssertionGetter() to sign RFC 7523 assertions from a private key. The assertion getter receives { clientId, tokenEndpoint, grantType }, so integrations do not duplicate client ID or token endpoint values inside assertion helpers. Core OAuth2 now exports private-key JWT-specific helpers and types: signPrivateKeyJwtClientAssertion, createPrivateKeyJwtClientAssertionGetter, PrivateKeyJwtSigningAlgorithm, and PRIVATE_KEY_JWT_SIGNING_ALGORITHMS.

    Token endpoint client authentication parameters are derived from clientId, clientSecret, and tokenEndpointAuth. Configured token endpoint authentication requires clientId; secret-based token endpoint authentication also requires clientSecret. Custom token parameters are for provider-specific fields and do not replace the configured client authentication values.

    refreshAccessToken() now forwards resource values to refresh-token requests, so RFC 8707 resource indicators work through both the high-level refresh helper and refreshAccessTokenRequest().

    The synchronous OAuth2 request builders createAuthorizationCodeRequest, createRefreshAccessTokenRequest, and createClientCredentialsTokenRequest have been removed. Use the async authorizationCodeRequest, refreshAccessTokenRequest, and clientCredentialsTokenRequest helpers instead.

    Servers verify JWT client assertions signed with asymmetric keys, and clients can use the same token endpoint authentication contract for authorization code, refresh, and client credentials token requests.

  • #​9134 652fa53 Thanks @​gustavovalverde! - The dynamic baseURL config now ignores x-forwarded-host and x-forwarded-proto unless you set advanced.trustedProxyHeaders: true.

    Requests using baseURL: { allowedHosts } now resolve the auth origin from Host by default, so forwarded headers cannot select another allowed host unless trusted proxy headers are enabled.

    Breaking change: if your proxy exposes the public hostname only through x-forwarded-host, set advanced.trustedProxyHeaders: true. Deployments where the proxy rewrites Host to the public hostname (nginx default, Vercel, Cloudflare, and Netlify) are unaffected.

    Migration:

    betterAuth({
      baseURL: { allowedHosts: [...] },
      advanced: {
        trustedProxyHeaders: true,
      },
    });
    
  • #​9240 729c00d Thanks @​adrianmxb! - feat(username): add immutable username option

    This allows users to set their username during sign-up or first update, but prevents changing it to a different value afterwards. Users can still update other profile fields.

  • #​10031 6fe9faa Thanks @​gustavovalverde! - Remove the deprecated oidcProvider plugin from better-auth/plugins. Migrate OIDC authorization-server integrations to @better-auth/oauth-provider.

  • #​10473 ed61b47 Thanks @​gustavovalverde! - Add transactional OIDC user resolution so applications can link verified issuer and subject pairs to exact existing users while preserving or updating the local profile.

  • #​10234 973fdde Thanks @​gustavovalverde! - The SIWE plugin now issues nonces before the wallet address or Chain ID is known. authClient.siwe.nonce() and authClient.siwe.getNonce() no longer accept wallet fields, getNonce must return an ERC-4361 nonce (8-250 alphanumeric characters), and SIWE verification now reads the wallet address and Chain ID from the signed ERC-4361 message.

  • #​9864 41cca60 Thanks @​GautamBytes! - Add a user.validateUserInfo provisioning gate that lets applications reject an identity before a user is created or a new account is linked. It runs once at the creation step for every method that provisions a user (OAuth, SSO/SAML, email/password, magic link, email OTP, anonymous, SIWE, phone number, admin-created users, and SCIM), including stateless setups with no persistent database.

    It also re-runs when an existing OAuth or SSO user signs in again (source.action is "sign-in"), where it receives the fresh provider email and profile so a domain or org policy can reject a user whose provider identity moved out of bounds. Non-provider returning sign-ins are not re-validated.

    The callback receives the mapped user plus a source describing the action (create-user, link-account, or sign-in), the method, and provider metadata: source.oauth for OAuth providers and source.sso for OIDC/SAML SSO providers. Return { error, errorDescription } to reject: browser flows redirect to the error URL and programmatic flows return a 403.

  • #​10036 ad35ead Thanks @​bytaesu! - Require Google One Tap server callbacks to resolve a Google client ID before verifying ID tokens. Configure oneTap({ clientId }) or socialProviders.google.clientId when using the One Tap plugin.

  • #​9057 544f1c6 Thanks @​gustavovalverde! - feat(two-factor)!: add OTP-only enablement and a discriminated response

    enableTwoFactor now accepts a method parameter ("otp" | "totp", default "totp") and returns a discriminated response with a method field.

method: "otp"
  • Sets twoFactorEnabled: true immediately.
  • Returns { method: "otp" }.
  • Requires otpOptions.sendOTP to be configured on the server; rejects with OTP_NOT_CONFIGURED otherwise.
method: "totp" (default)
  • Returns { method: "totp", totpURI, backupCodes }.
  • Rejects with TOTP_NOT_CONFIGURED if totpOptions.disable is set.

The existing skipVerificationOnEnable option remains supported for TOTP enrollment.

Breaking changes
  • Response shape changed: enableTwoFactor includes a method field in the response ("otp" or "totp").
Patch Changes
  • #​10014 73541c1 Thanks @​gustavovalverde! - Cloudflare Workers apps can now start when importing Better Auth subpaths such as better-auth/db. Beta builds were crashing during module initialization before application code ran.

  • #​10299 cf8eaac Thanks @​momomuchu! - widen drizzle-kit peer dependency range

  • #​10501 65fc17c Thanks @​KingIronMan2011! - Expand the optional drizzle-orm peer range to ^0.45.2 || >=1.0.0-rc.1 <2.0.0, matching @better-auth/drizzle-adapter and allowing Drizzle ORM v1 RC installations without peer dependency warnings.

  • #​10622 ecd83da Thanks @​gustavovalverde! - Sign-up no longer deadlocks when session cookie caching uses the JWT strategy on a single-connection SQLite database with native transactions enabled. JWKS key lookups and creation now resolve the transaction-scoped adapter instead of always querying the root connection, so minting a signing key during sign-up joins the surrounding transaction instead of racing it for the only available connection. On multi-connection databases (Postgres, MySQL) this also fixes a silent atomicity gap where a JWKS key created mid-transaction could commit independently of the transaction it was minted in.

  • #​10293 fe4c820 Thanks @​gustavovalverde! - npx auth migrate can now add required columns with static defaults and nullable
    unique columns to existing SQLite, PostgreSQL, and MySQL tables. Required unique
    columns still need distinct values to be backfilled manually before applying the
    unique constraint.

  • #​9898 7fe0e2b Thanks @​ItalyPaleAle! - Add clientAssertion support to the Microsoft Entra ID social provider.

  • #​9301 03e6c94 Thanks @​gustavovalverde! - Add allowIdpInitiated to GenericOAuthConfig and SSO OIDCConfig to support providers that initiate OAuth without a state parameter (e.g. Clever). When enabled, stateless callbacks restart the OAuth flow server-side with fresh state and PKCE, preserving CSRF protection. Also hardens parseState against undefined request bodies on GET callbacks.

  • #​10065 2196ea6 Thanks @​gustavovalverde! - OAuth and device-authorization responses that carry credentials now consistently send Cache-Control: no-store and Pragma: no-cache, so proxies, CDNs, and browsers never cache them. This covers the token, introspection, and userinfo endpoints, dynamic and admin client registration, client secret rotation, and the device code and device token responses, including the error responses from those endpoints.

    Endpoints declare this with metadata: { noStore: true }, and the header set is exported from @better-auth/core as NO_STORE_HEADERS for responses built by hand.

  • #​10124 06daf70 Thanks @​gustavovalverde! - Preserve the resolved OAuth user when overrideUserInfo returns null during account linking.

  • #​9304 e0d2b9e Thanks @​gustavovalverde! - Propagate sign-out to every connected app and cut off API access immediately, via OIDC Back-Channel Logout 1.0.

    When a user's session ends at the OP (sign-out, /oauth2/end-session, admin revoke, ban), @better-auth/oauth-provider now notifies every Relying Party that holds tokens for that session. The user's API access is cut off right away, instead of access tokens staying usable until their own TTL. Each client opts in by registering a backchannel_logout_uri (and optionally backchannel_logout_session_required) via DCR or the admin client-create endpoint. The provider signs a logout+jwt Logout Token per client and POSTs it to that client in parallel, with a short per-RP timeout.

    Breaking change. Introspection of an opaque or JWT access token whose bound session has ended now returns { active: false }, and /oauth2/userinfo rejects it with invalid_token. Previously the token stayed active until its own TTL. If you relied on access tokens outliving the user's session, that no longer holds.

    Refresh tokens without offline_access are revoked on session end; offline_access refresh tokens are preserved so long-lived API access can survive the browser session (OIDC Back-Channel Logout 1.0 §2.7). Access-token invalidation on session end is an additional OP hardening choice beyond §2.7, enforced by session liveness, so it holds even when the JWT plugin is disabled.

    Delivery runs through the host's background task handler when one is configured (Vercel waitUntil, Cloudflare ctx.waitUntil); without a handler it completes inline so notifications are not lost on request teardown. Configure advanced.backgroundTasks.handler on serverless runtimes to keep sign-out fast.

    Discovery at /.well-known/openid-configuration and /.well-known/oauth-authorization-server advertises backchannel_logout_supported: true and backchannel_logout_session_supported: true when the JWT plugin is enabled. Every registered backchannel_logout_uri must be a credential-free public HTTPS URL without a fragment; loopback HTTP is rejected for both public and confidential clients. CIMD documents cannot register back-channel logout metadata. The SSRF host guard, which blocks private, reserved, tunneled, and cloud-metadata hosts, also covers a private_key_jwt client's jwks_uri.

    Schema changes on @better-auth/oauth-provider:

    • oauthClient.backchannelLogoutUri: string | null
    • oauthClient.backchannelLogoutSessionRequired: boolean
    • oauthAccessToken.revoked: Date | null

    better-auth's signJWT gains an optional header argument, forwarded to custom remote signers. JWT profiles that need an explicit media type, such as typ: "logout+jwt", can now set it without reaching for the low-level signing primitives.

  • #​10125 a83152e Thanks @​gustavovalverde! - Create new OAuth accounts in the user creation transaction. Adapters with native
    transaction support roll back the user when the account write fails, while other
    adapters still perform the writes sequentially.

  • #​10128 97903c9 Thanks @​gustavovalverde! - Preserve previously granted OAuth scopes across sign-in re-authentication and refresh-token requests. account.scope now accumulates monotonically: newly granted scopes are merged in only when added via linkSocial, and providers returning a narrower scope claim than the user has granted no longer shrink the stored value.

  • #​10170 6ddb555 Thanks @​gustavovalverde! - Bundled dependencies were refreshed to their latest compatible releases, including jose, nanostores, the noble crypto packages, and SimpleWebAuthn. These updates are backward compatible and require no changes to existing projects.

  • #​10390 0de88f5 Thanks @​gustavovalverde! - SCIM connections can now provision Users, Groups, and direct memberships into application-defined provisioning domains without the organization or SSO plugins. Applications can map Group membership to validated custom roles through projections. The service also supports SCIM 2.0 discovery, filtering, pagination, response attribute selection, atomic PATCH operations, and common request patterns used by Microsoft Entra ID and Okta.

    This replaces the previous SCIM configuration, client APIs, database schema, and organization-backed Group model. Existing SCIM installations cannot migrate provisioning state in place. Follow the SCIM cutover in the 1.7 upgrade guide, including full directory reprovisioning, before resuming traffic.

    Deferred database side effects now run only after a successful transaction. A rolled-back User update no longer refreshes its cached profile, and a rolled-back bulk session revocation no longer invalidates sessions.

  • #​10505 d701f90 Thanks @​gustavovalverde! - One Tap, Electron, and Expo client plugins now compose with createAuthClient without TypeScript errors, and the resulting client preserves each plugin's inferred actions.

  • #​10621 59c4c83 Thanks @​gustavovalverde! - Allow test instances to enable native database transactions for postgres and mysql.

  • Updated dependencies [5c45abc, 763a267, 5d38b13, 692b22c, ea06c5a, 3d04fab, 430c895, de8394d, dbd302e, 8784c1c, ecd83da, e4818b5, 7fe0e2b, 0683a5f, e7eb45b, 7c7313c, aedcb97, 03e6c94, 4f53b61, 2196ea6, 91f235f, 34558bc, 1e5b808, 93d3871, 97903c9, ed61b47, 0de88f5, 3a79aff, 41cca60, d701f90]:

v1.6.34

Compare Source

better-auth
Bug Fixes
  • Fixed ACCOUNT_NOT_FOUND when retrieving an access token after stateless OAuth sign-in links a new provider to an existing user (#​11300)

For detailed changes, see CHANGELOG

@better-auth/sso
Bug Fixes
  • Fixed SAML sign-in for providers that use samlConfig.callbackUrl as their ACS URL without spMetadata.metadata, which had failed with invalid_saml_response since 1.6.21 (#​11730)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@​bytaesu, @​gustavovalverde

Full changelog: v1.6.33...v1.6.34

v1.6.33

Compare Source

@better-auth/core

Bug Fixes
  • Fixed database option type inference for projects that do not use Cloudflare Workers. (#​11291)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@​better-release[bot]

Full changelog: v1.6.32...v1.6.33

v1.6.32

Compare Source

better-auth
Bug Fixes
  • Improved diagnostics for Cloudflare Turnstile verification failures (#​11286)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@​better-release[bot]

Full changelog: v1.6.31...v1.6.32

v1.6.31

Compare Source

better-auth
Features
  • Added an option to disable OpenTelemetry span creation for individual auth instances (#​11228)

For detailed changes, see CHANGELOG

@better-auth/core
Features
  • Added an option to disable OpenTelemetry span creation for individual auth instances (#​11228)

For detailed changes, see CHANGELOG

Contributors

Thanks to everyone who contributed to this release:

@​better-release[bot]

Full changelog: v1.6.30...v1.6.31

v1.6.30

Compare Source

Patch Changes

v1.6.29

Compare Source

Patch Changes

v1.6.28

Compare Source

Patch Changes

v1.6.27

Compare Source

Patch Changes

v1.6.26

Compare Source

Patch Changes

v1.6.25

Compare Source

Patch Changes

v1.6.24

Compare Source

Patch Changes
  • #​10235 03dc5a0 Thanks @​ping-maxwell! - Fixes silent foreign-key and adapter-join misrouting when a user remaps a built-in model name to a string that collides with another schema key

  • #​10357 7508940 Thanks @​c-nicol! - Fixes Kysely migration generation for new-table fields that are both unique: true and index: true.

  • #​10342 bae7198 Thanks @​ping-maxwell! - Fix organization.listMembers failing with "User not found for member" for orgs with more than ~100 members by applying the same membership limit to the users query.

  • #​10336 ef4d273 Thanks @​Tushar-Khandelwal-2004! - Prevent verification callbacks from failing auth requests when cloning the request throws.

  • #​10333 99dbdd7 Thanks @​c-nicol! - Fixes Drizzle schema generation for fields that are both unique: true and index: true.

  • #​10368 086ca91 Thanks @​gaurav0107! - Force-validate the request Origin on the magic-link (/sign-in/magic-link) and email-otp (/email-otp/send-verification-otp) send endpoints, including cookieless requests, to match the built-in /sign-in/email and /sign-up/email routes. A cookieless cross-origin POST can no longer trigger a magic-link or verification-OTP email to an arbitrary address. Cookieless requests that carry no Origin (server-to-server) are unaffected.

  • #​10290 8f2dedd Thanks @​GautamBytes! - Expose the remote MCP auth client's 401 challenge headers to browser clients using CORS.

  • #​10453 4e685ee Thanks @​ping-maxwell! - OpenAPI now includes user.additionalFields and plugin user schema fields (e.g. username plugin username / displayUsername) on /sign-up/email and /update-user request bodies.

  • #​10190 3bf0e49 Thanks @​gaurav-init! - Pass the endpoint context as the second argument to beforeDeleteOrganization and afterDeleteOrganization hooks in the organization plugin, matching the signature shown in the docs and the existing databaseHooks pattern. The Stripe plugin's beforeDeleteOrganization wrapper now forwards the context to user-supplied hooks instead of dropping it.

  • #​10040 f59a0ee Thanks @​shiminshen! - Organization invitations now let the database generate their id when ID generation is delegated to the database (e.g. advanced.database.generateId: "uuid" with a UUID-capable adapter such as Postgres), matching every other model. Previously createInvitation always generated the invitation id in application code, so invitation rows received an app-generated value instead of a database-generated one while organizations, members and teams correctly deferred to the database (better-auth/better-auth#10024). A caller-provided id (e.g. via beforeCreateInvitation) is still honored.

  • #​10302 0f2cc1b Thanks @​momomuchu! - Prefer exact schema-key matches over modelName aliases in getDefaultModelName, so remapping a built-in table onto another table's schema key (e.g. user.modelName = "account") does not reroute internal adapter queries to the wrong table.

  • #​9787 ae78109 Thanks @​ping-maxwell! - Fixes an issue where useSession({ throw: true }) incorrectly excluded null from its data type.

  • #​10222 46d2bf0 Thanks @​ping-maxwell! - fix: add no-store cache-control headers to get-session route

  • #​10316 29a373e Thanks @​vinay-oppuri! - Recognize SQLite BIGINT as a valid number type in migration diffs so database-backed rate limiter columns like lastRequest no longer report spurious pending changes on every run.

  • #​10379 f6d18fa Thanks @​ping-maxwell! - fix(client): restore auth query revalidation and signal listeners after remount

  • #​5753 f23ce50 Thanks @​ping-maxwell! - feat(last-login-method): beforeStoreCookie option for GDPR compliance

  • #​10376 c4d1dda Thanks @​ping-maxwell! - Pass the request endpoint context as a third argument to verifyIdToken, so custom ID token verifiers can read request headers (for example Apple's user-agent requirement).

  • Updated dependencies [6758231, 54fab08, c4d1dda]:


Configuration

📅 Schedule: (UTC)

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

🚦 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.


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

This PR has been generated by Mend Renovate CLI.

This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---|---|---| | [better-auth](https://better-auth.com) ([source](https://github.com/better-auth/better-auth/tree/HEAD/packages/better-auth)) | [`1.6.23` → `1.7.25`](https://renovatebot.com/diffs/npm/better-auth/1.6.23/1.7.25) | ![age](https://developer.mend.io/api/mc/badges/age/npm/better-auth/1.7.25?slim=true) | ![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/better-auth/1.7.25?slim=true) | ![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/better-auth/1.6.23/1.7.25?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/better-auth/1.6.23/1.7.25?slim=true) | --- ### Release Notes <details> <summary>better-auth/better-auth (better-auth)</summary> ### [`v1.7.25`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1725) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.24...v1.7.25) ##### Patch Changes - [#&#8203;12603](https://github.com/better-auth/better-auth/pull/12603) [`ad07edd`](https://github.com/better-auth/better-auth/commit/ad07edda1bcb4b5e40368989aa70801839a5926b) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - An error thrown from an endpoint no longer drops the `Access-Control-Expose-Headers` names that a hook or the endpoint set earlier. The names from both are kept. Before, the error's value replaced the list, so a browser client on another origin could lose access to headers a hook had exposed. - Updated dependencies \[[`69e14d8`](https://github.com/better-auth/better-auth/commit/69e14d89b856c95ea0998869c9cb8742d483a808), [`2a8750b`](https://github.com/better-auth/better-auth/commit/2a8750b285725a45ff58b0f43487afb796c854e9), [`75c0821`](https://github.com/better-auth/better-auth/commit/75c0821fa61c8ed0ea20f1027d5ad42d16e0daae)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.25 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.25 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.25 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.25 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.25 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.25 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.25 ### [`v1.7.24`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1724) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.23...v1.7.24) ##### Patch Changes - [#&#8203;12593](https://github.com/better-auth/better-auth/pull/12593) [`b77e783`](https://github.com/better-auth/better-auth/commit/b77e7834123ce5431af6467b3c758d01a2c9bb92) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Looking up a verification value now deletes expired verification rows at most once a minute per server instance, instead of on every lookup. This removes a delete query from most verification reads. Each flow still checks a value's expiry before it accepts the value. - Updated dependencies \[[`0597f4c`](https://github.com/better-auth/better-auth/commit/0597f4c2f3e576c90e22866bfcf81384c7af308f)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.24 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.24 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.24 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.24 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.24 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.24 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.24 ### [`v1.7.23`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1723) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.22...v1.7.23) ##### Patch Changes - [#&#8203;12543](https://github.com/better-auth/better-auth/pull/12543) [`d6e9bbd`](https://github.com/better-auth/better-auth/commit/d6e9bbdbb71a2472fac387bfabacf7c1cbfe4f26) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - `getTestInstance` from `better-auth/test` now gives each test file its own MongoDB database and closes its MongoDB client on cleanup. Before, every MongoDB test instance shared the `better-auth` database and dropped it during cleanup, so test files running in parallel deleted each other's data. - Updated dependencies \[[`9dc64fb`](https://github.com/better-auth/better-auth/commit/9dc64fbba9c39c10bc809e54a46a9ae21e4e15d4), [`cfa73e9`](https://github.com/better-auth/better-auth/commit/cfa73e90fae636f22179b933c29171bada503515), [`41a2526`](https://github.com/better-auth/better-auth/commit/41a25269dbd0d2942ba95b7c362cb266f977bccb), [`c10b35d`](https://github.com/better-auth/better-auth/commit/c10b35da7383ddefe1710190a100dabb7283803e)]: - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.23 - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.23 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.23 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.23 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.23 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.23 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.23 ### [`v1.7.22`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1722) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.21...v1.7.22) ##### Patch Changes - [#&#8203;12324](https://github.com/better-auth/better-auth/pull/12324) [`80c407b`](https://github.com/better-auth/better-auth/commit/80c407b7ca15d70b734a68b048dfbd22614c2242) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed `ReferenceError: window is not defined` when a client session subscription is cleaned up after a test environment such as happy-dom tears down its globals. - [#&#8203;12462](https://github.com/better-auth/better-auth/pull/12462) [`d32a30a`](https://github.com/better-auth/better-auth/commit/d32a30af372591f42b06a396755d38c0c76d4f28) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed session polling never firing when the runtime's `navigator` does not report connectivity. - [#&#8203;12324](https://github.com/better-auth/better-auth/pull/12324) [`80c407b`](https://github.com/better-auth/better-auth/commit/80c407b7ca15d70b734a68b048dfbd22614c2242) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed client session refresh listeners piling up on the server during SSR. - [#&#8203;12321](https://github.com/better-auth/better-auth/pull/12321) [`946177e`](https://github.com/better-auth/better-auth/commit/946177e509ce1cb0010bfd98562609f72abe4da7) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Revoking your current session with `revokeSession` or `revokeSessions`, and deleting your account with `deleteUser({ token })`, now expire the session cookies, so a cached session is no longer returned afterwards. - [#&#8203;12459](https://github.com/better-auth/better-auth/pull/12459) [`d210421`](https://github.com/better-auth/better-auth/commit/d210421a26ef7f7728ea7e668bba755b3003fe37) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - `getSession` no longer leaves an expired session in the request context after returning `null`. - [#&#8203;12331](https://github.com/better-auth/better-auth/pull/12331) [`485567e`](https://github.com/better-auth/better-auth/commit/485567e2c9d594a35810cfdfe07247e8cb744e24) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - OAuth sign-up rejected by a `user.create.before` hook returning `false` no longer logs a `TypeError`. It still fails with `unable_to_create_user`. - [#&#8203;12334](https://github.com/better-auth/better-auth/pull/12334) [`ec3047d`](https://github.com/better-auth/better-auth/commit/ec3047da814ec22294573db6d9cdde3ba616992a) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed `deleteOrganization` clearing the session's active organization when a `beforeDeleteOrganization` hook rejected the delete. The active organization is now cleared only after the organization is actually deleted. - [#&#8203;12330](https://github.com/better-auth/better-auth/pull/12330) [`d521343`](https://github.com/better-auth/better-auth/commit/d521343fc3e3c8a27ff63b7a62551459e85de38c) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fix `deferSessionRefresh` refresh requests failing with 415 in Next.js and Nuxt, where the bodyless `POST /get-session` reached the handler as an empty stream without a `Content-Type`. - [#&#8203;12451](https://github.com/better-auth/better-auth/pull/12451) [`71052af`](https://github.com/better-auth/better-auth/commit/71052af880e62e2a3ce8e4a200880e020c309348) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - On Cloudflare Workers, consuming a verification value no longer hangs when an earlier request for the same identifier ended mid-query. A superseded code can also no longer be consumed while a newer one is being verified on a database without transactions. - [#&#8203;12448](https://github.com/better-auth/better-auth/pull/12448) [`79219b4`](https://github.com/better-auth/better-auth/commit/79219b4f8e8b4cff0e184333fc46ef64e3c47e2e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Schema validation now runs when the auth instance is first used instead of when it is created, so a module-scope instance on Cloudflare Workers no longer logs a validation error on every cold start. On every runtime, a mismatch is no longer logged at startup and is reported by the requests that fail. - Updated dependencies \[[`6536174`](https://github.com/better-auth/better-auth/commit/6536174213f63a8d544cd0d018659515bab5f8ad), [`262adc0`](https://github.com/better-auth/better-auth/commit/262adc0e76969fe04ef9355d1c77972bd5f0a1a8), [`e9d8e59`](https://github.com/better-auth/better-auth/commit/e9d8e5977cb37ab1aa3824f1bb67b5c99a444e9b), [`79219b4`](https://github.com/better-auth/better-auth/commit/79219b4f8e8b4cff0e184333fc46ef64e3c47e2e)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.22 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.22 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.22 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.22 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.22 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.22 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.22 ### [`v1.7.21`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1721) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.20...v1.7.21) ##### Patch Changes - [#&#8203;12126](https://github.com/better-auth/better-auth/pull/12126) [`4739122`](https://github.com/better-auth/better-auth/commit/4739122fa67c77cde71c4d1b60d84483fc1bf11b) Thanks [@&#8203;hardikguptaofficialgit](https://github.com/hardikguptaofficialgit)! - On Prisma with MongoDB, when your id columns use `@db.ObjectId`, magic link and email OTP sign-in for users with an unverified email no longer fail with a 500 (`Malformed ObjectID`). SAML sign-in, DPoP-bound OAuth requests, and `private_key_jwt` client authentication are fixed the same way, and Sign-In with Ethereum now keeps the email the user supplied instead of a placeholder. Setups with plain `String` id columns and other databases are unaffected. - [#&#8203;12392](https://github.com/better-auth/better-auth/pull/12392) [`a277020`](https://github.com/better-auth/better-auth/commit/a277020fa3f0f379e153d008daad64267dc39227) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Fixed the device approval page showing a valid code as invalid when the same signed-in user looked it up twice at once, such as under React Strict Mode in development. - [#&#8203;12052](https://github.com/better-auth/better-auth/pull/12052) [`1c3410a`](https://github.com/better-auth/better-auth/commit/1c3410a5c3af5bb93505c8da3671f538b286d6b1) Thanks [@&#8203;lakshya-dhariwal](https://github.com/lakshya-dhariwal)! - If you set `advanced.backgroundTasks.handler` and your database adapter runs transactions (the built-in Kysely connection, MongoDB with `client`, or Prisma or Drizzle with `transaction: true`), email sign-up now keeps the database writes made while sending the verification email or running `onExistingUserSignUp`. Before, those writes failed because the sign-up had already been saved. With `emailOTP({ overrideDefaultEmailVerification: true })`, the code was never stored, so new users could not verify their email. Whatever adapter or background handler you use, a sign-up verification email now goes out after the new user is saved, and `onExistingUserSignUp`, when a duplicate sign-up triggers it, runs after the request's database work completes. If `sendVerificationEmail` throws, the sign-up succeeds and the error is logged; before, the sign-up failed. To reject a sign-up, use a `databaseHooks.user.create.before` hook. If `onExistingUserSignUp` throws, the error is logged and the request still returns the generic response. - [#&#8203;12179](https://github.com/better-auth/better-auth/pull/12179) [`9105261`](https://github.com/better-auth/better-auth/commit/91052614cb82051d19d4290c09aaaf2059585ede) Thanks [@&#8203;ghazy2001](https://github.com/ghazy2001)! - Generic OAuth providers configured with `discoveryHeaders` now send those headers when fetching the provider's signing keys from a `jwks_uri` on the same origin as `discoveryUrl`. Before, the keys request went out without them. Providers whose key endpoint requires the headers rejected it, so every ID token failed verification and sign-in failed. A `jwks_uri` on another origin still gets no headers. With `discoveryHeaders` set, the discovery request now follows redirects only within the origin of `discoveryUrl`, so the headers no longer reach another host. If your `discoveryUrl` redirects to another origin, discovery now fails and Better Auth logs an error. Set `discoveryUrl` to the final URL. Providers without `discoveryHeaders` are unaffected. - [#&#8203;12401](https://github.com/better-auth/better-auth/pull/12401) [`c3a94c5`](https://github.com/better-auth/better-auth/commit/c3a94c546af3c3bf4573f254b666facb4a94fc2d) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Generic OAuth providers configured with `authorizationHeaders` now send those headers when refreshing an access token, not only when exchanging the authorization code. Before, a provider whose token endpoint requires one of those headers accepted the sign-in but rejected every refresh. Providers without `authorizationHeaders` are unaffected. `refreshAccessToken` and `refreshAccessTokenRequest` from `@better-auth/core/oauth2` accept an optional `headers` input for providers that need extra headers on refresh. Client authentication headers still take precedence over it. Extra headers whose names differ only in case from a client authentication header, such as `Authorization` with `authentication: "basic"`, no longer produce a combined, invalid header on the code exchange or refresh. Client authentication replaces them, including headers a custom `tokenEndpointAuth` hook sets in any casing. - [#&#8203;12404](https://github.com/better-auth/better-auth/pull/12404) [`9b40ac6`](https://github.com/better-auth/better-auth/commit/9b40ac62af33d16952778bc3acfc0dc86d25a84a) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Plugin packages no longer import from the `better-auth` root, so bundlers that split code stop emitting unused Kysely chunks when used with `better-auth/minimal`. - [#&#8203;12395](https://github.com/better-auth/better-auth/pull/12395) [`b0ced9d`](https://github.com/better-auth/better-auth/commit/b0ced9d2576db02f5c41b493e07e847d5a260e4d) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - On Cloudflare Workers, sign-ins with a `genericOAuth` provider that uses `discoveryUrl`, and OAuth provider requests that use a configured resource (`resources`), no longer hang. An auth instance created at module scope retries discovery and resource seeding on later requests. Previously, if the request running that retry ended first, every later request that needed it waited forever. Each request now retries on its own until one succeeds. On other runtimes, concurrent requests may send a few more discovery requests or resource lookups while the identity provider or database is unreachable. - [#&#8203;11912](https://github.com/better-auth/better-auth/pull/11912) [`5c12074`](https://github.com/better-auth/better-auth/commit/5c120747e4d5ab37f1fa71fee11653ade82739bc) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - If you use PostgreSQL and other schemas in the same database are dropped while Better Auth reads the database, for example one schema per tenant or parallel test runs, the startup schema check and `npx auth@latest migrate` no longer fail intermittently with `schema "…" does not exist` or `relation "…" does not exist`. The startup schema check now reads only the schemas on the connection's `search_path`, plus any schema set with `schemaName` or a schema plugin, and `migrate` reads only the schema it migrates. Before, both read every schema in the database. If no other schemas change while Better Auth reads the database, nothing changes for you. - [#&#8203;12403](https://github.com/better-auth/better-auth/pull/12403) [`e64f624`](https://github.com/better-auth/better-auth/commit/e64f62427300a1fb6885c0ceb494818f77684a82) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - SIWE verification no longer includes the internal error message (for example a database error) in its 401 response. The original error is now logged on the server instead. - Updated dependencies \[[`4739122`](https://github.com/better-auth/better-auth/commit/4739122fa67c77cde71c4d1b60d84483fc1bf11b), [`c3a94c5`](https://github.com/better-auth/better-auth/commit/c3a94c546af3c3bf4573f254b666facb4a94fc2d), [`4080630`](https://github.com/better-auth/better-auth/commit/40806302bf5245c675120bbd1e60f70e1f168252), [`5c12074`](https://github.com/better-auth/better-auth/commit/5c120747e4d5ab37f1fa71fee11653ade82739bc), [`b241ffc`](https://github.com/better-auth/better-auth/commit/b241ffc9404fa182fa60a2cf5c54765334729f4f), [`57fa843`](https://github.com/better-auth/better-auth/commit/57fa8432d89117e7a1ce49cdb7f194dbde0839e3), [`1a79da5`](https://github.com/better-auth/better-auth/commit/1a79da50672c8a7ccea89c93b11b25aea86262be), [`2d10ae7`](https://github.com/better-auth/better-auth/commit/2d10ae733eacab919ce13413ac9e840f2ea7f1c6), [`ef4d4f7`](https://github.com/better-auth/better-auth/commit/ef4d4f7dc457fc78b0414e9c6a60827a7a3eab0e)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.21 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.21 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.21 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.21 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.21 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.21 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.21 ### [`v1.7.20`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1720) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.19...v1.7.20) ##### Patch Changes - Updated dependencies \[[`a3f50d0`](https://github.com/better-auth/better-auth/commit/a3f50d064dd3bd1ee8a83a687ea54428aaec8997), [`e9b43d7`](https://github.com/better-auth/better-auth/commit/e9b43d7149cec0b52e8194b1341e582d0ea19bba), [`265629c`](https://github.com/better-auth/better-auth/commit/265629cb958913cf4459ea7545ae212ccaa485e1), [`e9b43d7`](https://github.com/better-auth/better-auth/commit/e9b43d7149cec0b52e8194b1341e582d0ea19bba)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.20 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.20 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.20 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.20 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.20 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.20 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.20 ### [`v1.7.19`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1719) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.18...v1.7.19) ##### Patch Changes - [#&#8203;12323](https://github.com/better-auth/better-auth/pull/12323) [`a10121d`](https://github.com/better-auth/better-auth/commit/a10121d33cfccc525c79777319288d220940164b) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed the `active-sessions` list in secondary storage being stored without an expiry when the last remaining session had less than a second left. - [#&#8203;12335](https://github.com/better-auth/better-auth/pull/12335) [`c41f8ff`](https://github.com/better-auth/better-auth/commit/c41f8ff08755c4b493a82b89d5f4555a4998c122) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed `/admin/create-user` leaving a user without a credential account when the password was rejected (for example by the Have I Been Pwned plugin), which made retrying with the same email fail with `USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL`. - [#&#8203;12338](https://github.com/better-auth/better-auth/pull/12338) [`e786566`](https://github.com/better-auth/better-auth/commit/e786566dc553b3e21a9bdec0e7c4777dfb51bacf) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed `/admin/create-user` leaving a user without a credential account when creating that account failed, which made retrying with the same email fail with `USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL`. - [#&#8203;12336](https://github.com/better-auth/better-auth/pull/12336) [`ac778b7`](https://github.com/better-auth/better-auth/commit/ac778b70e9c35ce3fb13014fe7122f8faa1cd178) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Client actions now send the HTTP method passed in the second-argument fetch options, the same as when it is nested under `fetchOptions`. Passing a lowercase `get` or `HEAD` method no longer fails because a request body was attached. - [#&#8203;12246](https://github.com/better-auth/better-auth/pull/12246) [`5eb69d8`](https://github.com/better-auth/better-auth/commit/5eb69d8371aac809c81b16b08f82f05a448c5583) Thanks [@&#8203;AryaKesharwani](https://github.com/AryaKesharwani)! - `/device/token` now returns `invalid_client` instead of `invalid_grant` when `validateClient` rejects the `client_id`, matching `/device/code` and RFC 6749 section 5.2. - [#&#8203;12319](https://github.com/better-auth/better-auth/pull/12319) [`4b0327b`](https://github.com/better-auth/better-auth/commit/4b0327bfa363395fdba24bd51bdf18668e62657d) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Plugin schema overrides (`modelName` and field names) no longer leak into other instances of the same plugin in one process. - [#&#8203;12320](https://github.com/better-auth/better-auth/pull/12320) [`2d5ef64`](https://github.com/better-auth/better-auth/commit/2d5ef64daee607af5ff2175262dc387a767f7e35) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fix OAuth sign-in failing with `state_mismatch` during the daylight saving time fall-back hour on servers that do not run in UTC. The stored OAuth state is no longer created already expired. - [#&#8203;12337](https://github.com/better-auth/better-auth/pull/12337) [`5f0a0fc`](https://github.com/better-auth/better-auth/commit/5f0a0fcef3b15ea4a657a08a46b71df96ada77cc) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - The OpenAPI schema now documents query parameters for endpoints whose query schema is wrapped in `z.optional()`, such as `/get-session` and Stripe's `/subscription/list`. - [#&#8203;12332](https://github.com/better-auth/better-auth/pull/12332) [`89e74b8`](https://github.com/better-auth/better-auth/commit/89e74b81e554e2a4edc5bf925adad59dfdace734) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed `organization.getInvitation` returning a missing, expired, or non-pending invitation error without a `code`. It now returns `INVITATION_NOT_FOUND` like the other invitation endpoints. - [#&#8203;12318](https://github.com/better-auth/better-auth/pull/12318) [`525bd7a`](https://github.com/better-auth/better-auth/commit/525bd7a7d866012c88f0fff2c488d352a6eb8f2e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fix `listMembers` failing with a syntax error on PostgreSQL and MSSQL when the filter or offset matches no members. - [#&#8203;12366](https://github.com/better-auth/better-auth/pull/12366) [`23a60a3`](https://github.com/better-auth/better-auth/commit/23a60a31f42ab9f78f3db976d232df37f8414e9d) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed SIWE sign-in leaving a user behind when creating its wallet address or account failed, which blocked that wallet from signing in again. - [#&#8203;12340](https://github.com/better-auth/better-auth/pull/12340) [`29e45f8`](https://github.com/better-auth/better-auth/commit/29e45f84d5946a002330b56dc07600a156984a2c) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed social sign-up hanging on single-connection databases when the username plugin is enabled and the provider profile maps a username. - Updated dependencies \[[`8412a5f`](https://github.com/better-auth/better-auth/commit/8412a5f43b724648b3faaa452bec0a6ba8e01765), [`34ee100`](https://github.com/better-auth/better-auth/commit/34ee1008d8269d32b875e2743b9805090863f2f4)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.19 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.19 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.19 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.19 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.19 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.19 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.19 ### [`v1.7.18`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1718) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.17...v1.7.18) ##### Patch Changes - [#&#8203;12259](https://github.com/better-auth/better-auth/pull/12259) [`9e2a0ec`](https://github.com/better-auth/better-auth/commit/9e2a0ecbe7c976a291d214f8e709cfd1f7619c65) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Fixed "revoke all sessions" with `secondaryStorage` and `storeSessionInDatabase` so that sessions created while the revocation runs are revoked too. Sessions created by a `session.delete.after` hook still stay active. - [#&#8203;12244](https://github.com/better-auth/better-auth/pull/12244) [`85f4342`](https://github.com/better-auth/better-auth/commit/85f4342472522e91c7a263184962b55d5586778e) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - `getTestInstance` with `testWith: "mysql"` no longer hangs. It expects a MySQL server at `127.0.0.1:3306` whose `root` password is `root_password`, and it creates and drops a separate database for each instance, so several instances can run in one test file. - [#&#8203;12250](https://github.com/better-auth/better-auth/pull/12250) [`278bb28`](https://github.com/better-auth/better-auth/commit/278bb28191fbf16a4df38fce434727c4950991bd) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - `getTestInstance` from `better-auth/test` now closes the Postgres connection pool and the SQLite database it created when the test file finishes. Previously both stayed open until the test worker exited. With Vitest's default `sequence.hooks: "stack"`, `afterAll` hooks in your test files still run before this cleanup and can keep using the instance. - Updated dependencies \[[`9342a73`](https://github.com/better-auth/better-auth/commit/9342a73e3635719011828c798e276c0f908792de)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.18 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.18 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.18 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.18 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.18 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.18 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.18 ### [`v1.7.17`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1717) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.16...v1.7.17) ##### Patch Changes - [#&#8203;12181](https://github.com/better-auth/better-auth/pull/12181) [`b6a87ad`](https://github.com/better-auth/better-auth/commit/b6a87ad96f443805eef45f20feed37f867a5f6a7) Thanks [@&#8203;aliamanatali](https://github.com/aliamanatali)! - A remembered sign-in or sign-up now refreshes its session like any other remembered session, even if an earlier sign-in in the same browser used `rememberMe: false`. Previously the earlier choice stayed in a `dont_remember` cookie until the browser closed, so the new session stopped sliding and expired `expiresIn` after it was created. - [#&#8203;12216](https://github.com/better-auth/better-auth/pull/12216) [`cff7c1c`](https://github.com/better-auth/better-auth/commit/cff7c1ceda1fb4446b033b897c84d3cff92c2019) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Denying a device authorization request now rejects a session that was revoked but still present in the cookie cache, matching the behavior of approving a request. - [#&#8203;12217](https://github.com/better-auth/better-auth/pull/12217) [`16aa412`](https://github.com/better-auth/better-auth/commit/16aa41252139533922a4f6324d9a83f36e160eeb) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - IP wildcard trusted origins such as `exp://192.168.*.*:*` no longer match custom-scheme hosts with out-of-range or zero-padded octets like `192.168.999.999`. - [#&#8203;12219](https://github.com/better-auth/better-auth/pull/12219) [`9150314`](https://github.com/better-auth/better-auth/commit/9150314da38bfe1d0da718db1e828a41c21f5d3f) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Signing up with an empty `phoneNumber` with the phone number plugin no longer stores the empty string or makes later sign-ups fail. Linking an account whose profile maps an empty phone number no longer replaces the user's verified number. - [#&#8203;12212](https://github.com/better-auth/better-auth/pull/12212) [`f5be351`](https://github.com/better-auth/better-auth/commit/f5be35166ea9f1d2ea81b9eb2d42e36db0d04e69) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed sessions created by a `session.delete.after` hook during "revoke all sessions" being treated as revoked immediately when `secondaryStorage` is used with `storeSessionInDatabase`. - [#&#8203;12211](https://github.com/better-auth/better-auth/pull/12211) [`98bcd98`](https://github.com/better-auth/better-auth/commit/98bcd9849c3e392f70d15e768795279109d7c90e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed TOTP verification rejecting sessions created from an API key, so API-key sessions can now finish two-factor enrollment. - [#&#8203;12228](https://github.com/better-auth/better-auth/pull/12228) [`fbff537`](https://github.com/better-auth/better-auth/commit/fbff53792533ef5b9b96a6457d77bd71aff8e071) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Trusted devices are honored again when an app runs the two-factor sign-in step from its own hook on another sign-in path, such as email OTP. - Updated dependencies \[[`fc2e00b`](https://github.com/better-auth/better-auth/commit/fc2e00bb4fd65e80246fdefcd5d8d5b1ea9d1512)]: - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.17 - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.17 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.17 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.17 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.17 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.17 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.17 ### [`v1.7.16`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1716) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.15...v1.7.16) ##### Patch Changes - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - `/admin/update-user` now requires the new `user:set-phone` permission to change `phoneNumber` or `phoneNumberVerified`. If you use a custom access control, add `set-phone` to your statements and grant it to roles that should change phone numbers. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Anonymous users are no longer linked or deleted on a sign-in that is still waiting for a second factor. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Email verification and change email links now only work for the account they were issued to. Opening a change email link no longer signs the user in, and it verifies the new email only if the current one was verified. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Wildcard trusted origins now match custom-scheme URLs by their parsed scheme, host, and path, and no longer match a URL with userinfo or one that cannot be parsed as an origin. A wildcard host made only of digits, dots, and wildcards, such as `192.168.*.*`, now matches only IPv4 addresses in that range. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Looking up a user by email now returns only an account whose stored address matches the requested one, ignoring case, even on databases with accent-insensitive collations. The email OTP password reset routes now validate the address and send the code to the address on the account. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Email change codes from the email OTP plugin are now bound to the requesting account and the new address. Codes sent before the upgrade must be requested again. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed the email OTP `checkVerificationOtp` and `resetPassword` endpoints and the phone number `resetPassword` endpoint allowing more OTP guesses than `allowedAttempts` when requests arrive concurrently. A correct check still leaves the OTP usable for the follow-up call. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - The Expo authorization proxy now sets the signed state cookie only for an unexpired state issued by the server. `getAuthStateVerificationIdentifier` is now exported for plugins that look up OAuth state records. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Passkey registration, device authorization, two-factor enrollment and verification, social account linking, and phone number changes now reject a session revoked in the database or secondary storage, even when cookie caching is enabled. Sessions created from API keys are refused on these routes, except two-factor enrollment and phone number changes. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Generic OAuth providers whose discovery document has a `jwks_uri` now reject a user profile whose `sub` differs from the ID token's `sub`. Direct ID-token sign-in and linking with a generic OAuth provider now resolve the profile from the verified ID token only and ignore a submitted access or refresh token. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Pending organization invitations are now only returned to members whose role can create invitations, and creator-role invitations only to members who hold that role. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Organization invitations use random IDs when `advanced.database.generateId` is not set, including on adapters with sequential default IDs such as MongoDB. On MongoDB, new invitation IDs are strings instead of ObjectIds. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Dynamic access control roles reused through `hasPermission`'s `useMemoryCache` option are now cached per request and per auth instance instead of in a process-global cache, so role changes apply on the next request. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Organization role lists are now trimmed and split on commas wherever they are stored or compared, and invitations with an empty role are rejected. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - With the phone number plugin, `updateUserInfoOnLink` no longer copies an unverified `phoneNumber` from the linked provider's profile. An existing verified number is kept. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - With `requireVerification`, phone number password reset now works only for a verified number. No reset code is sent to an unverified number, and `resetPassword` returns `PHONE_NUMBER_PENDING_VERIFICATION` without adding or changing a password. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - With the phone number plugin, a new user no longer stores a `phoneNumber` that was not verified. Sign-up and sign-in routes reject one sent in the request, and a number from a social profile or in `data` passed to `createUser` is dropped unless it is marked verified. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Phone number verification and password reset codes are now stored under their own namespaces, so a phone number request can no longer create, consume or delete verification codes that belong to other flows. Codes sent before the upgrade must be requested again. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Account linking and new OAuth users now count an email as verified only when the provider claim is `true` or the string `"true"`, which covers generic OAuth, Cognito, and Microsoft Entra ID claims sent as strings. Any other `emailVerified` value, including a non-boolean value returned from `mapProfileToUser` such as `1`, now reads as unverified. - [#&#8203;12198](https://github.com/better-auth/better-auth/pull/12198) [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - With secondary storage, revoking all of a user's sessions or deleting the user now also ends sessions missing from the cached session list. - Updated dependencies \[[`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e), [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e), [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e), [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e), [`f25081a`](https://github.com/better-auth/better-auth/commit/f25081ac0476a9a4295f7a9736ae7239974c7b1e)]: - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.16 - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.16 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.16 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.16 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.16 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.16 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.16 ### [`v1.7.15`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1715) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.14...v1.7.15) ##### Patch Changes - [#&#8203;12100](https://github.com/better-auth/better-auth/pull/12100) [`7769e91`](https://github.com/better-auth/better-auth/commit/7769e91a3e126335f0a67c08d40ec5a4723bfd36) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - The admin plugin now rejects banned users when a session is created outside an endpoint, such as through `internalAdapter.createSession`. - [#&#8203;12099](https://github.com/better-auth/better-auth/pull/12099) [`3f17193`](https://github.com/better-auth/better-auth/commit/3f17193023ed76a174a1564352cf0cbb7cddb948) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - The admin `update-user` endpoint now rejects `data.id` with a 400 error instead of attempting to change the user's primary key. - [#&#8203;12145](https://github.com/better-auth/better-auth/pull/12145) [`804ba8a`](https://github.com/better-auth/better-auth/commit/804ba8aa3a07b8840804960f24ec93c0ff61ce11) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Headers and cookies set by a `hooks.before` hook that does not return early now reach the response instead of being dropped. When the endpoint sets the same header or cookie, the endpoint's value wins. - [#&#8203;12108](https://github.com/better-auth/better-auth/pull/12108) [`1391673`](https://github.com/better-auth/better-auth/commit/1391673e2cdba75240dd6ff67f7de94b72076449) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - The HubSpot helper in `genericOAuth` now encodes the access token as a single path segment when fetching token info, so tokens containing reserved characters are no longer truncated, and it returns `null` instead of requesting `/access-tokens/undefined` when no access token is present. - [#&#8203;12142](https://github.com/better-auth/better-auth/pull/12142) [`f63fb0e`](https://github.com/better-auth/better-auth/commit/f63fb0ec275d4ed810fbe2561af8d839c7b5f069) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Organization and API key metadata, and OAuth proxy sign-ins whose `additionalData` contains a `constructor` field, no longer fail with "Potential prototype pollution attempt detected". - [#&#8203;12098](https://github.com/better-auth/better-auth/pull/12098) [`4cdcb1c`](https://github.com/better-auth/better-auth/commit/4cdcb1c002c1e13ca3aeca1e1d88be46b17dcbb9) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Error messages for a malformed `BETTER_AUTH_SECRETS` entry now report the entry position instead of including the entry value. - [#&#8203;12097](https://github.com/better-auth/better-auth/pull/12097) [`6e7fa27`](https://github.com/better-auth/better-auth/commit/6e7fa279a52e5e31091e6bda78ab5d56a5c50d24) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed `/sign-in/username` and `/is-username-available` rejecting a username that sign-up accepts when `validationOrder.username` is `post-normalization`. Both endpoints now validate the same value as sign-up. - [#&#8203;12109](https://github.com/better-auth/better-auth/pull/12109) [`264de2b`](https://github.com/better-auth/better-auth/commit/264de2b8974277e7a9eb8bef3a4e2ca1eef8e68a) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fix the Vue client's `useSession(useFetch)` returning no session during Nuxt SSR when `baseURL` is inferred from the environment. - Updated dependencies \[[`9ef0c5b`](https://github.com/better-auth/better-auth/commit/9ef0c5bda9c5ebc46d2c5d93fc0874893d1c6256)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.15 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.15 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.15 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.15 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.15 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.15 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.15 ### [`v1.7.14`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1714) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.13...v1.7.14) ##### Patch Changes - [#&#8203;12031](https://github.com/better-auth/better-auth/pull/12031) [`d2cb7f5`](https://github.com/better-auth/better-auth/commit/d2cb7f56ca2e9faa9b1522117570c438aa367484) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Two-factor TOTP now accepts imported Base32 secrets stored as `base32:<secret>`. - Updated dependencies \[[`2947ab7`](https://github.com/better-auth/better-auth/commit/2947ab76c3a141f205813de9201661ee21e327c2)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.14 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.14 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.14 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.14 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.14 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.14 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.14 ### [`v1.7.13`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1713) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.12...v1.7.13) ##### Patch Changes - [#&#8203;11985](https://github.com/better-auth/better-auth/pull/11985) [`9cb95fe`](https://github.com/better-auth/better-auth/commit/9cb95fe3d0f5b3bdd08522b42ef362739dc1597a) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Type declarations no longer list type-only exports as values, so importing one as a value now fails type checking instead of failing at runtime. - Updated dependencies \[[`2712441`](https://github.com/better-auth/better-auth/commit/2712441e355204389c88a7eb546974cce1936b5f), [`1cf4b2c`](https://github.com/better-auth/better-auth/commit/1cf4b2c8f5bffdbdb101f11a6fefedbab6cf2994)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.13 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.13 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.13 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.13 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.13 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.13 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.13 ### [`v1.7.12`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1712) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.11...v1.7.12) ##### Patch Changes - [#&#8203;11925](https://github.com/better-auth/better-auth/pull/11925) [`fb48619`](https://github.com/better-auth/better-auth/commit/fb48619083d292639797eaa3c7168a010f1e3308) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - The JWT plugin now verifies JWTs and session cookie-cache JWTs with the algorithm of the stored key instead of the one named in the token header, and logs a warning when it signs or verifies a JWT without an issuer or audience. - [#&#8203;11924](https://github.com/better-auth/better-auth/pull/11924) [`681aa5c`](https://github.com/better-auth/better-auth/commit/681aa5c4439a5845e027d41eda01b84acc9c58b6) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - `auth.api.getSession()` now keeps the original database error as `error.cause` when the session lookup fails, so server code can tell a transient failure from a bug. - [#&#8203;11930](https://github.com/better-auth/better-auth/pull/11930) [`b18e3b8`](https://github.com/better-auth/better-auth/commit/b18e3b8fdfe3d6a03bfdf18b34ed8667459d85dd) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - OAuth callbacks completed through the OAuth proxy now run the same checks as the standard OAuth callback, including issuer and ID token nonce validation. - Updated dependencies \[[`681aa5c`](https://github.com/better-auth/better-auth/commit/681aa5c4439a5845e027d41eda01b84acc9c58b6), [`4bc78ba`](https://github.com/better-auth/better-auth/commit/4bc78baa83aecea47ff30062d68820a578efeed3), [`876608d`](https://github.com/better-auth/better-auth/commit/876608da86036d9ca4b5c2a37ed0b01e29264e52), [`083a743`](https://github.com/better-auth/better-auth/commit/083a74329be51ed35c1981524f9c543984f08bd3), [`492134a`](https://github.com/better-auth/better-auth/commit/492134aeba6707bdfb371cf8ef817c945d6fb44f)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.12 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.12 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.12 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.12 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.12 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.12 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.12 ### [`v1.7.11`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1711) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.10...v1.7.11) ##### Patch Changes - [#&#8203;10719](https://github.com/better-auth/better-auth/pull/10719) [`bd65db0`](https://github.com/better-auth/better-auth/commit/bd65db02c1e7c854dc6090bd1e3885fc0cd55ec8) Thanks [@&#8203;rendermani](https://github.com/rendermani)! - Recognize PostgreSQL `int8` as a valid number type in migration diffs. Kysely's introspector reports `pg_type.typname`, so Better Auth's own `bigint` columns (such as the database-backed rate limiter's `lastRequest`) came back as `int8` and were reported as a type mismatch on every run. - [#&#8203;11521](https://github.com/better-auth/better-auth/pull/11521) [`37a75c9`](https://github.com/better-auth/better-auth/commit/37a75c95d148e0db4f29f30cabcd2ceb26bc7085) Thanks [@&#8203;aryan1306](https://github.com/aryan1306)! - Fix database-backed rate limiting returning an excessively large `X-Retry-After` header instead of the remaining seconds in the rate-limit window. - Updated dependencies \[[`ce96569`](https://github.com/better-auth/better-auth/commit/ce965696d1f8695e96e6b88490fc8ea2f8ab78ae), [`2f6ca3f`](https://github.com/better-auth/better-auth/commit/2f6ca3f370e80081d911a5e95a39393920b6d22e), [`dfd8dd2`](https://github.com/better-auth/better-auth/commit/dfd8dd287a4cfeb1d3541bb4e004e8f33edce15f)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.11 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.11 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.11 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.11 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.11 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.11 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.11 ### [`v1.7.10`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1710) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.9...v1.7.10) ##### Patch Changes - [#&#8203;11393](https://github.com/better-auth/better-auth/pull/11393) [`eebd47a`](https://github.com/better-auth/better-auth/commit/eebd47a0e7103caae35e4ac46971e293d1579594) Thanks [@&#8203;breken-ai](https://github.com/breken-ai)! - If you use the `lastLoginMethod` plugin with `oneTap`, signing in through Google One Tap now records `"google"`, the same value as signing in with the Google button. Before, a One Tap sign-in recorded nothing: the `better-auth.last_used_login_method` cookie kept its previous value or stayed unset, and so did `user.lastLoginMethod` with `storeInDatabase`. If your `customResolveMethod` includes a `/one-tap/callback` → `"google"` workaround, remove that mapping; keep the resolver if it handles other routes. - [#&#8203;11821](https://github.com/better-auth/better-auth/pull/11821) [`09d07e6`](https://github.com/better-auth/better-auth/commit/09d07e6feac8c0173551c9cbaee222d403c31fd9) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - The Google One Tap prompt (`authClient.oneTap()` without `button`) no longer calls `getSkippedReason()`, which Google's FedCM migration guide says to remove. Before, it called it under any `promptOptions.fedCM` setting. A skipped prompt now ends the flow and calls `onPromptNotification` right away instead of retrying. Google doesn't report why a prompt was skipped, and a prompt the user closed stays hidden for Google's cooldown period, so the retries could not bring it back. Dismissed prompts still retry as before. Google Identity Services still logs its FedCM migration warning, because it does so whenever `prompt()` receives a listener. The plugin needs the listener to track dismissals and skips, so the warning is expected and safe to ignore. - [#&#8203;11819](https://github.com/better-auth/better-auth/pull/11819) [`eaecb72`](https://github.com/better-auth/better-auth/commit/eaecb7229eeef5cfd763477b12f7dd2e44b89e51) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Google One Tap now applies your `socialProviders.google.mapProfileToUser` and custom `getUserInfo`, the same as the standard Google sign-in. Before, One Tap ignored both and always stored the token's `name`, `email`, `email_verified`, and `picture` claims. If you configured either one, users created through One Tap now get the fields it returns, including any extra fields such as a first name from `given_name`. Returning users are updated from it on each One Tap sign-in when `socialProviders.google.overrideUserInfoOnSignIn` is on, and so are existing users that One Tap links when `account.accountLinking.updateUserInfoOnLink` is on. Check that your mapper suits One Tap users too. If you set `socialProviders.google.overrideUserInfoOnSignIn`, One Tap sign-ins now update returning users' name, image, and email from Google, as the redirect sign-in already does. Setups that configure none of these, including `oneTap({ clientId })` with no Google provider, are unaffected. - [#&#8203;11820](https://github.com/better-auth/better-auth/pull/11820) [`88c9a37`](https://github.com/better-auth/better-auth/commit/88c9a378562fcb240085bd803a243062e2d28f47) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - If you set `autoSelect` or `cancelOnTapOutside` on `oneTapClient()`, the One Tap prompt and the Sign in with Google button now use them. Before, only values passed to `authClient.oneTap()` took effect, so with `autoSelect: true` on the plugin, returning users still had to pick an account. Values passed to `oneTap()` still override the plugin's. Apps that set these options only on `oneTap()`, or not at all, are unaffected. - [#&#8203;11696](https://github.com/better-auth/better-auth/pull/11696) [`0cf1f7c`](https://github.com/better-auth/better-auth/commit/0cf1f7c6bc7b9870b276fdf7d9f5c38af6dbaa30) Thanks [@&#8203;drewsephski](https://github.com/drewsephski)! - You can now import the One Tap plugin from `better-auth/plugins/one-tap`, as you can for other plugins. Previously this import failed with a "not exported" error. Imports from `better-auth/plugins` and `better-auth/client/plugins` are unchanged. - [#&#8203;11822](https://github.com/better-auth/better-auth/pull/11822) [`414e845`](https://github.com/better-auth/better-auth/commit/414e845f128011369b451a3ffd15d8d314351cdb) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - You can now import the Captcha and OpenAPI plugins from `better-auth/plugins/captcha` and `better-auth/plugins/open-api`, as you can for other plugins. Previously these imports failed with a "not exported" error. Imports from `better-auth/plugins` are unchanged. If your TypeScript config uses `"moduleResolution": "node"` (or `node10`), `better-auth/plugins/multi-session` and `better-auth/test` now resolve their types. Previously TypeScript reported that it could not find their declarations. Projects using `bundler`, `node16`, or `nodenext` resolution are unaffected. - Updated dependencies \[[`fedd055`](https://github.com/better-auth/better-auth/commit/fedd05586c5a7ee2eb6b8d16b88b08dfb7e16a89), [`bbd0a19`](https://github.com/better-auth/better-auth/commit/bbd0a19c30fc384213d1e98b9a8a54a903a0b574), [`99cdfe7`](https://github.com/better-auth/better-auth/commit/99cdfe7c2e6bb3b8556b1ea8c3a336369cba6976)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.10 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.10 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.10 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.10 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.10 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.10 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.10 ### [`v1.7.9`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#179) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.8...v1.7.9) ##### Patch Changes - [#&#8203;11766](https://github.com/better-auth/better-auth/pull/11766) [`6edaa61`](https://github.com/better-auth/better-auth/commit/6edaa61e629625909a97d191809473030f24a2b6) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - An `onAPIError.onError` handler that throws or rejects no longer fails the request or causes an unhandled promise rejection. The error is logged and the original error response is returned. Async handlers now finish before the response is sent. - Updated dependencies \[]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.9 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.9 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.9 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.9 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.9 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.9 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.9 ### [`v1.7.8`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#178) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.7...v1.7.8) ##### Patch Changes - [#&#8203;11512](https://github.com/better-auth/better-auth/pull/11512) [`4175b34`](https://github.com/better-auth/better-auth/commit/4175b34a8f70d5abb542ac7fc205dc42f564c03a) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Align the installed `@better-auth/utils` version with `better-call` while accepting `^0.4.2 || ^0.5.0` across official package peers. - [#&#8203;11565](https://github.com/better-auth/better-auth/pull/11565) [`b1cfc1e`](https://github.com/better-auth/better-auth/commit/b1cfc1ec1e006a52654c5441422fa26c240ee123) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Omit non-returned custom fields from inferred output schema types. - [#&#8203;11540](https://github.com/better-auth/better-auth/pull/11540) [`678c555`](https://github.com/better-auth/better-auth/commit/678c555beb533c1416c1411a0d77129de2e97d31) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Align cookie security attributes with RFC 6265bis. - [#&#8203;10591](https://github.com/better-auth/better-auth/pull/10591) [`193efff`](https://github.com/better-auth/better-auth/commit/193efff3d290740b1fd7c564b4b9b6f18343d05e) Thanks [@&#8203;kdelay](https://github.com/kdelay)! - Expired or revoked sessions now clear stale cookies when using `customSession`. - [#&#8203;11625](https://github.com/better-auth/better-auth/pull/11625) [`369176e`](https://github.com/better-auth/better-auth/commit/369176e7f6201b4170269991bd3a14d3722eda41) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Fixed single-use checks for apps that set `advanced.database.generateId: "uuid"`. Default id generation is unaffected, and no migration is needed. - On SQLite and MySQL, SAML sign-in, DPoP-bound requests checked against the database replay store, `private_key_jwt` client authentication, and magic link or email OTP sign-ins that adopt an unverified account failed with a `NOT NULL` error on the `id` column. They now work. SIWE sign-in ignored the `email` field and used the wallet-derived address; it now uses the supplied email when no other account has it. - On Postgres and MongoDB, a reused SAML assertion, DPoP proof, or client assertion was accepted. Each one is now accepted only once. With `generateId: "serial"`, behavior is unchanged: these checks need string ids, and Better Auth now logs a warning the first time one of these flows runs. - [#&#8203;11391](https://github.com/better-auth/better-auth/pull/11391) [`352d04c`](https://github.com/better-auth/better-auth/commit/352d04c8eb40c597a54da8667d68e05a1cb69426) Thanks [@&#8203;breken-ai](https://github.com/breken-ai)! - Fix `customSession` treating session lookup failures as signed-out users. Server-side `getSession` now throws an API error, and HTTP clients receive an error response. - [#&#8203;11750](https://github.com/better-auth/better-auth/pull/11750) [`e9d9921`](https://github.com/better-auth/better-auth/commit/e9d99212174c231559b8f75679fc0464dfd4345f) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Generic OAuth providers configured with `discoveryUrl` now recover without a restart when discovery fails at startup. Since 1.7.3, a provider whose discovery request failed at boot (for example, because the identity provider was still starting) was skipped, and every sign-in returned `PROVIDER_NOT_FOUND` until the app restarted. Better Auth now retries discovery on the next sign-in, callback, or account-linking request for that provider, and the provider works as soon as discovery succeeds. While discovery keeps failing, sign-in and account linking still return `PROVIDER_NOT_FOUND` and callbacks redirect with `error=oauth_provider_not_found`, and each request may wait up to 10 seconds for the retry. - [#&#8203;11549](https://github.com/better-auth/better-auth/pull/11549) [`1bd5c37`](https://github.com/better-auth/better-auth/commit/1bd5c37884f95766c737e35f4be86dfb46d31d36) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fix `lastLoginMethod` failing sign-in when `cookieName` or `maxAge` is explicitly `undefined`. - [#&#8203;11174](https://github.com/better-auth/better-auth/pull/11174) [`766cfb3`](https://github.com/better-auth/better-auth/commit/766cfb3f42f08665bd52b25b035d0041f000373d) Thanks [@&#8203;Bekacru](https://github.com/Bekacru)! - Stop double-decoding `callbackURL`, `newUserCallbackURL` and `errorCallbackURL` on magic link verify so percent-encoded characters in callback query params are preserved. - [#&#8203;11505](https://github.com/better-auth/better-auth/pull/11505) [`c46992f`](https://github.com/better-auth/better-auth/commit/c46992f6ce548fad0f26f5f07dc8525893e8196c) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Preserve the referenced model and field keys when generating Kysely migrations, so custom table names that match another model key produce the intended foreign keys. - [#&#8203;11718](https://github.com/better-auth/better-auth/pull/11718) [`1d95ebf`](https://github.com/better-auth/better-auth/commit/1d95ebf3fe9a788d9b6566495e0016e60d1fd7fd) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fix missing device sessions and failed automatic session switching after cache eviction when `storeSessionInDatabase` is enabled and `preserveSessionInDatabase` is disabled. Automatic switching now follows the order of held sessions, and large lists or sessions whose users no longer exist no longer cause valid sessions to disappear. - [#&#8203;10266](https://github.com/better-auth/better-auth/pull/10266) [`c68b2cd`](https://github.com/better-auth/better-auth/commit/c68b2cd8a5a03012ea77071ce74a7708210ea5a9) Thanks [@&#8203;shiminshen](https://github.com/shiminshen)! - Projects that enable `exactOptionalPropertyTypes` can use the `oauthProvider()` and `mcp()` plugins again. Since 1.7.0, adding either plugin to `betterAuth()` failed to type-check with a TS2322 error. - [#&#8203;11637](https://github.com/better-auth/better-auth/pull/11637) [`2627237`](https://github.com/better-auth/better-auth/commit/2627237fae3d02c53b3ba68b3812cbc31905a24b) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Projects using the organization plugin with `skipLibCheck: false` no longer get `TS2536: Type '"role"' cannot be used to index type ...` from `crud-members.d.mts`. The `role` returned by `getActiveMemberRole` is now typed as the organization's role names directly. Projects with `skipLibCheck: true` (the default in most templates) were unaffected. - [#&#8203;10866](https://github.com/better-auth/better-auth/pull/10866) [`3d1f008`](https://github.com/better-auth/better-auth/commit/3d1f008ae381dec01751725b7422985c84c74aae) Thanks [@&#8203;heliohm](https://github.com/heliohm)! - The organization plugin no longer pulls every zod locale into server bundles. - [#&#8203;11626](https://github.com/better-auth/better-auth/pull/11626) [`67743ed`](https://github.com/better-auth/better-auth/commit/67743ed6e8d873114b723fc5c196aea17a870509) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Password resets and password changes no longer keep the new password when revoking the user's sessions fails. With `revokeSessionsOnPasswordReset` or `revokeOtherSessions`, database sessions are revoked and the password is saved in one transaction when transactions are enabled for the adapter, so a failure leaves the old password and sessions in place. Sessions in secondary storage are still removed after commit, on a best-effort basis. This covers `/reset-password`, the email OTP and phone number reset endpoints, and `/change-password`. `onPasswordReset` now runs after sessions are revoked, so a throwing callback no longer leaves them active, and a callback that lists the user's sessions no longer sees the revoked ones. - [#&#8203;10698](https://github.com/better-auth/better-auth/pull/10698) [`f0ce580`](https://github.com/better-auth/better-auth/commit/f0ce580fbdfd62bba262fb5c3625736197fd0eac) Thanks [@&#8203;jashkarangiya](https://github.com/jashkarangiya)! - If you set the phone number plugin's `verifyOTP` option, `/phone-number/reset-password` now checks the code with it, as `/phone-number/verify` already does. Before, password reset always compared the code with the one Better Auth generated, so apps that verify codes through a provider such as Twilio Verify could not reset passwords. If your `sendPasswordResetOTP` sends the code Better Auth generates, make sure `verifyOTP` accepts that code. Apps that don't set `verifyOTP` are unaffected. - [#&#8203;11646](https://github.com/better-auth/better-auth/pull/11646) [`07a0938`](https://github.com/better-auth/better-auth/commit/07a09389ef00c0fc322983f2eca463194c642865) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - A failed password reset no longer spends the reset link or OTP. `/reset-password`, `/email-otp/reset-password` and `/phone-number/reset-password` now check the token or code without consuming it, validate and hash the new password, then consume it in the same transaction as the session revocation and password write. A rejected password, such as one refused by `haveIBeenPwned`, always leaves the link or OTP usable. A later failure, such as a failed session revocation or password write, also leaves it usable when the adapter's transactions are enabled and verification values are stored in the database (the default without secondary storage, or `verification.storeInDatabase: true`). Wrong codes still count against the OTP attempt limit. - [#&#8203;11643](https://github.com/better-auth/better-auth/pull/11643) [`92dfcea`](https://github.com/better-auth/better-auth/commit/92dfceaa14c287543f7cf62239641f4f7406cfb2) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - A sign-up that fails after its session is created no longer leaves that session in secondary storage. With `storeSessionInDatabase` enabled and `preserveSessionInDatabase` off, the secondary-storage copy is written only after the sign-up commits, and listing a user's sessions now reads the database, so a session whose cached copy failed to write is still listed and revoked. With other secondary-storage settings, sign-up, passkey registration and SSO sign-in with `resolveUser` keep the session write inside their transaction, so a storage failure rolls back the new user, passkey or account instead of keeping them without a session. A session revoked by a `session.create.after` database hook also stays revoked. - [#&#8203;11488](https://github.com/better-auth/better-auth/pull/11488) [`e15625f`](https://github.com/better-auth/better-auth/commit/e15625ff382a2dd9ea41e7d142d4098f6a8715a2) Thanks [@&#8203;Bekacru](https://github.com/Bekacru)! - SvelteKit 3 projects can now install Better Auth without `--legacy-peer-deps`. The `@sveltejs/kit` peer range now includes `^3.0.0`; SvelteKit 2 projects are unaffected. - Updated dependencies \[[`4175b34`](https://github.com/better-auth/better-auth/commit/4175b34a8f70d5abb542ac7fc205dc42f564c03a), [`435645e`](https://github.com/better-auth/better-auth/commit/435645e5eb6fcd0a3a9a2bbd1fd486f47ff4c4cc), [`369176e`](https://github.com/better-auth/better-auth/commit/369176e7f6201b4170269991bd3a14d3722eda41), [`0cfe07d`](https://github.com/better-auth/better-auth/commit/0cfe07dccd5046308b4e957603cbd6d0918dca2c), [`9904b29`](https://github.com/better-auth/better-auth/commit/9904b29ece82ecf10f0bf5aa4b194d77aea2e898), [`e78f15a`](https://github.com/better-auth/better-auth/commit/e78f15aa316bedeaf53a654bb07d4a222ca5e578), [`60525e0`](https://github.com/better-auth/better-auth/commit/60525e08fd8f4c10385bcd9f215224a2144ef94a), [`8133265`](https://github.com/better-auth/better-auth/commit/813326536c79291d70897a18b63272fa1e7346a7), [`ea85d13`](https://github.com/better-auth/better-auth/commit/ea85d133d640233c1564417f3bad611d99356bb9), [`916ef5c`](https://github.com/better-auth/better-auth/commit/916ef5c5307103d9bea44446a40da5d31de2b3fe), [`1d95ebf`](https://github.com/better-auth/better-auth/commit/1d95ebf3fe9a788d9b6566495e0016e60d1fd7fd), [`6214610`](https://github.com/better-auth/better-auth/commit/62146108c70bbba57aab034bc79f98f016147e78), [`67743ed`](https://github.com/better-auth/better-auth/commit/67743ed6e8d873114b723fc5c196aea17a870509), [`c46d2a6`](https://github.com/better-auth/better-auth/commit/c46d2a6ac4442b2b9d2f301619a9f1b9ef400f55), [`92dfcea`](https://github.com/better-auth/better-auth/commit/92dfceaa14c287543f7cf62239641f4f7406cfb2), [`a9f455d`](https://github.com/better-auth/better-auth/commit/a9f455d586f4124bf8c544345f47130a93580103), [`9725f1b`](https://github.com/better-auth/better-auth/commit/9725f1b361a0b5db8a1b519e3c5aacae3ed2fb98)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.8 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.8 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.8 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.8 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.8 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.8 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.8 ### [`v1.7.7`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#177) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.6...v1.7.7) ##### Patch Changes - [#&#8203;11476](https://github.com/better-auth/better-auth/pull/11476) [`4186e36`](https://github.com/better-auth/better-auth/commit/4186e36d1505f881585dea03353a4fc4ff864474) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Return CAPTCHA errors with the correct JSON Content-Type header. - [#&#8203;11469](https://github.com/better-auth/better-auth/pull/11469) [`8620aa9`](https://github.com/better-auth/better-auth/commit/8620aa94fdb9e158824cbba0d0fcf364143c56fe) Thanks [@&#8203;aryan1306](https://github.com/aryan1306)! - Return rate limit errors with a JSON Content-Type header. - [#&#8203;11491](https://github.com/better-auth/better-auth/pull/11491) [`55cb92e`](https://github.com/better-auth/better-auth/commit/55cb92e940e6ac26e746bbb06b92c445050cdb09) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Respect social provider `disableSignUp` when signing in with an ID token. - [#&#8203;11375](https://github.com/better-auth/better-auth/pull/11375) [`69defbc`](https://github.com/better-auth/better-auth/commit/69defbcabae430ca48e4ff1088c52a051566905e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Refresh the active organization after sign-in when a session hook selects the initial organization. - [#&#8203;11494](https://github.com/better-auth/better-auth/pull/11494) [`ac54bfd`](https://github.com/better-auth/better-auth/commit/ac54bfd67821a7607533af2059858b12bc54e26a) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Isolate OAuth state cookies and each OAuth Proxy payload with purpose-specific encryption keys. The `oAuthProxy` options and supported configuration remain unchanged. Upgrade all Better Auth nodes that handle the same cookie-backed OAuth or SAML relay-state flow together. Upgrade every OAuth Proxy participant, including production and preview or development deployments, in the same cutover. OAuth sign-in, account-linking, and cookie-backed SAML sign-in flows started before the upgrade must be restarted. Mixed old and new participants cannot exchange existing state or proxy payloads, and there is no fallback to the previous shared key. - [#&#8203;11494](https://github.com/better-auth/better-auth/pull/11494) [`ac54bfd`](https://github.com/better-auth/better-auth/commit/ac54bfd67821a7607533af2059858b12bc54e26a) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Magic Link verification now accepts only records issued for Magic Link. Magic Link records and database-backed OAuth or SAML state use separate verification identifier prefixes. Links and database-backed sign-ins started before the upgrade cannot complete; request new Magic Links and restart those sign-ins. Upgrade servers sharing verification storage together, and update `verification.storeIdentifier.overrides` rules for these flows to match the new `magic-link:` and `auth-state:` prefixes. The link token, callback state, endpoints, and public option types are unchanged. Upgrade installed Better Auth adapters, plugins, and integrations released with `better-auth` alongside it so participating packages use the same release version. - Updated dependencies \[[`35d7cd3`](https://github.com/better-auth/better-auth/commit/35d7cd389220f99cfc98ac699e6a42b48313a740), [`07bdf7e`](https://github.com/better-auth/better-auth/commit/07bdf7ecafb75222f7a24e0733cc3b056f84e9e8)]: - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.7 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.7 - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.7 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.7 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.7 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.7 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.7 ### [`v1.7.6`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#176) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.5...v1.7.6) ##### Patch Changes - [#&#8203;11325](https://github.com/better-auth/better-auth/pull/11325) [`af88385`](https://github.com/better-auth/better-auth/commit/af883851ae9c3b55c0d8240fabea3dc797cd5cd0) Thanks [@&#8203;Wadiou](https://github.com/Wadiou)! - Admin plugin `bannedUserMessage` can now be a function that receives the banned user, so sign-in errors can include details such as the ban reason. - [#&#8203;11268](https://github.com/better-auth/better-auth/pull/11268) [`2fa501c`](https://github.com/better-auth/better-auth/commit/2fa501cdf9e022d5f7241fabce1a67f1828d777b) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Support linking social accounts through the OAuth Proxy plugin. - [#&#8203;11366](https://github.com/better-auth/better-auth/pull/11366) [`d41e2ca`](https://github.com/better-auth/better-auth/commit/d41e2caf1a5bf09afc916087b739e0a5d00ab5c5) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Use targeted PRAGMA queries when a Kysely dialect cannot introspect Cloudflare D1. - [#&#8203;11016](https://github.com/better-auth/better-auth/pull/11016) [`3d0efa3`](https://github.com/better-auth/better-auth/commit/3d0efa308d575c4aa184d65deaf29f2626add8fd) Thanks [@&#8203;davbrito](https://github.com/davbrito)! - Support Vercel BotID checks on protected authentication routes in Vercel-hosted applications. - [#&#8203;11333](https://github.com/better-auth/better-auth/pull/11333) [`631ac29`](https://github.com/better-auth/better-auth/commit/631ac296a55ccecf51a7995e89a3e528a5f782da) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Preserve logical model identity when a custom model name matches another schema key. - [#&#8203;11324](https://github.com/better-auth/better-auth/pull/11324) [`8853419`](https://github.com/better-auth/better-auth/commit/88534192c1d0fd62b108b568243a134e6df61ab1) Thanks [@&#8203;XXMOHAMED012](https://github.com/XXMOHAMED012)! - Passwords longer than `maxPasswordLength` are now rejected with `PASSWORD_TOO_LONG` before hashing on sign-in (email, username, phone number), verify-password, change-password (`currentPassword`), delete-user, the two-factor endpoints that take a password, and admin create-user, matching what sign-up and password reset already did. - [#&#8203;11316](https://github.com/better-auth/better-auth/pull/11316) [`2ee1545`](https://github.com/better-auth/better-auth/commit/2ee1545f244d87e5edd6a003b48b90a5539f8afa) Thanks [@&#8203;Smidge](https://github.com/Smidge)! - Fix React hydration mismatches when a session or plugin auth query resolves before a streamed component hydrates. Preserve the server-rendered pending state during hydration, then update to the current client state without changing ordinary or computed plugin stores. - [#&#8203;11376](https://github.com/better-auth/better-auth/pull/11376) [`fc45d08`](https://github.com/better-auth/better-auth/commit/fc45d08b26ac8e433fc7899a12be392cc52735ba) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Prevent older auth-query responses from replacing newer results when requests overlap. - Updated dependencies \[[`41b7dc1`](https://github.com/better-auth/better-auth/commit/41b7dc15de41a8726422c392a4857d8764828891), [`d41e2ca`](https://github.com/better-auth/better-auth/commit/d41e2caf1a5bf09afc916087b739e0a5d00ab5c5), [`631ac29`](https://github.com/better-auth/better-auth/commit/631ac296a55ccecf51a7995e89a3e528a5f782da), [`2b13e01`](https://github.com/better-auth/better-auth/commit/2b13e011b4a4f8be4e4c39573b9e962cbabb2094)]: - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.6 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.6 - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.6 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.6 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.6 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.6 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.6 ### [`v1.7.5`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#175) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.4...v1.7.5) ##### Patch Changes - [#&#8203;11283](https://github.com/better-auth/better-auth/pull/11283) [`e56c45b`](https://github.com/better-auth/better-auth/commit/e56c45bddb8ca859175a47ab345b6c6dcc6da5d7) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Log Cloudflare Turnstile error codes and binding mismatches on the server so CAPTCHA verification failures can be diagnosed. - [#&#8203;11209](https://github.com/better-auth/better-auth/pull/11209) [`8d37cc3`](https://github.com/better-auth/better-auth/commit/8d37cc3b7732a04908b28d8abb7f010a286d6c43) Thanks [@&#8203;siam923](https://github.com/siam923)! - Remove the unused optional `better-sqlite3` peer dependency to prevent installation conflicts. - [#&#8203;11272](https://github.com/better-auth/better-auth/pull/11272) [`348fc26`](https://github.com/better-auth/better-auth/commit/348fc26c9904d800cd471947ba98abb5e8431b60) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Use MySQL's reported byte lengths when validating indexes on existing string columns. - [#&#8203;11203](https://github.com/better-auth/better-auth/pull/11203) [`cb627eb`](https://github.com/better-auth/better-auth/commit/cb627ebeb174d9a35ccc79018110bbc7a50a6fb8) Thanks [@&#8203;dshukertjr](https://github.com/dshukertjr)! - Add a `database.schemaName` option for direct PostgreSQL connections. When set, the adapter and the CLI qualify every statement with that schema, so `auth generate` writes a schema-qualified migration that creates the schema before its tables instead of relying on the connection's `search_path`. - [#&#8203;11270](https://github.com/better-auth/better-auth/pull/11270) [`133f6a2`](https://github.com/better-auth/better-auth/commit/133f6a27175ba21193742564d94aebfc9ec89920) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Prevent PostgreSQL migrations from treating tables in other schemas or views in the active schema as Better Auth tables. - Updated dependencies \[[`e18bc83`](https://github.com/better-auth/better-auth/commit/e18bc83172dca1804f0c5c3eff41d65e2849c557), [`cb627eb`](https://github.com/better-auth/better-auth/commit/cb627ebeb174d9a35ccc79018110bbc7a50a6fb8), [`dae97ed`](https://github.com/better-auth/better-auth/commit/dae97ed932a084a11faa96a2e1abf052fc85d2ea)]: - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.5 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.5 - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.5 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.5 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.5 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.5 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.5 ### [`v1.7.4`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#174) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.3...v1.7.4) ##### Patch Changes - [#&#8203;11205](https://github.com/better-auth/better-auth/pull/11205) [`3f890eb`](https://github.com/better-auth/better-auth/commit/3f890eb631e0278045b3b5b40d29b11bd9d4c332) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Support Vitest 5 in the testing utilities while retaining support for previously supported Vitest versions. - [#&#8203;11224](https://github.com/better-auth/better-auth/pull/11224) [`c1756a2`](https://github.com/better-auth/better-auth/commit/c1756a22745d4580425559b8800acc6e9a70e30f) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Add `experimental.instrumentation.enabled` to disable Better Auth OpenTelemetry span creation per auth instance. Instrumentation remains enabled by default and independent of usage reporting. - [#&#8203;11217](https://github.com/better-auth/better-auth/pull/11217) [`9b9638e`](https://github.com/better-auth/better-auth/commit/9b9638ec99057ade649280fa1447408238158f1c) Thanks [@&#8203;onmax](https://github.com/onmax)! - Allow `testUtils` auth helpers to accept additional session fields through the `session` option, including required fields without defaults and per-session overrides of configured defaults. - Updated dependencies \[[`3ff842a`](https://github.com/better-auth/better-auth/commit/3ff842ab7fdf60746b5d7bd8c27becd2d0a79c2e), [`b905bfe`](https://github.com/better-auth/better-auth/commit/b905bfe3d97de3c88fc31f7f2820531702df583e), [`c1756a2`](https://github.com/better-auth/better-auth/commit/c1756a22745d4580425559b8800acc6e9a70e30f)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.4 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.4 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.4 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.4 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.4 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.4 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.4 ### [`v1.7.3`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#173) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.2...v1.7.3) ##### Patch Changes - [#&#8203;11060](https://github.com/better-auth/better-auth/pull/11060) [`3660f06`](https://github.com/better-auth/better-auth/commit/3660f062d6e7a9b02e3fc8eee5b99482117ebba0) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Handle malformed custom-scheme callback URLs without excessive processing. - [#&#8203;11037](https://github.com/better-auth/better-auth/pull/11037) [`5bd7096`](https://github.com/better-auth/better-auth/commit/5bd709640a14e03972420bbfe778e3c732df80d5) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Prevent repeated TOTP enrollment from replacing an active authenticator and its backup codes. - [#&#8203;11120](https://github.com/better-auth/better-auth/pull/11120) [`7ec7146`](https://github.com/better-auth/better-auth/commit/7ec71461f1b3dcc0f9852ced5cc1ed7252ded4f2) Thanks [@&#8203;onmax](https://github.com/onmax)! - Prevent `getSession` from failing when cookie caching is disabled while clients still have cached session cookies. - [#&#8203;9908](https://github.com/better-auth/better-auth/pull/9908) [`76d311f`](https://github.com/better-auth/better-auth/commit/76d311f4b94799496ddfc0be7d03db1731f4b2a6) Thanks [@&#8203;harshil1712](https://github.com/harshil1712)! - Add Cloudflare as a built-in social provider, with support for client-secret authentication and PKCE clients without a secret. - [#&#8203;11188](https://github.com/better-auth/better-auth/pull/11188) [`c47b765`](https://github.com/better-auth/better-auth/commit/c47b76517d235d7cde07a1b4717f76b8f71a675f) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Normalize Auth0 domains without a potentially slow trailing-slash regular expression. - [#&#8203;11084](https://github.com/better-auth/better-auth/pull/11084) [`2d5c63d`](https://github.com/better-auth/better-auth/commit/2d5c63d512164d4e18c5e67b79a9c7d19b27b669) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Prevent duplicate session requests and hydration mismatches when using the Vue client with Nuxt `useFetch`. - [#&#8203;11147](https://github.com/better-auth/better-auth/pull/11147) [`a9d8c12`](https://github.com/better-auth/better-auth/commit/a9d8c12d14b99a2df35ae2fba98ad4e4bc512ad5) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Add `isPasswordCompromised` for checking passwords against Have I Been Pwned in custom server-side flows, while ignoring padded response entries with zero occurrences. - [#&#8203;10988](https://github.com/better-auth/better-auth/pull/10988) [`9fc7498`](https://github.com/better-auth/better-auth/commit/9fc749867592536b6e472381581cee8f00f6b59b) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Run callback hooks after proxied OAuth sign-ins and preserve server state when callback cookies are unavailable. The legacy `/oauth-proxy-callback` endpoint is deprecated and will be removed in the next minor release. - [#&#8203;11178](https://github.com/better-auth/better-auth/pull/11178) [`be0e007`](https://github.com/better-auth/better-auth/commit/be0e007e20ea310aa533acf49edfae34cfc797a9) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Report missing tables, missing columns, and required columns Better Auth never writes during initialization, with guidance for fixing them. Kysely checks the live database schema. Authentication requests await the same check and are rejected if the schema does not match. Validation is enabled by default, including in production. Set `advanced.database.validateSchema: false` to disable runtime validation. `auth migrate` refuses to apply changes when required unwritten columns need manual repair. - [#&#8203;11069](https://github.com/better-auth/better-auth/pull/11069) [`0bb0dbf`](https://github.com/better-auth/better-auth/commit/0bb0dbf6f38ab53a0c1f2fb639acd7bd602e2a24) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Improve dynamic organization role permission check performance. - [#&#8203;11153](https://github.com/better-auth/better-auth/pull/11153) [`2220ee7`](https://github.com/better-auth/better-auth/commit/2220ee726934de3aa128d5b4114391be8e9570cc) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Restore sign-in compatibility with 1.6 databases by identifying accounts with `(providerId, accountId)` and removing the `issuer` requirement introduced in 1.7.0. Upgrading from 1.6 no longer requires an account schema migration. Ambiguous account keys are rejected instead of selecting an arbitrary account. If you applied the 1.7.0 through 1.7.2 account schema, remove its issuer unique index before upgrading. For SQL databases, also make `issuer` nullable or remove the column so sign-ups and account linking can succeed. `auth migrate` does not perform this cleanup. Follow the [upgrade guide](https://www.better-auth.com/docs/guides/1-7-upgrade-guide) for database-specific steps. - [#&#8203;10978](https://github.com/better-auth/better-auth/pull/10978) [`5fe5bc2`](https://github.com/better-auth/better-auth/commit/5fe5bc21d1bf699655054192f4f833bcc33d0ba4) Thanks [@&#8203;BetterAndBetterII](https://github.com/BetterAndBetterII)! - Skip a generic OAuth provider when discovery fails instead of taking down the rest of the auth API. - [#&#8203;10963](https://github.com/better-auth/better-auth/pull/10963) [`74a7369`](https://github.com/better-auth/better-auth/commit/74a7369179cc6ab9fc2a9f13a67b33e8c00aa9ac) Thanks [@&#8203;thisismert](https://github.com/thisismert)! - Track email OTP sign-ins in the last login method plugin. - [#&#8203;11085](https://github.com/better-auth/better-auth/pull/11085) [`e16b40a`](https://github.com/better-auth/better-auth/commit/e16b40aeadde4203c883f121e7e5f7a2ec04d6b2) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Provide type-safe Nuxt `useFetch` integration for the Vue client's `useSession` hook. - [#&#8203;11066](https://github.com/better-auth/better-auth/pull/11066) [`c0444dc`](https://github.com/better-auth/better-auth/commit/c0444dc97928f46978c42083083d3b8642b89493) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Upgrade the packaged Zod dependency to 4.5. Generated OpenAPI schemas now mark required request fields consistently with runtime validation, including passkey registration responses. - Updated dependencies \[[`352d012`](https://github.com/better-auth/better-auth/commit/352d012bd54e613782bf4af22aae24443541c77c), [`76d311f`](https://github.com/better-auth/better-auth/commit/76d311f4b94799496ddfc0be7d03db1731f4b2a6), [`3e9e197`](https://github.com/better-auth/better-auth/commit/3e9e19746e609004da31bea3356c0059611d9ed4), [`157ec8d`](https://github.com/better-auth/better-auth/commit/157ec8d8799ddda642f2fe40120fc364660a8864), [`baa08f4`](https://github.com/better-auth/better-auth/commit/baa08f4ee674f5cc39624063847c89f1bea73186), [`9e36635`](https://github.com/better-auth/better-auth/commit/9e36635eb2fbf27d58c70d3361724335ba9a9951), [`be0e007`](https://github.com/better-auth/better-auth/commit/be0e007e20ea310aa533acf49edfae34cfc797a9), [`a2bae0c`](https://github.com/better-auth/better-auth/commit/a2bae0cad04ccc23c40555c77f86b0da1ba40ebc), [`1a1b7d5`](https://github.com/better-auth/better-auth/commit/1a1b7d56f51cb9ce6b06334b22fcbfa0d52be05a), [`2220ee7`](https://github.com/better-auth/better-auth/commit/2220ee726934de3aa128d5b4114391be8e9570cc)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.3 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.3 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.3 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.3 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.3 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.3 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.3 ### [`v1.7.2`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1725) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.7.1...v1.7.2) ##### Patch Changes - [#&#8203;12603](https://github.com/better-auth/better-auth/pull/12603) [`ad07edd`](https://github.com/better-auth/better-auth/commit/ad07edda1bcb4b5e40368989aa70801839a5926b) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - An error thrown from an endpoint no longer drops the `Access-Control-Expose-Headers` names that a hook or the endpoint set earlier. The names from both are kept. Before, the error's value replaced the list, so a browser client on another origin could lose access to headers a hook had exposed. - Updated dependencies \[[`69e14d8`](https://github.com/better-auth/better-auth/commit/69e14d89b856c95ea0998869c9cb8742d483a808), [`2a8750b`](https://github.com/better-auth/better-auth/commit/2a8750b285725a45ff58b0f43487afb796c854e9), [`75c0821`](https://github.com/better-auth/better-auth/commit/75c0821fa61c8ed0ea20f1027d5ad42d16e0daae)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.25 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.25 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.25 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.25 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.25 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.25 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.25 ### [`v1.7.1`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1719) ##### Patch Changes - [#&#8203;12323](https://github.com/better-auth/better-auth/pull/12323) [`a10121d`](https://github.com/better-auth/better-auth/commit/a10121d33cfccc525c79777319288d220940164b) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed the `active-sessions` list in secondary storage being stored without an expiry when the last remaining session had less than a second left. - [#&#8203;12335](https://github.com/better-auth/better-auth/pull/12335) [`c41f8ff`](https://github.com/better-auth/better-auth/commit/c41f8ff08755c4b493a82b89d5f4555a4998c122) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed `/admin/create-user` leaving a user without a credential account when the password was rejected (for example by the Have I Been Pwned plugin), which made retrying with the same email fail with `USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL`. - [#&#8203;12338](https://github.com/better-auth/better-auth/pull/12338) [`e786566`](https://github.com/better-auth/better-auth/commit/e786566dc553b3e21a9bdec0e7c4777dfb51bacf) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed `/admin/create-user` leaving a user without a credential account when creating that account failed, which made retrying with the same email fail with `USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL`. - [#&#8203;12336](https://github.com/better-auth/better-auth/pull/12336) [`ac778b7`](https://github.com/better-auth/better-auth/commit/ac778b70e9c35ce3fb13014fe7122f8faa1cd178) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Client actions now send the HTTP method passed in the second-argument fetch options, the same as when it is nested under `fetchOptions`. Passing a lowercase `get` or `HEAD` method no longer fails because a request body was attached. - [#&#8203;12246](https://github.com/better-auth/better-auth/pull/12246) [`5eb69d8`](https://github.com/better-auth/better-auth/commit/5eb69d8371aac809c81b16b08f82f05a448c5583) Thanks [@&#8203;AryaKesharwani](https://github.com/AryaKesharwani)! - `/device/token` now returns `invalid_client` instead of `invalid_grant` when `validateClient` rejects the `client_id`, matching `/device/code` and RFC 6749 section 5.2. - [#&#8203;12319](https://github.com/better-auth/better-auth/pull/12319) [`4b0327b`](https://github.com/better-auth/better-auth/commit/4b0327bfa363395fdba24bd51bdf18668e62657d) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Plugin schema overrides (`modelName` and field names) no longer leak into other instances of the same plugin in one process. - [#&#8203;12320](https://github.com/better-auth/better-auth/pull/12320) [`2d5ef64`](https://github.com/better-auth/better-auth/commit/2d5ef64daee607af5ff2175262dc387a767f7e35) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fix OAuth sign-in failing with `state_mismatch` during the daylight saving time fall-back hour on servers that do not run in UTC. The stored OAuth state is no longer created already expired. - [#&#8203;12337](https://github.com/better-auth/better-auth/pull/12337) [`5f0a0fc`](https://github.com/better-auth/better-auth/commit/5f0a0fcef3b15ea4a657a08a46b71df96ada77cc) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - The OpenAPI schema now documents query parameters for endpoints whose query schema is wrapped in `z.optional()`, such as `/get-session` and Stripe's `/subscription/list`. - [#&#8203;12332](https://github.com/better-auth/better-auth/pull/12332) [`89e74b8`](https://github.com/better-auth/better-auth/commit/89e74b81e554e2a4edc5bf925adad59dfdace734) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed `organization.getInvitation` returning a missing, expired, or non-pending invitation error without a `code`. It now returns `INVITATION_NOT_FOUND` like the other invitation endpoints. - [#&#8203;12318](https://github.com/better-auth/better-auth/pull/12318) [`525bd7a`](https://github.com/better-auth/better-auth/commit/525bd7a7d866012c88f0fff2c488d352a6eb8f2e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fix `listMembers` failing with a syntax error on PostgreSQL and MSSQL when the filter or offset matches no members. - [#&#8203;12366](https://github.com/better-auth/better-auth/pull/12366) [`23a60a3`](https://github.com/better-auth/better-auth/commit/23a60a31f42ab9f78f3db976d232df37f8414e9d) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed SIWE sign-in leaving a user behind when creating its wallet address or account failed, which blocked that wallet from signing in again. - [#&#8203;12340](https://github.com/better-auth/better-auth/pull/12340) [`29e45f8`](https://github.com/better-auth/better-auth/commit/29e45f84d5946a002330b56dc07600a156984a2c) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Fixed social sign-up hanging on single-connection databases when the username plugin is enabled and the provider profile maps a username. - Updated dependencies \[[`8412a5f`](https://github.com/better-auth/better-auth/commit/8412a5f43b724648b3faaa452bec0a6ba8e01765), [`34ee100`](https://github.com/better-auth/better-auth/commit/34ee1008d8269d32b875e2743b9805090863f2f4)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.19 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.19 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.19 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.19 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.19 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.19 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.19 ### [`v1.7.0`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#170) ##### Minor Changes - [#&#8203;8733](https://github.com/better-auth/better-auth/pull/8733) [`4e8e4c7`](https://github.com/better-auth/better-auth/commit/4e8e4c7fc5fb2723144cbf41c4a1bfa28de8d671) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Add `hydrateSession` to seed the client with a server-fetched session so `useSession` returns data on the first render. - [#&#8203;9930](https://github.com/better-auth/better-auth/pull/9930) [`0cbaf81`](https://github.com/better-auth/better-auth/commit/0cbaf81bed9dec4c56880ee78a532262386e1ec5) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Anonymous account linking now works after social and generic OAuth sign-in in Expo and other in-app browsers, where the OAuth callback returns without the session cookie. `onLinkAccount` fires and the anonymous user is migrated; before, it was silently skipped. Plugins can now carry server-trusted data across an OAuth redirect with the new `addOAuthServerContext` API, read back on the callback via `getOAuthState().serverContext`. Unlike `additionalData`, it cannot be set from the request body, so it is the right place for values the server must trust. For `@better-auth/oauth-provider`, the post-login authorization query now travels through that server-only channel, so it can no longer be injected through `additionalData`. - [#&#8203;10004](https://github.com/better-auth/better-auth/pull/10004) [`b36c38f`](https://github.com/better-auth/better-auth/commit/b36c38f9842d3416689340552989449a32007819) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - The captcha plugin now requires endpoint entries to match full auth paths unless they use wildcard patterns. This prevents requests like `/sign-in//email` from bypassing captcha while preserving trailing-slash matches like `/sign-in/email/`. To protect multiple routes, replace partial paths like `/sign-in` with explicit wildcards such as `/sign-in/*` or `/sign-in/**`. - [#&#8203;10746](https://github.com/better-auth/better-auth/pull/10746) [`6782647`](https://github.com/better-auth/better-auth/commit/6782647d7c2d248246f9ef3980e656725c29ce64) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - OAuth device grants now use `oauthDeviceAuthorization()` alongside `oauthProvider()` or `mcp()`. This single integration replaces both the standalone `deviceCodeGrant()` plugin and the shared-grant configuration. Standalone Device Authorization no longer accepts or stores RFC 8707 resources, and `onDeviceAuthRequest` receives only `clientId` and `scope`. The OAuth integration rejects resource indicators that are not absolute, fragment-free URIs. The OAuth integration replaces the optional `resource` column with `oauthClientId` and `resources`. Regenerate and apply the schema when using it. Before upgrading from an earlier 1.7 prerelease, let pending OAuth device codes expire or delete them because they cannot be exchanged through the new integration. - [#&#8203;10402](https://github.com/better-auth/better-auth/pull/10402) [`763a267`](https://github.com/better-auth/better-auth/commit/763a2671c5372d88c291881977c8a1c2e29034b1) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Plugin database schemas can now define named or generated table-level indexes across multiple fields. SQL migrations and generated Drizzle or Prisma schemas resolve configured table and column names consistently, while the MongoDB adapter creates the same indexes before the first index-enforcing write. - [#&#8203;9766](https://github.com/better-auth/better-auth/pull/9766) [`bf39cbf`](https://github.com/better-auth/better-auth/commit/bf39cbf13f3b934f728cde72b1e7ebdc4c85f641) Thanks [@&#8203;GautamBytes](https://github.com/GautamBytes)! - Add a server-only `auth.api.consumePhoneNumberOTP` API for custom phone OTP flows that need to verify and consume a code without creating or updating users or sessions. - [#&#8203;10330](https://github.com/better-auth/better-auth/pull/10330) [`081d3c3`](https://github.com/better-auth/better-auth/commit/081d3c379c720926295067d878c421b5e8684c78) Thanks [@&#8203;ping-maxwell](https://github.com/ping-maxwell)! - Allow the username plugin's separate `displayUsername` field to be omitted by setting `displayUsername: false` on both the server and client plugins. - [#&#8203;10059](https://github.com/better-auth/better-auth/pull/10059) [`49b5cf6`](https://github.com/better-auth/better-auth/commit/49b5cf650e1264ecc4c917ca193ea05c3b58a3b9) Thanks [@&#8203;GautamBytes](https://github.com/GautamBytes)! - Device Authorization now creates unique database indexes for `deviceCode` and `userCode`, so each generated code must be unique in its column. Existing installations on every adapter must resolve duplicate 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 before running it. Generated codes are limited to 191 characters. Issuance makes up to 3 attempts to overcome unique-key collisions, then returns `server_error` if it cannot create a unique `deviceCode` and `userCode`. Default-generated user codes accept case changes and readability separators during verification, approval, and denial; custom codes outside the default alphabet are matched exactly. The `/device` limiter allows 5 requests over a window equal to the configured code lifetime, while `/device/token` polling keeps its separate interval behavior. - [#&#8203;9645](https://github.com/better-auth/better-auth/pull/9645) [`e014029`](https://github.com/better-auth/better-auth/commit/e0140297a59ddb59cccbcb4ba46c513de8cb86a7) Thanks [@&#8203;ping-maxwell](https://github.com/ping-maxwell)! - Harden the Electron OAuth flow and tighten custom-scheme trusted-origin matching. The Electron sign-in flow now mandates PKCE S256. Plain PKCE is rejected: the `code_challenge_method` parameter is gone and every authorization code is verified by hashing the verifier with SHA-256. The server no longer trusts an `electron-origin` header to set the request Origin. The Electron client now sends a real `Origin` (for example `myapp:/`), so upgrade the `@better-auth/electron` client and server together and make sure your app's scheme is in `trustedOrigins`. The unused `disableOriginOverride` option is removed. Custom-scheme entries in `trustedOrigins` now match by scheme and authority instead of string prefix. A host-less entry such as `myapp://` or `exp://` still trusts every host of that scheme, but a host-bearing entry such as `myapp://callback` matches that host exactly, so it is no longer satisfied by `myapp://callback.attacker.tld`. - [#&#8203;9948](https://github.com/better-auth/better-auth/pull/9948) [`3d04fab`](https://github.com/better-auth/better-auth/commit/3d04fababbf3efd4c46a4012f46ed9397715c2e3) Thanks [@&#8203;yordis](https://github.com/yordis)! - feat(generic-oauth): add `refreshTokenParams` config to forward extra params on token refresh Multi-tenant OIDC providers (Zitadel multi-org, Auth0 with `audience`) need to send extra body params on the refresh call to rescope tokens without a full authorization redirect. The generic-oauth plugin now accepts a `refreshTokenParams` option (object or sync/async function) that is merged into the refresh request body, with `grant_type` and `refresh_token` protected from override. The function form receives request metadata for the request that triggered the refresh, so request-scoped data (headers, cookies) is available without out-of-band state like AsyncLocalStorage. `UpstreamProvider.refreshAccessToken` now accepts an optional second `ctx` argument; the change is backwards compatible because existing implementations that take only `refreshToken` remain valid. See [#&#8203;7554](https://github.com/better-auth/better-auth/issues/7554). - [#&#8203;9069](https://github.com/better-auth/better-auth/pull/9069) [`c7d2253`](https://github.com/better-auth/better-auth/commit/c7d22539ec4f7322d9625ae2953d397c3863d097) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Rewrite the generic OAuth plugin as a first-class social provider with OAuth 2.1 security defaults. Providers now use `signIn.social` + `callback/:id` instead of dedicated plugin endpoints, with PKCE required by default (OAuth 2.1), RFC 9207 issuer validation, OIDC auto-discovery with `openid` scope injection, and typed provider IDs. **Breaking changes:** - `signIn.oauth2({ providerId })` replaced by `signIn.social({ provider })` - `oauth2.link()` replaced by `linkSocial()` - Callback URL changed from `/api/auth/oauth2/callback/:id` to `/api/auth/callback/:id` - `genericOAuthClient()` removed; generic OAuth providers now use the standard social client APIs - `pkce` defaults to `true` (was `false`); set `pkce: false` for providers that reject PKCE - `authorizationUrlParams` and `tokenUrlParams` only accept `Record<string, string>` - `issuer` and `requireIssuerValidation` config fields removed; issuer validation is automatic via OIDC discovery - `mapProfileToUser` profile typed as `OAuth2UserInfo & Record<string, unknown>` - [#&#8203;9966](https://github.com/better-auth/better-auth/pull/9966) [`ec8a38c`](https://github.com/better-auth/better-auth/commit/ec8a38c08f5cfe2d922be0f8a49f2d0fa84de799) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - genericOAuth providers configured with a `discoveryUrl` now verify the provider's `id_token` against its published JWKS (signature, issuer, audience, and advertised algorithms) and bind it to the authorization request with a server-generated OIDC `nonce`. A sign-in whose `id_token` fails verification, or does not echo the expected `nonce`, is rejected. Set `disableIdTokenNonceBinding: true` on a provider that does not return the `nonce` claim in the authorization-code flow. These providers also accept client-submitted id\_token sign-in through `signIn.social({ idToken })`, which previously returned `ID_TOKEN_NOT_SUPPORTED`. Providers configured with explicit endpoints instead of `discoveryUrl` are unchanged. - [#&#8203;9368](https://github.com/better-auth/better-auth/pull/9368) [`430c895`](https://github.com/better-auth/better-auth/commit/430c89549060ef6bd477ed2510650b9e49bba560) Thanks [@&#8203;GautamBytes](https://github.com/GautamBytes)! - Generic OAuth users can now sign out from the configured OpenID provider when they call `authClient.signOut()`. When a provider exposes a discovered or configured logout endpoint, Better Auth redirects to it and includes the stored `id_token_hint` when available. Pass `callbackURL` or configure `postLogoutRedirectURI` for the return flow, with optional `state`, or set `disableRedirect` to handle the returned `url` yourself. When multiple linked providers support logout, Better Auth selects the most recently updated account. Set `disableProviderLogout: true` to keep sign-out local. - [#&#8203;9431](https://github.com/better-auth/better-auth/pull/9431) [`523f95c`](https://github.com/better-auth/better-auth/commit/523f95c10db24b790bbd75fe85c86c34d3465267) Thanks [@&#8203;pi0](https://github.com/pi0)! - feat: make `Auth` instance fetchable - [#&#8203;10577](https://github.com/better-auth/better-auth/pull/10577) [`5c45abc`](https://github.com/better-auth/better-auth/commit/5c45abcd2094d4a430cc84af6f9719fa0515ad71) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - MCP clients that hit a scope wall now learn exactly which scopes to ask for. Missing protected scopes produce a `403` with an RFC 6750 `insufficient_scope` `WWW-Authenticate` challenge that names every missing scope. Clients can union those scopes into one authorization request instead of opening one browser redirect per scope. - Configure protected scopes with `requiredScopes` through `RequireMcpAuthOptions` or the matching `createMcpProtectedRequestHandler` verifier option. Exact membership remains the default; `isScopeSatisfied` can define hierarchical policies. - Use `createInsufficientScopeError` when an operation determines its required scopes dynamically. `createResourceServerChallenge` converts that signal and recognized token failures into safe RFC 6750 challenges. - Use `challengeScopes` only as the unauthenticated challenge hint. Handler-produced responses, ordinary permission denials, configuration failures, and unrelated thrown values keep their original status and identity. - [#&#8203;10403](https://github.com/better-auth/better-auth/pull/10403) [`dbd302e`](https://github.com/better-auth/better-auth/commit/dbd302e422c66620cde391f6a80ab90ee34182f9) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Scope account identity by trusted issuer instead of provider configuration. Accounts now use the unique `(issuer, accountId)` key, so aliases for one OpenID Connect issuer deduplicate one external identity while equal subjects from different issuers remain separate. This identity deduplication does not introduce independent grant or provider lifecycle records for aliases. This release requires `Account.issuer` but preserves `Account.accountId` as the provider-assigned account identifier. Account-specific APIs select the local `Account.id` through the `accountId` request property; token and provider-profile APIs can instead select the signed account cookie with `useAccountCookie: true`. Credential accounts use `local:credential` and the linked user's stable `id` as their provider identity. OAuth provider identity now comes from raw verified profiles. OpenID Connect discovery uses `sub`, plain OAuth uses `id`, and providers can declare `accountSubject` for another immutable field; Better Auth no longer switches between `sub` and `id` at runtime. `getUserInfo().user` no longer carries provider identity, and `mapProfileToUser` cannot return `id`. Read the selected identity from `accountInfo.account.accountId` instead of `accountInfo.user.id`. The generic `microsoftEntraId` helper now requires a concrete tenant GUID; use the built-in Microsoft provider for multi-tenant authorities. SSO account subjects are now protocol-defined. OIDC uses the verified `sub` claim, and SAML uses the signed `NameID`; `mapping.id` is removed from both configurations. A manual SAML configuration without metadata XML must set `idpMetadata.entityID`, because `samlConfig.issuer` identifies the service provider and no longer acts as the IdP identity. Apply the reviewed account-identity backfill in the Better Auth 1.7 upgrade guide before deploying. The generated schema migration cannot assign trusted issuers or resolve existing identity collisions automatically. - [#&#8203;10359](https://github.com/better-auth/better-auth/pull/10359) [`8784c1c`](https://github.com/better-auth/better-auth/commit/8784c1c1f4301acf96d980e5bf81ff56435e2545) Thanks [@&#8203;ping-maxwell](https://github.com/ping-maxwell)! - Database joins have moved out of `experimental` into a stable option at `advanced.database.joins` (default: `false`). If you previously set `experimental: { joins: true }`, update your config to: ```ts advanced: { database: { joins: true, }, } ``` Adapters that support native joins use them when enabled. If an adapter cannot return joined data for a query, Better Auth falls back to additional queries and combines the results. Drizzle and Prisma users should ensure their schema includes the required relations (`npx auth@latest generate`). - [#&#8203;9992](https://github.com/better-auth/better-auth/pull/9992) [`e53582c`](https://github.com/better-auth/better-auth/commit/e53582ce55a0ddbca62f52efeb3459523816f222) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - The MCP plugin moves out of `better-auth` into its own package, `@better-auth/mcp`, built on `@better-auth/oauth-provider`. Import the authorization plugin and protected-request helpers from the package root. The in-core MCP client (`createMcpAuthClient` and its adapters) is removed; MCP protocol and transport clients come from the official version 2 `@modelcontextprotocol/client` and `@modelcontextprotocol/server` packages. The OAuth endpoints move from `/mcp/*` to `/oauth2/*`, with discovery at `/.well-known/oauth-authorization-server` and protected resource metadata at `/.well-known/oauth-protected-resource`. Discovery-based MCP clients pick up the new locations on their own. The shared-auth route helper is renamed from `withMcpAuth` to `requireMcpAuth`. The standalone protected-resource factory is renamed from `mcpHandler` to `createMcpProtectedRequestHandler`; pass one flat `McpProtectedRequestHandlerOptions` object with `issuer`, a single `audience`, optional `jwtVerifyOptions`, token-verification fields, and challenge fields. Its callback receives `accessTokenClaims`. `requireMcpAuth` verifies the access token against the published JWKS, validates DPoP proofs for DPoP-bound tokens, and passes the verified access-token claims to your handler. `createInsufficientScopeError` now validates a custom description against the RFC 6750 `error_description` character set when the error is constructed. Invalid descriptions throw `TypeError("invalid error_description")` before an error can reach resource-challenge serialization. MCP 2026-07-28 uses a stateless request and response transport. Serve MCP routes with version 2 of `@modelcontextprotocol/server`, configure `createMcpHandler` with `legacy: "reject"`, wrap it with `requireMcpAuth`, and export only `POST`. Remove MCP-route `GET` and `DELETE` exports and session-store options such as `redisUrl`. OAuth clients, consent, authorization codes, refresh tokens, and security records remain durable authorization state. To migrate, install `@better-auth/mcp`, `@better-auth/cimd`, and the official version 2 MCP client or server package needed by your application; add the `jwt()` plugin, which is now required for token signing; and move options that were nested under `oidcConfig` to flat options on `mcp({ ... })`. The database models change: `oauthApplication` becomes `oauthClient`, with new `oauthRefreshToken` and `oauthClientAssertion` tables. Regenerate or migrate your schema with `npx auth migrate` or `npx auth generate`. - [#&#8203;10204](https://github.com/better-auth/better-auth/pull/10204) [`0683a5f`](https://github.com/better-auth/better-auth/commit/0683a5f36befb45ade3866c7f8057791eadeee59) Thanks [@&#8203;GautamBytes](https://github.com/GautamBytes)! - Microsoft sign-in now identifies Entra accounts with the stable `oid` claim in both the built-in `microsoft` provider and the Generic OAuth `microsoftEntraId` helper. Tokens without a valid `oid` are rejected, and the Generic OAuth helper refuses to initialize unless Microsoft discovery provides ID-token verification metadata. Existing Microsoft account rows created from `sub` must be migrated before upgrading. - [#&#8203;9305](https://github.com/better-auth/better-auth/pull/9305) [`e7eb45b`](https://github.com/better-auth/better-auth/commit/e7eb45b065903f5fccddae491696cb069814a3c8) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - feat(oauth): per-request `additionalParams` and `loginHint` parity across `signIn.social`, `linkSocial`, and `signIn.sso` Unified escape hatch for customizing the provider authorization URL on a per-request basis. Previously, dynamic parameters like Google's `access_type=offline` / `prompt=consent`, Cognito's `identity_provider=Google`, or Microsoft's `domain_hint` could only be set as static server configuration. ##### New capabilities - `signIn.social`, `linkSocial`, and `signIn.sso` accept `additionalParams: Record<string, string>`. Values are appended to the authorization URL as query parameters. - `linkSocial` also accepts `loginHint`, matching the surface of `signIn.social` and `signIn.sso`. - `OAuthProvider.createAuthorizationURL` gains `additionalParams` in its input contract; every built-in provider forwards it to the shared helper. - Generic-OAuth providers merge call-time `additionalParams` with the config-level `authorizationUrlParams`; call-time wins on key collision. - Cognito exposes a typed `identityProvider?: string` config option that maps to the `identity_provider` query parameter, avoiding magic strings. ##### Security - The shared `createAuthorizationURL` helper silently drops any caller-supplied key in `RESERVED_AUTHORIZATION_PARAMS` (`state`, `client_id`, `redirect_uri`, `response_type`, `code_challenge`, `code_challenge_method`, `nonce`, `scope`). The request-body Zod schema rejects the same keys with 400, so misuse is visible at the edge rather than silently overriding security-critical parameters. `nonce` is reserved so a caller cannot replace the OIDC nonce Better Auth generates when binding a discovery provider's `id_token` to the authorization request. - Providers that use non-standard client identifiers (`wechat` → `appid`, `tiktok` → `client_key`) additionally filter those keys so a caller cannot swap the configured OAuth app. - Provider protocol constants that are required for the integration to function (`atlassian` → `audience`, `notion` → `owner`) are merged last so caller-supplied `additionalParams` cannot override them. Configured defaults that represent operator intent (e.g. Google `include_granted_scopes`, Cognito `identityProvider`) remain caller-overridable. - `signIn.sso` rejects `additionalParams` with 400 when the resolved provider is SAML; the SAML AuthnRequest is signed and cannot carry caller-supplied query parameters, so silently dropping them would mislead integrators. ##### OpenAPI - Added `ZodRecord` handling to the OpenAPI generator so `z.record()` fields emit `type: object` with typed `additionalProperties`. Incidentally fixes a long-standing bug where `additionalData` was rendered as `type: string`. ##### Refactors - `discord`, `roblox`, `zoom`, and `slack` providers now delegate to the shared `createAuthorizationURL` helper and inherit its RFC behavior and reserved-key guard. - `tiktok` and `wechat` keep their manual URL construction (non-standard OAuth2 parameter names and URL fragment requirements) but thread `additionalParams` with the same reserved-key filter. Closes [#&#8203;2351](https://github.com/better-auth/better-auth/issues/2351). Closes [#&#8203;5441](https://github.com/better-auth/better-auth/issues/5441). Closes [#&#8203;5592](https://github.com/better-auth/better-auth/issues/5592). Closes [#&#8203;5604](https://github.com/better-auth/better-auth/issues/5604). Supersedes [#&#8203;4992](https://github.com/better-auth/better-auth/issues/4992) and [#&#8203;5443](https://github.com/better-auth/better-auth/issues/5443). - [#&#8203;10127](https://github.com/better-auth/better-auth/pull/10127) [`7c7313c`](https://github.com/better-auth/better-auth/commit/7c7313c8189baabd11a2ecb681bd2b16eb40fa4d) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - OAuth sign-in, account linking, callback, and proxy flows now build `redirect_uri` from the current request base URL when `baseURL.allowedHosts` is configured. Built-in social providers and generic OAuth providers now use the resolved request host for redirects in multi-host deployments. Custom `OAuthProvider` implementations can omit `callbackPath` when using the shared `/callback/<provider-id>` route. Set `callbackPath` only for custom callback routes. - [#&#8203;10039](https://github.com/better-auth/better-auth/pull/10039) [`aedcb97`](https://github.com/better-auth/better-auth/commit/aedcb974f055c3514fe0464dc53d71d45a8a1725) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - feat(oauth-provider)!: DPoP-bound access tokens (RFC 9449) OAuth provider integrations can issue and verify DPoP sender-constrained tokens. Clients request them with `dpop_bound_access_tokens` at registration, `dpop_jkt` on the authorization request, or by targeting a resource configured with `dpopBoundAccessTokensRequired`. Issued tokens carry `cnf.jkt`, return `token_type: "DPoP"`, and stay bound through refresh-token rotation, introspection, and userinfo. Resource servers verify DPoP requests with `verifyAccessTokenRequest`, which checks the `Authorization: DPoP` scheme, the proof, the request target, the access-token hash, and proof replay. The MCP package advertises DPoP in protected resource metadata and verifies DPoP-bound requests. Proof replay is rejected through the database-backed verification store, so anti-replay holds across instances. `verifyAccessTokenRequest` and `requireMcpAuth` use that store by default; build one with `createDpopReplayStore(internalAdapter)` or pass a custom `dpop.replayStore`. This needs database-backed verification storage: a secondary-storage-only deployment rejects DPoP requests rather than skipping replay protection. Breaking: the raw-token verifier `verifyAccessToken` is renamed to `verifyBearerToken`, both in `better-auth/oauth2` and as the `oauthProviderResourceClient` action, and it rejects DPoP-bound tokens. Use `verifyAccessTokenRequest` on any endpoint that may receive them. The resource-request input type is renamed from `AccessTokenRequestInput` to `ResourceRequestInput`, and the DPoP algorithm option is `signingAlgorithms` everywhere. Run a schema migration for the DPoP token-binding fields: the `confirmation` column on the access-token and refresh-token tables. DPoP-bound clients also gain `dpopBoundAccessTokens` and resources `dpopBoundAccessTokensRequired`. No dedicated replay table is added; proof replay reuses the verification store. - [#&#8203;9828](https://github.com/better-auth/better-auth/pull/9828) [`4f53b61`](https://github.com/better-auth/better-auth/commit/4f53b61f49b470a40ccab18fe1fe4d80f225905f) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Verify social-provider id\_tokens with a single shared verifier. Client-submitted id\_token sign-in (`signIn.social({ idToken })` and account linking) is verified by one function instead of a per-provider `verifyIdToken` method. Each provider declares an `idToken` config with a JWKS source, issuer, and audience, and the core verifier runs the signature, issuer, audience, and nonce checks. A provider that declares no config rejects the client id\_token path. PayPal previously accepted any decodable id\_token without verifying its signature. PayPal derives identity from the access token, so it now declares no `idToken` config, and the client id\_token path returns `ID_TOKEN_NOT_SUPPORTED`. PayPal sign-in through the redirect flow is unchanged. Custom providers that implement `UpstreamProvider` directly replace the removed `verifyIdToken` method with an `idToken` config: ```ts idToken: { jwks: createRemoteJWKSet(new URL("https://issuer.example/.well-known/jwks.json")), issuer: "https://issuer.example", audience: clientId, }, ``` For verification that cannot use a local JWKS, pass `idToken: { verify: async (token, nonce) => boolean }`. The `verifyIdToken` and `disableIdTokenSignIn` provider options are unchanged. - [#&#8203;9079](https://github.com/better-auth/better-auth/pull/9079) [`6f2948e`](https://github.com/better-auth/better-auth/commit/6f2948e87bb5fa14bd2174a91f7143e1eced1b87) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - feat(oauth-provider): compute `at_hash` in ID tokens per OIDC Core §3.1.3.6 ID tokens issued alongside an access token now include the `at_hash` claim, which cryptographically binds the two tokens to prevent token substitution attacks. The hash algorithm is selected based on the actual signing key's algorithm (EdDSA/Ed25519 uses SHA-512, RS/ES/PS384 uses SHA-384, RS/ES/PS512 uses SHA-512, all others use SHA-256). A new `resolveSigningKey()` export is available from `better-auth/plugins` to resolve the current JWKS signing key (including its algorithm). When using a custom `jwt.sign` callback, the signed ID token's header is validated against the declared algorithm to prevent `at_hash` mismatches. - [#&#8203;10135](https://github.com/better-auth/better-auth/pull/10135) [`f68044d`](https://github.com/better-auth/better-auth/commit/f68044dcfbd9fb83763249ed9509cfacbcce47be) Thanks [@&#8203;brentmitchell25](https://github.com/brentmitchell25)! - Registered OAuth clients can now use the RFC 8628 device flow to obtain OAuth access tokens. Add `oauthDeviceAuthorization()` alongside `oauthProvider()` or `mcp()`, request a code at `/device/code`, and exchange it at `/oauth2/token` after the user approves it. OAuth and OpenID discovery advertise the `device_authorization_endpoint`. Device authorization requests can bind RFC 8707 resource indicators. `GET /device` returns the requested client, scopes, and resources to the authenticated user who owns the request. Token requests can reuse or narrow the approved resources, but cannot add new ones. Existing first-party device clients continue to receive Better Auth session tokens from `/device/token`. Enabling `oauthDeviceAuthorization()` adds nullable `oauthClientId` and `resources` fields to `deviceCode`. Regenerate and apply the database schema after adding the integration. Confidential clients authenticate at `/device/code` with their registered method, while public clients send `client_id`. Empty `client_id`, `scope`, `user_id`, and authentication values are treated as omitted; multiple non-empty values for any of these parameters return `invalid_request`, while multiple `resource` values remain supported. Unknown OAuth client IDs enter the standalone device flow only when `oauthDeviceAuthorization({ validateClient })` accepts them. - [#&#8203;9929](https://github.com/better-auth/better-auth/pull/9929) [`91f235f`](https://github.com/better-auth/better-auth/commit/91f235f8604cd432749adf18c7bd7d658aa1519b) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Add `requireEmailVerification` to OAuth provider options, for built-in social providers and the Generic OAuth plugin. When a provider reports an unverified email, the user and account are still created or linked, but no session is issued: the OAuth callback redirects with `?error=email_not_verified`, and ID token and One Tap sign-in return `403` `EMAIL_NOT_VERIFIED`. Verification emails follow the existing `emailVerification.sendOnSignUp` / `sendOnSignIn` settings. It is opt-in per provider and does not inherit `emailAndPassword.requireEmailVerification`, so existing social logins keep working. The gate checks the local user's verification state, so a user verified through another method keeps access. Only enable it for providers that report a trustworthy `email_verified` signal. - [#&#8203;9648](https://github.com/better-auth/better-auth/pull/9648) [`d2a79ba`](https://github.com/better-auth/better-auth/commit/d2a79bae79b88e2b28cb678f5eefd9759239b627) Thanks [@&#8203;brentmitchell25](https://github.com/brentmitchell25)! - OAuth provider now models protected resources explicitly. Configure them with `resources` or create them through the `oauthResource` admin API. Each resource can define token TTLs, allowed scopes, custom JWT claims, and JWT signing pins. `validAudiences` is removed. Move each existing resource identifier into `resources`; link clients that should be limited to specific resources through `oauthClientResource` or Dynamic Client Registration `resources`. Access-token issuance now applies resource policy to the requested RFC 8707 `resource` values. The OAuth provider narrows scopes to resource allowlists, uses the shortest configured TTL, strips reserved RFC 9068 claim names from custom claims, emits `jti`, and keeps repeated `resource` form parameters. Refresh-token TTLs now use the shortest applicable lifetime. Deployments with a per-resource `refreshTokenTtl` longer than `refreshTokenExpiresIn` will see refresh tokens expire at the provider default instead of the longer resource value. JWT signing can now honor per-resource pins. `signJWT()` accepts `signingKeyId` and `signingAlgorithm`; JWKS adapters expose `getKeyById()` and `getLatestKeyByAlg()`. The `jwks` table adds nullable `alg` and `crv` columns, and `keyPairConfigs` can provision multiple algorithms in one keyring. After upgrading, run `npx auth generate` and apply the migration before deploying. The migration adds `oauthResource`, `oauthClientResource`, and the new `jwks` columns. Without it, resources using `signingAlgorithm` cannot find matching keys. Resource servers should publish RFC 9728 protected-resource metadata at their own origin. The OAuth provider exposes challenge helpers that point clients at that metadata. `@better-auth/mcp` now requires an explicit `resource` option. The plugin stores that identifier as an OAuth resource, publishes RFC 9728 protected-resource metadata for it, and binds issued access tokens to that resource. Existing `mcp({ loginPage, consentPage })` setups should add a protected MCP resource identifier, for example `resource: "https://api.example.com/mcp"`. - [#&#8203;10397](https://github.com/better-auth/better-auth/pull/10397) [`bb6c102`](https://github.com/better-auth/better-auth/commit/bb6c1021e8f6200e60ff852cbd95fb6841a0ec4b) Thanks [@&#8203;ping-maxwell](https://github.com/ping-maxwell)! - Add `organization.getOrganization()` to fetch organization metadata without members or invitations. - [#&#8203;8931](https://github.com/better-auth/better-auth/pull/8931) [`34558bc`](https://github.com/better-auth/better-auth/commit/34558bc52b0e043021a1072f78de5f5439ae1734) Thanks [@&#8203;GautamBytes](https://github.com/GautamBytes)! - Add opt-in JWKS-backed asymmetric JWT support for `session_data` cookie cache tokens, so services can verify cookie-cache JWTs with public keys instead of shared secrets. Enable it with `jwt({ sessionCookieCache: true })` alongside `session.cookieCache.strategy = "jwt"`. - [#&#8203;8977](https://github.com/better-auth/better-auth/pull/8977) [`954b664`](https://github.com/better-auth/better-auth/commit/954b664f4f251f8dd028451dab3ab43067dbf890) Thanks [@&#8203;ruban-s](https://github.com/ruban-s)! - allow passing `userId` and `organizationId` to the `listUserTeams` API. `userId` lets callers list teams for another member of an organization (gated behind the `member:update` permission). `organizationId` scopes the result to a specific organization without needing to switch the session's active organization, matching the pattern used by `addTeamMember`/`removeTeamMember`. - [#&#8203;9969](https://github.com/better-auth/better-auth/pull/9969) [`76a3342`](https://github.com/better-auth/better-auth/commit/76a33429fc2a3edcc85307bf81b9d92a95f9de6c) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Signing out with `secondaryStorage` and `session.preserveSessionInDatabase` now runs your configured `session.delete` hooks and marks the preserved session row as ended. OAuth Provider access and refresh tokens bound to that session are revoked and back-channel logout is dispatched on sign-out. Previously these hooks were skipped in this setup, so the tokens stayed valid until they expired. - [#&#8203;9657](https://github.com/better-auth/better-auth/pull/9657) [`1e5b808`](https://github.com/better-auth/better-auth/commit/1e5b80847208cf839c9d45363ca19b8eab41c68a) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Harden `private_key_jwt` and token endpoint client authentication, and add the helpers that make the fix structural. `@better-auth/core/oauth2` now exposes `encodeBasicCredentials` and `decodeBasicCredentials`, a round-trip-tested pair that follows RFC 6749 §2.3.1 (`application/x-www-form-urlencoded` each value, split on the first `:` only). The decoder accepts the scheme case-insensitively and tolerates one or more spaces before the credentials per RFC 7235 §2.1. `client_secret_basic` on the client side and the Better Auth OAuth provider on the server side both go through these helpers, so credentials containing reserved characters round-trip cleanly across the stack and headers like `basic xxx` or `Basic xxx` are accepted. `createPrivateKeyJwtClientAssertionGetter` validates options eagerly. Unsupported algorithms (`HS256`, `none`), a JWK with no key material, and disagreement between an explicit `algorithm` and the JWK-embedded `alg` all throw at construction rather than on the first token request. `signPrivateKeyJwtClientAssertion` enforces the same checks for direct callers. **Breaking:** configurations that paired an unsupported JWK `alg` with a different explicit `algorithm` used to silently sign with the explicit option; they now fail at construction. **Breaking:** `@better-auth/oauth-provider` accepts client `jwks` metadata only as an RFC 7517 JWK Set object with a non-empty `keys` array. Replace `jwks: [key]` with `jwks: { keys: [key] }` in DCR payloads, administrative and user client creation, Client ID Metadata Documents, test fixtures, and generated client code. Remotely fetched `jwks_uri` responses must use the same object shape. EC keys must use P-256, P-384, or P-521; OKP keys must use [`Ed25519`](https://github.com/better-auth/better-auth/commit/Ed25519). When a key declares `alg`, it must be a supported `private_key_jwt` algorithm that matches the key type and curve; omit `alg` when the client chooses the algorithm in its assertion header. OAuth client rows previously written through `oauthToSchema` are already stored as JWK Set objects, so this is a request, configuration, and type migration rather than another database rewrite; audit rows written outside Better Auth separately. The SSO `private_key_jwt` flow redirects with `error_description=no_private_key_available` when a `resolvePrivateKey` callback returns no `privateKeyJwk` or `privateKeyPem`. The redirect path previously short-circuited only when the resolver was absent entirely; an empty resolver return fell through into an internal signing error. `better-auth/test` adds `getHttpTestInstance`, a counterpart to `getTestInstance` that binds a real HTTP listener on an OS-assigned port and constructs the auth instance against the discovered URL. It removes the temp-server-then-rebind race that test files have been individually copy-pasting. - [#&#8203;8836](https://github.com/better-auth/better-auth/pull/8836) [`93d3871`](https://github.com/better-auth/better-auth/commit/93d3871bd2f7c2fdd423c4c88a22a50b6333e656) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Add client authentication configuration for token endpoint requests across the stack, including `private_key_jwt` (RFC 7523). Generic OAuth providers now accept `tokenEndpointAuth` for token endpoint client authentication. Use `tokenEndpointAuth: { method: "private_key_jwt", getClientAssertion }` for JWT client assertions, `{ method: "none" }` for public clients, and `{ method: "client_secret_basic" }` or `{ method: "client_secret_post" }` with `clientSecret` for explicit secret-based client authentication. The existing `authentication: "basic" | "post"` option remains available for secret-based token requests. Use `createPrivateKeyJwtClientAssertionGetter()` to sign RFC 7523 assertions from a private key. The assertion getter receives `{ clientId, tokenEndpoint, grantType }`, so integrations do not duplicate client ID or token endpoint values inside assertion helpers. Core OAuth2 now exports private-key JWT-specific helpers and types: `signPrivateKeyJwtClientAssertion`, `createPrivateKeyJwtClientAssertionGetter`, `PrivateKeyJwtSigningAlgorithm`, and `PRIVATE_KEY_JWT_SIGNING_ALGORITHMS`. Token endpoint client authentication parameters are derived from `clientId`, `clientSecret`, and `tokenEndpointAuth`. Configured token endpoint authentication requires `clientId`; secret-based token endpoint authentication also requires `clientSecret`. Custom token parameters are for provider-specific fields and do not replace the configured client authentication values. `refreshAccessToken()` now forwards `resource` values to refresh-token requests, so RFC 8707 resource indicators work through both the high-level refresh helper and `refreshAccessTokenRequest()`. The synchronous OAuth2 request builders `createAuthorizationCodeRequest`, `createRefreshAccessTokenRequest`, and `createClientCredentialsTokenRequest` have been removed. Use the async `authorizationCodeRequest`, `refreshAccessTokenRequest`, and `clientCredentialsTokenRequest` helpers instead. Servers verify JWT client assertions signed with asymmetric keys, and clients can use the same token endpoint authentication contract for authorization code, refresh, and client credentials token requests. - [#&#8203;9134](https://github.com/better-auth/better-auth/pull/9134) [`652fa53`](https://github.com/better-auth/better-auth/commit/652fa53e4912837fe234651e7c7705fb35abe188) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - The dynamic `baseURL` config now ignores `x-forwarded-host` and `x-forwarded-proto` unless you set `advanced.trustedProxyHeaders: true`. Requests using `baseURL: { allowedHosts }` now resolve the auth origin from `Host` by default, so forwarded headers cannot select another allowed host unless trusted proxy headers are enabled. **Breaking change:** if your proxy exposes the public hostname only through `x-forwarded-host`, set `advanced.trustedProxyHeaders: true`. Deployments where the proxy rewrites `Host` to the public hostname (nginx default, Vercel, Cloudflare, and Netlify) are unaffected. **Migration:** ```ts betterAuth({ baseURL: { allowedHosts: [...] }, advanced: { trustedProxyHeaders: true, }, }); ``` - [#&#8203;9240](https://github.com/better-auth/better-auth/pull/9240) [`729c00d`](https://github.com/better-auth/better-auth/commit/729c00d74c94f558893da1e3a9ee86451d1b23da) Thanks [@&#8203;adrianmxb](https://github.com/adrianmxb)! - feat(username): add immutable username option This allows users to set their username during sign-up or first update, but prevents changing it to a different value afterwards. Users can still update other profile fields. - [#&#8203;10031](https://github.com/better-auth/better-auth/pull/10031) [`6fe9faa`](https://github.com/better-auth/better-auth/commit/6fe9faab65eb640dbe9bb762954a068586e8661c) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Remove the deprecated `oidcProvider` plugin from `better-auth/plugins`. Migrate OIDC authorization-server integrations to `@better-auth/oauth-provider`. - [#&#8203;10473](https://github.com/better-auth/better-auth/pull/10473) [`ed61b47`](https://github.com/better-auth/better-auth/commit/ed61b4798e0ccedadc3b0c0e0a2d08b5d4b7ed5a) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Add transactional OIDC user resolution so applications can link verified issuer and subject pairs to exact existing users while preserving or updating the local profile. - [#&#8203;10234](https://github.com/better-auth/better-auth/pull/10234) [`973fdde`](https://github.com/better-auth/better-auth/commit/973fdde79d9746b15d5ac0427049e8a008a7705c) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - The SIWE plugin now issues nonces before the wallet address or Chain ID is known. `authClient.siwe.nonce()` and `authClient.siwe.getNonce()` no longer accept wallet fields, `getNonce` must return an ERC-4361 nonce (8-250 alphanumeric characters), and SIWE verification now reads the wallet address and Chain ID from the signed ERC-4361 message. - [#&#8203;9864](https://github.com/better-auth/better-auth/pull/9864) [`41cca60`](https://github.com/better-auth/better-auth/commit/41cca606d14e7b8a1d16da662d644ca39fe4281f) Thanks [@&#8203;GautamBytes](https://github.com/GautamBytes)! - Add a `user.validateUserInfo` provisioning gate that lets applications reject an identity before a user is created or a new account is linked. It runs once at the creation step for every method that provisions a user (OAuth, SSO/SAML, email/password, magic link, email OTP, anonymous, SIWE, phone number, admin-created users, and SCIM), including stateless setups with no persistent database. It also re-runs when an existing OAuth or SSO user signs in again (`source.action` is `"sign-in"`), where it receives the fresh provider email and profile so a domain or org policy can reject a user whose provider identity moved out of bounds. Non-provider returning sign-ins are not re-validated. The callback receives the mapped `user` plus a `source` describing the `action` (`create-user`, `link-account`, or `sign-in`), the `method`, and provider metadata: `source.oauth` for OAuth providers and `source.sso` for OIDC/SAML SSO providers. Return `{ error, errorDescription }` to reject: browser flows redirect to the error URL and programmatic flows return a `403`. - [#&#8203;10036](https://github.com/better-auth/better-auth/pull/10036) [`ad35ead`](https://github.com/better-auth/better-auth/commit/ad35eadd130162565a1b93c27f3a66910dca0b0e) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Require Google One Tap server callbacks to resolve a Google client ID before verifying ID tokens. Configure `oneTap({ clientId })` or `socialProviders.google.clientId` when using the One Tap plugin. - [#&#8203;9057](https://github.com/better-auth/better-auth/pull/9057) [`544f1c6`](https://github.com/better-auth/better-auth/commit/544f1c63c9826831d96a126fbe568d8a8a8fde68) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - feat(two-factor)!: add OTP-only enablement and a discriminated response `enableTwoFactor` now accepts a `method` parameter (`"otp" | "totp"`, default `"totp"`) and returns a discriminated response with a `method` field. ##### `method: "otp"` - Sets `twoFactorEnabled: true` immediately. - Returns `{ method: "otp" }`. - Requires `otpOptions.sendOTP` to be configured on the server; rejects with `OTP_NOT_CONFIGURED` otherwise. ##### `method: "totp"` (default) - Returns `{ method: "totp", totpURI, backupCodes }`. - Rejects with `TOTP_NOT_CONFIGURED` if `totpOptions.disable` is set. The existing `skipVerificationOnEnable` option remains supported for TOTP enrollment. ##### Breaking changes - **Response shape changed**: `enableTwoFactor` includes a `method` field in the response (`"otp"` or `"totp"`). ##### Patch Changes - [#&#8203;10014](https://github.com/better-auth/better-auth/pull/10014) [`73541c1`](https://github.com/better-auth/better-auth/commit/73541c119041113b1909fe244ff4b8210618b5b5) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Cloudflare Workers apps can now start when importing Better Auth subpaths such as `better-auth/db`. Beta builds were crashing during module initialization before application code ran. - [#&#8203;10299](https://github.com/better-auth/better-auth/pull/10299) [`cf8eaac`](https://github.com/better-auth/better-auth/commit/cf8eaac26e11bcdb7309d537f1730b2559963861) Thanks [@&#8203;momomuchu](https://github.com/momomuchu)! - widen drizzle-kit peer dependency range - [#&#8203;10501](https://github.com/better-auth/better-auth/pull/10501) [`65fc17c`](https://github.com/better-auth/better-auth/commit/65fc17c755c3e2c8c77d5b401d612737764c219d) Thanks [@&#8203;KingIronMan2011](https://github.com/KingIronMan2011)! - Expand the optional `drizzle-orm` peer range to `^0.45.2 || >=1.0.0-rc.1 <2.0.0`, matching `@better-auth/drizzle-adapter` and allowing Drizzle ORM v1 RC installations without peer dependency warnings. - [#&#8203;10622](https://github.com/better-auth/better-auth/pull/10622) [`ecd83da`](https://github.com/better-auth/better-auth/commit/ecd83daa01ec482d31667019737cb6697f03da0b) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Sign-up no longer deadlocks when session cookie caching uses the JWT strategy on a single-connection SQLite database with native transactions enabled. JWKS key lookups and creation now resolve the transaction-scoped adapter instead of always querying the root connection, so minting a signing key during sign-up joins the surrounding transaction instead of racing it for the only available connection. On multi-connection databases (Postgres, MySQL) this also fixes a silent atomicity gap where a JWKS key created mid-transaction could commit independently of the transaction it was minted in. - [#&#8203;10293](https://github.com/better-auth/better-auth/pull/10293) [`fe4c820`](https://github.com/better-auth/better-auth/commit/fe4c8209479cc90a4c2e8692b2d84fde26926af2) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - `npx auth migrate` can now add required columns with static defaults and nullable unique columns to existing SQLite, PostgreSQL, and MySQL tables. Required unique columns still need distinct values to be backfilled manually before applying the unique constraint. - [#&#8203;9898](https://github.com/better-auth/better-auth/pull/9898) [`7fe0e2b`](https://github.com/better-auth/better-auth/commit/7fe0e2b165c17207a43863b0f1c12c401976d6b2) Thanks [@&#8203;ItalyPaleAle](https://github.com/ItalyPaleAle)! - Add `clientAssertion` support to the Microsoft Entra ID social provider. - [#&#8203;9301](https://github.com/better-auth/better-auth/pull/9301) [`03e6c94`](https://github.com/better-auth/better-auth/commit/03e6c94e965a7e87c1d44074b8e90257cb1f1cd2) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Add `allowIdpInitiated` to `GenericOAuthConfig` and SSO `OIDCConfig` to support providers that initiate OAuth without a `state` parameter (e.g. Clever). When enabled, stateless callbacks restart the OAuth flow server-side with fresh state and PKCE, preserving CSRF protection. Also hardens `parseState` against undefined request bodies on GET callbacks. - [#&#8203;10065](https://github.com/better-auth/better-auth/pull/10065) [`2196ea6`](https://github.com/better-auth/better-auth/commit/2196ea65e724830d9f1066c6593210579de586b9) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - OAuth and device-authorization responses that carry credentials now consistently send `Cache-Control: no-store` and `Pragma: no-cache`, so proxies, CDNs, and browsers never cache them. This covers the token, introspection, and userinfo endpoints, dynamic and admin client registration, client secret rotation, and the device code and device token responses, including the error responses from those endpoints. Endpoints declare this with `metadata: { noStore: true }`, and the header set is exported from `@better-auth/core` as `NO_STORE_HEADERS` for responses built by hand. - [#&#8203;10124](https://github.com/better-auth/better-auth/pull/10124) [`06daf70`](https://github.com/better-auth/better-auth/commit/06daf7011e548ef5a7d513c96e3a440331977a7d) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Preserve the resolved OAuth user when `overrideUserInfo` returns `null` during account linking. - [#&#8203;9304](https://github.com/better-auth/better-auth/pull/9304) [`e0d2b9e`](https://github.com/better-auth/better-auth/commit/e0d2b9eb9b4a515e1b73be71e1e3681faaa9b55f) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Propagate sign-out to every connected app and cut off API access immediately, via OIDC Back-Channel Logout 1.0. When a user's session ends at the OP (sign-out, `/oauth2/end-session`, admin revoke, ban), `@better-auth/oauth-provider` now notifies every Relying Party that holds tokens for that session. The user's API access is cut off right away, instead of access tokens staying usable until their own TTL. Each client opts in by registering a `backchannel_logout_uri` (and optionally `backchannel_logout_session_required`) via DCR or the admin client-create endpoint. The provider signs a `logout+jwt` Logout Token per client and POSTs it to that client in parallel, with a short per-RP timeout. **Breaking change.** Introspection of an opaque or JWT access token whose bound session has ended now returns `{ active: false }`, and `/oauth2/userinfo` rejects it with `invalid_token`. Previously the token stayed active until its own TTL. If you relied on access tokens outliving the user's session, that no longer holds. Refresh tokens without `offline_access` are revoked on session end; `offline_access` refresh tokens are preserved so long-lived API access can survive the browser session (OIDC Back-Channel Logout 1.0 §2.7). Access-token invalidation on session end is an additional OP hardening choice beyond §2.7, enforced by session liveness, so it holds even when the JWT plugin is disabled. Delivery runs through the host's background task handler when one is configured (Vercel `waitUntil`, Cloudflare `ctx.waitUntil`); without a handler it completes inline so notifications are not lost on request teardown. Configure `advanced.backgroundTasks.handler` on serverless runtimes to keep sign-out fast. Discovery at `/.well-known/openid-configuration` and `/.well-known/oauth-authorization-server` advertises `backchannel_logout_supported: true` and `backchannel_logout_session_supported: true` when the JWT plugin is enabled. Every registered `backchannel_logout_uri` must be a credential-free public HTTPS URL without a fragment; loopback HTTP is rejected for both public and confidential clients. CIMD documents cannot register back-channel logout metadata. The SSRF host guard, which blocks private, reserved, tunneled, and cloud-metadata hosts, also covers a `private_key_jwt` client's `jwks_uri`. Schema changes on `@better-auth/oauth-provider`: - `oauthClient.backchannelLogoutUri: string | null` - `oauthClient.backchannelLogoutSessionRequired: boolean` - `oauthAccessToken.revoked: Date | null` `better-auth`'s `signJWT` gains an optional `header` argument, forwarded to custom remote signers. JWT profiles that need an explicit media type, such as `typ: "logout+jwt"`, can now set it without reaching for the low-level signing primitives. - [#&#8203;10125](https://github.com/better-auth/better-auth/pull/10125) [`a83152e`](https://github.com/better-auth/better-auth/commit/a83152e2e884b1ac1724f95cea2056795d60e5cc) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Create new OAuth accounts in the user creation transaction. Adapters with native transaction support roll back the user when the account write fails, while other adapters still perform the writes sequentially. - [#&#8203;10128](https://github.com/better-auth/better-auth/pull/10128) [`97903c9`](https://github.com/better-auth/better-auth/commit/97903c9cca47f5fa62cf1d2ab86f6228db04aff0) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Preserve previously granted OAuth scopes across sign-in re-authentication and refresh-token requests. `account.scope` now accumulates monotonically: newly granted scopes are merged in only when added via `linkSocial`, and providers returning a narrower scope claim than the user has granted no longer shrink the stored value. - [#&#8203;10170](https://github.com/better-auth/better-auth/pull/10170) [`6ddb555`](https://github.com/better-auth/better-auth/commit/6ddb5554c01da4df6c637013dac7ea4ec8a43b52) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Bundled dependencies were refreshed to their latest compatible releases, including jose, nanostores, the noble crypto packages, and SimpleWebAuthn. These updates are backward compatible and require no changes to existing projects. - [#&#8203;10390](https://github.com/better-auth/better-auth/pull/10390) [`0de88f5`](https://github.com/better-auth/better-auth/commit/0de88f5e61d96f460e02b8a526e58acb16455d15) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - SCIM connections can now provision Users, Groups, and direct memberships into application-defined provisioning domains without the organization or SSO plugins. Applications can map Group membership to validated custom roles through projections. The service also supports SCIM 2.0 discovery, filtering, pagination, response attribute selection, atomic PATCH operations, and common request patterns used by Microsoft Entra ID and Okta. This replaces the previous SCIM configuration, client APIs, database schema, and organization-backed Group model. Existing SCIM installations cannot migrate provisioning state in place. Follow the SCIM cutover in the 1.7 upgrade guide, including full directory reprovisioning, before resuming traffic. Deferred database side effects now run only after a successful transaction. A rolled-back User update no longer refreshes its cached profile, and a rolled-back bulk session revocation no longer invalidates sessions. - [#&#8203;10505](https://github.com/better-auth/better-auth/pull/10505) [`d701f90`](https://github.com/better-auth/better-auth/commit/d701f90e6f81ede26209a50a5100bd9914a7ad5a) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - One Tap, Electron, and Expo client plugins now compose with `createAuthClient` without TypeScript errors, and the resulting client preserves each plugin's inferred actions. - [#&#8203;10621](https://github.com/better-auth/better-auth/pull/10621) [`59c4c83`](https://github.com/better-auth/better-auth/commit/59c4c832fc4eed813e98b5b2c45a91cf2d4ad9e7) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Allow test instances to enable native database transactions for postgres and mysql. - Updated dependencies \[[`5c45abc`](https://github.com/better-auth/better-auth/commit/5c45abcd2094d4a430cc84af6f9719fa0515ad71), [`763a267`](https://github.com/better-auth/better-auth/commit/763a2671c5372d88c291881977c8a1c2e29034b1), [`5d38b13`](https://github.com/better-auth/better-auth/commit/5d38b138c3c73eb06fe247ef6631c66e86ccc92b), [`692b22c`](https://github.com/better-auth/better-auth/commit/692b22c517011444f812fe21c206e399e35e8417), [`ea06c5a`](https://github.com/better-auth/better-auth/commit/ea06c5a71f448dfc600f1c2f7b0de732730c79cd), [`3d04fab`](https://github.com/better-auth/better-auth/commit/3d04fababbf3efd4c46a4012f46ed9397715c2e3), [`430c895`](https://github.com/better-auth/better-auth/commit/430c89549060ef6bd477ed2510650b9e49bba560), [`de8394d`](https://github.com/better-auth/better-auth/commit/de8394de207bae2fe9d0b8d7e901a196c1dc08d0), [`dbd302e`](https://github.com/better-auth/better-auth/commit/dbd302e422c66620cde391f6a80ab90ee34182f9), [`8784c1c`](https://github.com/better-auth/better-auth/commit/8784c1c1f4301acf96d980e5bf81ff56435e2545), [`ecd83da`](https://github.com/better-auth/better-auth/commit/ecd83daa01ec482d31667019737cb6697f03da0b), [`e4818b5`](https://github.com/better-auth/better-auth/commit/e4818b545984dce99e3c798ead5691c5bf775a70), [`7fe0e2b`](https://github.com/better-auth/better-auth/commit/7fe0e2b165c17207a43863b0f1c12c401976d6b2), [`0683a5f`](https://github.com/better-auth/better-auth/commit/0683a5f36befb45ade3866c7f8057791eadeee59), [`e7eb45b`](https://github.com/better-auth/better-auth/commit/e7eb45b065903f5fccddae491696cb069814a3c8), [`7c7313c`](https://github.com/better-auth/better-auth/commit/7c7313c8189baabd11a2ecb681bd2b16eb40fa4d), [`aedcb97`](https://github.com/better-auth/better-auth/commit/aedcb974f055c3514fe0464dc53d71d45a8a1725), [`03e6c94`](https://github.com/better-auth/better-auth/commit/03e6c94e965a7e87c1d44074b8e90257cb1f1cd2), [`4f53b61`](https://github.com/better-auth/better-auth/commit/4f53b61f49b470a40ccab18fe1fe4d80f225905f), [`2196ea6`](https://github.com/better-auth/better-auth/commit/2196ea65e724830d9f1066c6593210579de586b9), [`91f235f`](https://github.com/better-auth/better-auth/commit/91f235f8604cd432749adf18c7bd7d658aa1519b), [`34558bc`](https://github.com/better-auth/better-auth/commit/34558bc52b0e043021a1072f78de5f5439ae1734), [`1e5b808`](https://github.com/better-auth/better-auth/commit/1e5b80847208cf839c9d45363ca19b8eab41c68a), [`93d3871`](https://github.com/better-auth/better-auth/commit/93d3871bd2f7c2fdd423c4c88a22a50b6333e656), [`97903c9`](https://github.com/better-auth/better-auth/commit/97903c9cca47f5fa62cf1d2ab86f6228db04aff0), [`ed61b47`](https://github.com/better-auth/better-auth/commit/ed61b4798e0ccedadc3b0c0e0a2d08b5d4b7ed5a), [`0de88f5`](https://github.com/better-auth/better-auth/commit/0de88f5e61d96f460e02b8a526e58acb16455d15), [`3a79aff`](https://github.com/better-auth/better-auth/commit/3a79aff58ed82e45caf04c2ee4acaf0f4d09a86c), [`41cca60`](https://github.com/better-auth/better-auth/commit/41cca606d14e7b8a1d16da662d644ca39fe4281f), [`d701f90`](https://github.com/better-auth/better-auth/commit/d701f90e6f81ede26209a50a5100bd9914a7ad5a)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.7.0 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.7.0 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.7.0 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.7.0 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.7.0 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.7.0 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.7.0 ### [`v1.6.34`](https://github.com/better-auth/better-auth/releases/tag/v1.6.34) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.6.33...v1.6.34) ##### `better-auth` ##### Bug Fixes - Fixed `ACCOUNT_NOT_FOUND` when retrieving an access token after stateless OAuth sign-in links a new provider to an existing user ([#&#8203;11300](https://github.com/better-auth/better-auth/pull/11300)) For detailed changes, see [`CHANGELOG`](https://github.com/better-auth/better-auth/blob/bfec1c4f5a0d9a41ec89c65155024e06c7cb0f32/packages/better-auth/CHANGELOG.md) ##### `@better-auth/sso` ##### Bug Fixes - Fixed SAML sign-in for providers that use `samlConfig.callbackUrl` as their ACS URL without `spMetadata.metadata`, which had failed with `invalid_saml_response` since 1.6.21 ([#&#8203;11730](https://github.com/better-auth/better-auth/pull/11730)) For detailed changes, see [`CHANGELOG`](https://github.com/better-auth/better-auth/blob/bfec1c4f5a0d9a41ec89c65155024e06c7cb0f32/packages/sso/CHANGELOG.md) ##### Contributors Thanks to everyone who contributed to this release: [@&#8203;bytaesu](https://github.com/bytaesu), [@&#8203;gustavovalverde](https://github.com/gustavovalverde) **Full changelog:** [`v1.6.33...v1.6.34`](https://github.com/better-auth/better-auth/compare/v1.6.33...v1.6.34) ### [`v1.6.33`](https://github.com/better-auth/better-auth/releases/tag/v1.6.33) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.6.32...v1.6.33) #### `@better-auth/core` ##### Bug Fixes - Fixed database option type inference for projects that do not use Cloudflare Workers. ([#&#8203;11291](https://github.com/better-auth/better-auth/pull/11291)) For detailed changes, see [`CHANGELOG`](https://github.com/better-auth/better-auth/blob/0cb621c79766c3067c7c5de9b1e2f15cddd345e3/packages/core/CHANGELOG.md) #### Contributors Thanks to everyone who contributed to this release: [@&#8203;better-release](https://github.com/better-release)\[bot] **Full changelog:** [`v1.6.32...v1.6.33`](https://github.com/better-auth/better-auth/compare/v1.6.32...v1.6.33) ### [`v1.6.32`](https://github.com/better-auth/better-auth/releases/tag/v1.6.32) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.6.31...v1.6.32) ##### `better-auth` ##### Bug Fixes - Improved diagnostics for Cloudflare Turnstile verification failures ([#&#8203;11286](https://github.com/better-auth/better-auth/pull/11286)) For detailed changes, see [`CHANGELOG`](https://github.com/better-auth/better-auth/blob/fbc512bece72a32fae678d975a836eb4e9a4137b/packages/better-auth/CHANGELOG.md) ##### Contributors Thanks to everyone who contributed to this release: [@&#8203;better-release](https://github.com/better-release)\[bot] **Full changelog:** [`v1.6.31...v1.6.32`](https://github.com/better-auth/better-auth/compare/v1.6.31...v1.6.32) ### [`v1.6.31`](https://github.com/better-auth/better-auth/releases/tag/v1.6.31) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.6.30...v1.6.31) ##### `better-auth` ##### Features - Added an option to disable OpenTelemetry span creation for individual auth instances ([#&#8203;11228](https://github.com/better-auth/better-auth/pull/11228)) For detailed changes, see [`CHANGELOG`](https://github.com/better-auth/better-auth/blob/43415d7081b24e577c227052cc51ccebe909b6a8/packages/better-auth/CHANGELOG.md) ##### `@better-auth/core` ##### Features - Added an option to disable OpenTelemetry span creation for individual auth instances ([#&#8203;11228](https://github.com/better-auth/better-auth/pull/11228)) For detailed changes, see [`CHANGELOG`](https://github.com/better-auth/better-auth/blob/43415d7081b24e577c227052cc51ccebe909b6a8/packages/core/CHANGELOG.md) ##### Contributors Thanks to everyone who contributed to this release: [@&#8203;better-release](https://github.com/better-release)\[bot] **Full changelog:** [`v1.6.30...v1.6.31`](https://github.com/better-auth/better-auth/compare/v1.6.30...v1.6.31) ### [`v1.6.30`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1630) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.6.29...v1.6.30) ##### Patch Changes - Updated dependencies \[[`07c1718`](https://github.com/better-auth/better-auth/commit/07c17189f58502bf038e5f22766f8a99df60ac99)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.6.30 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.6.30 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.6.30 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.6.30 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.6.30 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.6.30 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.6.30 ### [`v1.6.29`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1629) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.6.28...v1.6.29) ##### Patch Changes - [#&#8203;10805](https://github.com/better-auth/better-auth/pull/10805) [`e6e1b4e`](https://github.com/better-auth/better-auth/commit/e6e1b4e8146a84d2a2c5fe2c497c81d03dfc2ad3) Thanks [@&#8203;Emmaccen](https://github.com/Emmaccen)! - Speed up batch session revocation when secondary storage is enabled by deleting sessions in parallel. - Updated dependencies \[]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.6.29 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.6.29 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.6.29 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.6.29 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.6.29 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.6.29 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.6.29 ### [`v1.6.28`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1628) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.6.27...v1.6.28) ##### Patch Changes - [#&#8203;10769](https://github.com/better-auth/better-auth/pull/10769) [`773de54`](https://github.com/better-auth/better-auth/commit/773de54b18c0e920a3542bdecaf8b42fffc0dc4b) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Prevent duplicate session requests during transient remounts while ensuring incomplete refreshes are revalidated. - [#&#8203;10794](https://github.com/better-auth/better-auth/pull/10794) [`2ad2928`](https://github.com/better-auth/better-auth/commit/2ad2928f967afa9f9858caecd01466ecb8686982) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Restore client plugin declaration compatibility for downstream TypeScript consumers. - Updated dependencies \[]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.6.28 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.6.28 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.6.28 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.6.28 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.6.28 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.6.28 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.6.28 ### [`v1.6.27`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1627) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.6.26...v1.6.27) ##### Patch Changes - [#&#8203;10657](https://github.com/better-auth/better-auth/pull/10657) [`2ae491e`](https://github.com/better-auth/better-auth/commit/2ae491eac3ece50839a0eb2d4f868c4deedac67b) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Aligned endpoint and middleware context types with runtime route parameters, and preserved response headers when resolving sessions from endpoint contexts. - [#&#8203;10676](https://github.com/better-auth/better-auth/pull/10676) [`90b5093`](https://github.com/better-auth/better-auth/commit/90b509344794b8064700371cbc04b985d0519839) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Deduplicate in-flight session requests when React retries a suspended component. - Updated dependencies \[[`2ae491e`](https://github.com/better-auth/better-auth/commit/2ae491eac3ece50839a0eb2d4f868c4deedac67b)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.6.27 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.6.27 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.6.27 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.6.27 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.6.27 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.6.27 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.6.27 ### [`v1.6.26`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1626) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.6.25...v1.6.26) ##### Patch Changes - [#&#8203;10619](https://github.com/better-auth/better-auth/pull/10619) [`9ede805`](https://github.com/better-auth/better-auth/commit/9ede8059b56e1415c1e8cfdd93ff72691b848bbf) Thanks [@&#8203;jeroenvandermerwe](https://github.com/jeroenvandermerwe)! - Ensure database rate-limit cleanup completes when no background task handler is configured. - [#&#8203;10608](https://github.com/better-auth/better-auth/pull/10608) [`5a811f1`](https://github.com/better-auth/better-auth/commit/5a811f1b4314b8bcf6f21c0b72de5cb67d552d97) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Pass the email verification type to custom OTP generators after email sign-up. - [#&#8203;10605](https://github.com/better-auth/better-auth/pull/10605) [`d8327f1`](https://github.com/better-auth/better-auth/commit/d8327f1fea92243b6fea1b0ab183e2a989792c0c) Thanks [@&#8203;XXMOHAMED012](https://github.com/XXMOHAMED012)! - The email OTP verification check no longer reveals whether an email is registered before the OTP itself is verified. - [#&#8203;10513](https://github.com/better-auth/better-auth/pull/10513) [`e2c73fb`](https://github.com/better-auth/better-auth/commit/e2c73fbec87f5e19f6a2b5ac371bc5bba9bd49ff) Thanks [@&#8203;mrosberghaus](https://github.com/mrosberghaus)! - Fix `jwtClient()` collapsing `createAuthClient` type inference when combined with other client plugins such as `inferAdditionalFields`. Additional user fields (for example on `updateUser`) are preserved again. - [#&#8203;10635](https://github.com/better-auth/better-auth/pull/10635) [`af50c45`](https://github.com/better-auth/better-auth/commit/af50c45553a62cfb6cdcdede86828731ca00c22c) Thanks [@&#8203;krish-vachhani](https://github.com/krish-vachhani)! - Fix `oneTapClient()` collapsing `createAuthClient` type inference when combined with other client plugins. The `oneTap` action is available on the client again. - [#&#8203;10633](https://github.com/better-auth/better-auth/pull/10633) [`701cd43`](https://github.com/better-auth/better-auth/commit/701cd43babac52784d855291a6adc0cf3fba7970) Thanks [@&#8203;gustavovalverde](https://github.com/gustavovalverde)! - Minting or reading a JWKS signing key inside an active database transaction now uses the transaction-scoped adapter instead of the root connection. On a single-connection SQLite database with native transactions enabled, this no longer deadlocks, and on Postgres and MySQL the key commits with the surrounding transaction instead of independently of it. - [#&#8203;10599](https://github.com/better-auth/better-auth/pull/10599) [`e7b0eba`](https://github.com/better-auth/better-auth/commit/e7b0eba327e050f50764802e21484c6cabb56600) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Preserve Apple user data from `form_post` callbacks when using `oAuthProxy`. - [#&#8203;10552](https://github.com/better-auth/better-auth/pull/10552) [`2b4a14f`](https://github.com/better-auth/better-auth/commit/2b4a14f180ed2eeb9692d6933064b001f66ec52c) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Allow users to retry email OTP password resets after entering an invalid password. - [#&#8203;10467](https://github.com/better-auth/better-auth/pull/10467) [`7552a3b`](https://github.com/better-auth/better-auth/commit/7552a3b563fe1ae922fb65db12d005c38a12614d) Thanks [@&#8203;jlucaso1](https://github.com/jlucaso1)! - Improve `nextCookies` performance in instrumented Next.js applications. - [#&#8203;10580](https://github.com/better-auth/better-auth/pull/10580) [`ea38fca`](https://github.com/better-auth/better-auth/commit/ea38fcac7435137604e9b3ba2fe149a1848d0eeb) Thanks [@&#8203;Emmaccen](https://github.com/Emmaccen)! - Skip invalid secondary-storage session entries without discarding other valid sessions. - [#&#8203;10520](https://github.com/better-auth/better-auth/pull/10520) [`a03e4c1`](https://github.com/better-auth/better-auth/commit/a03e4c18677e2dc01a9b47b2a8017b92dbf9ece7) Thanks [@&#8203;bytaesu](https://github.com/bytaesu)! - Ensure deleting a user also removes their sessions from secondary storage. - Updated dependencies \[[`a30e274`](https://github.com/better-auth/better-auth/commit/a30e274b5daed6057086d76b91d17abfa02196d7)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.6.26 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.6.26 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.6.26 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.6.26 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.6.26 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.6.26 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.6.26 ### [`v1.6.25`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1625) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.6.24...v1.6.25) ##### Patch Changes - [#&#8203;10479](https://github.com/better-auth/better-auth/pull/10479) [`5124c34`](https://github.com/better-auth/better-auth/commit/5124c3487903e96223bb3f54347724bb0204bb95) Thanks [@&#8203;krish-vachhani](https://github.com/krish-vachhani)! - Prevent Google One Tap from creating new users when sign-up is disabled for the Google provider. - [#&#8203;10444](https://github.com/better-auth/better-auth/pull/10444) [`7439359`](https://github.com/better-auth/better-auth/commit/743935991f9991e8243d6c3d14773b9cfca462e8) Thanks [@&#8203;birkskyum](https://github.com/birkskyum)! - Expose the real `$fetch` instance and `$store` atoms from the Solid client instead of resolving them as dynamic API routes. - Updated dependencies \[[`0ffd1fb`](https://github.com/better-auth/better-auth/commit/0ffd1fb28d44a8266d62791cd4c97e263444d03b)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.6.25 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.6.25 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.6.25 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.6.25 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.6.25 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.6.25 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.6.25 ### [`v1.6.24`](https://github.com/better-auth/better-auth/blob/HEAD/packages/better-auth/CHANGELOG.md#1624) [Compare Source](https://github.com/better-auth/better-auth/compare/v1.6.23...v1.6.24) ##### Patch Changes - [#&#8203;10235](https://github.com/better-auth/better-auth/pull/10235) [`03dc5a0`](https://github.com/better-auth/better-auth/commit/03dc5a046f536994950800ea557b8e2e2e0cdfdd) Thanks [@&#8203;ping-maxwell](https://github.com/ping-maxwell)! - Fixes silent foreign-key and adapter-join misrouting when a user remaps a built-in model name to a string that collides with another schema key - [#&#8203;10357](https://github.com/better-auth/better-auth/pull/10357) [`7508940`](https://github.com/better-auth/better-auth/commit/750894037639c4158472cc1d4994b0e07bf1f59a) Thanks [@&#8203;c-nicol](https://github.com/c-nicol)! - Fixes Kysely migration generation for new-table fields that are both unique: true and index: true. - [#&#8203;10342](https://github.com/better-auth/better-auth/pull/10342) [`bae7198`](https://github.com/better-auth/better-auth/commit/bae71988ab79aeb4f19f245ceabac9eca8706a50) Thanks [@&#8203;ping-maxwell](https://github.com/ping-maxwell)! - Fix `organization.listMembers` failing with "User not found for member" for orgs with more than \~100 members by applying the same membership limit to the users query. - [#&#8203;10336](https://github.com/better-auth/better-auth/pull/10336) [`ef4d273`](https://github.com/better-auth/better-auth/commit/ef4d27360cec8a0bc11a94e135ea4a3dd32b1969) Thanks [@&#8203;Tushar-Khandelwal-2004](https://github.com/Tushar-Khandelwal-2004)! - Prevent verification callbacks from failing auth requests when cloning the request throws. - [#&#8203;10333](https://github.com/better-auth/better-auth/pull/10333) [`99dbdd7`](https://github.com/better-auth/better-auth/commit/99dbdd7ea98740d11689394220a718dfb9579276) Thanks [@&#8203;c-nicol](https://github.com/c-nicol)! - Fixes Drizzle schema generation for fields that are both unique: true and index: true. - [#&#8203;10368](https://github.com/better-auth/better-auth/pull/10368) [`086ca91`](https://github.com/better-auth/better-auth/commit/086ca91f51dd8158aff6cbf54c4f9c7ce220914d) Thanks [@&#8203;gaurav0107](https://github.com/gaurav0107)! - Force-validate the request `Origin` on the magic-link (`/sign-in/magic-link`) and email-otp (`/email-otp/send-verification-otp`) send endpoints, including cookieless requests, to match the built-in `/sign-in/email` and `/sign-up/email` routes. A cookieless cross-origin POST can no longer trigger a magic-link or verification-OTP email to an arbitrary address. Cookieless requests that carry no `Origin` (server-to-server) are unaffected. - [#&#8203;10290](https://github.com/better-auth/better-auth/pull/10290) [`8f2dedd`](https://github.com/better-auth/better-auth/commit/8f2dedd89301da9fb52c1a64df6a9683f9be55fd) Thanks [@&#8203;GautamBytes](https://github.com/GautamBytes)! - Expose the remote MCP auth client's 401 challenge headers to browser clients using CORS. - [#&#8203;10453](https://github.com/better-auth/better-auth/pull/10453) [`4e685ee`](https://github.com/better-auth/better-auth/commit/4e685eef420b5576913b9803b58c7e7ee7342203) Thanks [@&#8203;ping-maxwell](https://github.com/ping-maxwell)! - OpenAPI now includes `user.additionalFields` and plugin user schema fields (e.g. username plugin `username` / `displayUsername`) on `/sign-up/email` and `/update-user` request bodies. - [#&#8203;10190](https://github.com/better-auth/better-auth/pull/10190) [`3bf0e49`](https://github.com/better-auth/better-auth/commit/3bf0e4981e025ba9af684013a27b0102a04f7c56) Thanks [@&#8203;gaurav-init](https://github.com/gaurav-init)! - Pass the endpoint context as the second argument to `beforeDeleteOrganization` and `afterDeleteOrganization` hooks in the organization plugin, matching the signature shown in the docs and the existing `databaseHooks` pattern. The Stripe plugin's `beforeDeleteOrganization` wrapper now forwards the context to user-supplied hooks instead of dropping it. - [#&#8203;10040](https://github.com/better-auth/better-auth/pull/10040) [`f59a0ee`](https://github.com/better-auth/better-auth/commit/f59a0ee7895a024ddd4c5c387344173888e17be4) Thanks [@&#8203;shiminshen](https://github.com/shiminshen)! - Organization invitations now let the database generate their `id` when ID generation is delegated to the database (e.g. `advanced.database.generateId: "uuid"` with a UUID-capable adapter such as Postgres), matching every other model. Previously `createInvitation` always generated the invitation `id` in application code, so invitation rows received an app-generated value instead of a database-generated one while organizations, members and teams correctly deferred to the database ([better-auth/better-auth#10024](https://github.com/better-auth/better-auth/issues/10024)). A caller-provided id (e.g. via `beforeCreateInvitation`) is still honored. - [#&#8203;10302](https://github.com/better-auth/better-auth/pull/10302) [`0f2cc1b`](https://github.com/better-auth/better-auth/commit/0f2cc1b33b77850948dac4d889e5f46bba41e8d5) Thanks [@&#8203;momomuchu](https://github.com/momomuchu)! - Prefer exact schema-key matches over `modelName` aliases in `getDefaultModelName`, so remapping a built-in table onto another table's schema key (e.g. `user.modelName = "account"`) does not reroute internal adapter queries to the wrong table. - [#&#8203;9787](https://github.com/better-auth/better-auth/pull/9787) [`ae78109`](https://github.com/better-auth/better-auth/commit/ae781091186f321b4e4ec9e84f64b6e4d5ea1043) Thanks [@&#8203;ping-maxwell](https://github.com/ping-maxwell)! - Fixes an issue where `useSession({ throw: true })` incorrectly excluded `null` from its `data` type. - [#&#8203;10222](https://github.com/better-auth/better-auth/pull/10222) [`46d2bf0`](https://github.com/better-auth/better-auth/commit/46d2bf02c98902da7b344753372d48cfe0e5ebb3) Thanks [@&#8203;ping-maxwell](https://github.com/ping-maxwell)! - fix: add no-store cache-control headers to get-session route - [#&#8203;10316](https://github.com/better-auth/better-auth/pull/10316) [`29a373e`](https://github.com/better-auth/better-auth/commit/29a373eaf1778820061a9380c29831c2de2ce704) Thanks [@&#8203;vinay-oppuri](https://github.com/vinay-oppuri)! - Recognize SQLite `BIGINT` as a valid number type in migration diffs so database-backed rate limiter columns like `lastRequest` no longer report spurious pending changes on every run. - [#&#8203;10379](https://github.com/better-auth/better-auth/pull/10379) [`f6d18fa`](https://github.com/better-auth/better-auth/commit/f6d18fa8f79b9323e10b50f72e2b1a088844e4bb) Thanks [@&#8203;ping-maxwell](https://github.com/ping-maxwell)! - fix(client): restore auth query revalidation and signal listeners after remount - [#&#8203;5753](https://github.com/better-auth/better-auth/pull/5753) [`f23ce50`](https://github.com/better-auth/better-auth/commit/f23ce5012ea47fac1a69b1dad203dfdef3830fd0) Thanks [@&#8203;ping-maxwell](https://github.com/ping-maxwell)! - feat(last-login-method): beforeStoreCookie option for GDPR compliance - [#&#8203;10376](https://github.com/better-auth/better-auth/pull/10376) [`c4d1dda`](https://github.com/better-auth/better-auth/commit/c4d1ddaa952eab7edfec942fab223f35798518ab) Thanks [@&#8203;ping-maxwell](https://github.com/ping-maxwell)! - Pass the request endpoint context as a third argument to `verifyIdToken`, so custom ID token verifiers can read request headers (for example Apple's `user-agent` requirement). - Updated dependencies \[[`6758231`](https://github.com/better-auth/better-auth/commit/6758231905d2e86a7b3f058dd05c17ba739aa80f), [`54fab08`](https://github.com/better-auth/better-auth/commit/54fab084469a27257e66a0814523ebac7145ef5d), [`c4d1dda`](https://github.com/better-auth/better-auth/commit/c4d1ddaa952eab7edfec942fab223f35798518ab)]: - [@&#8203;better-auth/core](https://github.com/better-auth/core)@1.6.24 - [@&#8203;better-auth/drizzle-adapter](https://github.com/better-auth/drizzle-adapter)@1.6.24 - [@&#8203;better-auth/kysely-adapter](https://github.com/better-auth/kysely-adapter)@1.6.24 - [@&#8203;better-auth/memory-adapter](https://github.com/better-auth/memory-adapter)@1.6.24 - [@&#8203;better-auth/mongo-adapter](https://github.com/better-auth/mongo-adapter)@1.6.24 - [@&#8203;better-auth/prisma-adapter](https://github.com/better-auth/prisma-adapter)@1.6.24 - [@&#8203;better-auth/telemetry](https://github.com/better-auth/telemetry)@1.6.24 </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **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. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNjQuMiIsInVwZGF0ZWRJblZlciI6IjQ0LjY1LjUiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbImJhY2tlbmQiLCJmcm9udGVuZCIsInJlbm92YXRlIl19-->
renovate-bot force-pushed renovate/better-auth-monorepo from b518d06c33 to a474bd363f 2026-07-24 06:01:08 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.6.24 to Update dependency better-auth to v1.6.25 2026-07-24 06:01:09 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from a474bd363f to f27f3b2939 2026-08-10 13:05:55 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.6.25 to Update dependency better-auth to v1.6.26 2026-08-10 13:06:12 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from f27f3b2939 to 0f41e11b29 2026-08-12 06:01:53 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.6.26 to Update dependency better-auth to v1.6.27 2026-08-12 06:01:55 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from 0f41e11b29 to defe51e91e 2026-08-14 06:01:41 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.6.27 to Update dependency better-auth to v1.6.28 2026-08-14 06:01:44 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from defe51e91e to 6d01c96957 2026-08-15 06:01:09 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.6.28 to Update dependency better-auth to v1.6.29 2026-08-15 06:01:11 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from 6d01c96957 to 32b35f475e 2026-08-18 06:01:52 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.6.29 to Update dependency better-auth to v1.7.0 2026-08-18 06:01:54 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from 32b35f475e to 19b2db286e 2026-08-19 06:01:39 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.0 to Update dependency better-auth to v1.7.1 2026-08-19 06:01:41 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from 19b2db286e to 98757bc7e5 2026-08-27 06:01:40 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.1 to Update dependency better-auth to v1.7.2 2026-08-27 06:01:43 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from 98757bc7e5 to cff14a304e 2026-09-06 06:01:09 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.2 to Update dependency better-auth to v1.7.3 2026-09-06 06:01:12 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from cff14a304e to a9ea98f45a 2026-09-11 06:01:55 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.3 to Update dependency better-auth to v1.7.4 2026-09-11 06:02:18 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from a9ea98f45a to d949416bc5 2026-09-15 06:02:28 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.4 to Update dependency better-auth to v1.7.5 2026-09-15 06:02:33 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from d949416bc5 to 345c0c42a1 2026-09-25 06:02:12 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.5 to Update dependency better-auth to v1.7.6 2026-09-25 06:02:18 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from 345c0c42a1 to cfc85625c1 2026-10-01 06:02:27 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.6 to Update dependency better-auth to v1.7.7 2026-10-01 06:02:33 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from cfc85625c1 to 2ef64116eb 2026-10-15 06:01:41 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.7 to Update dependency better-auth to v1.7.9 2026-10-15 06:01:59 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from 2ef64116eb to 9d0e084539 2026-10-16 06:03:01 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.9 to Update dependency better-auth to v1.7.10 2026-10-16 06:03:07 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from 9d0e084539 to 8331b4c827 2026-10-19 06:01:29 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.10 to Update dependency better-auth to v1.7.11 2026-10-19 06:01:34 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from 8331b4c827 to c144dfc8ac 2026-10-21 06:01:59 +01:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.11 to Update dependency better-auth to v1.7.12 2026-10-21 06:02:04 +01:00
renovate-bot force-pushed renovate/better-auth-monorepo from c144dfc8ac to 30793376a8 2026-10-26 05:02:35 +00:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.12 to Update dependency better-auth to v1.7.13 2026-10-26 05:02:40 +00:00
renovate-bot force-pushed renovate/better-auth-monorepo from 30793376a8 to 4f16dacba3 2026-10-27 05:01:31 +00:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.13 to Update dependency better-auth to v1.7.14 2026-10-27 05:01:37 +00:00
renovate-bot force-pushed renovate/better-auth-monorepo from 4f16dacba3 to e984683d74 2026-11-02 05:01:36 +00:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.14 to Update dependency better-auth to v1.7.15 2026-11-02 05:01:43 +00:00
renovate-bot force-pushed renovate/better-auth-monorepo from e984683d74 to 6523f8dc32 2026-11-04 05:02:54 +00:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.15 to Update dependency better-auth to v1.7.16 2026-11-04 05:03:02 +00:00
renovate-bot force-pushed renovate/better-auth-monorepo from 6523f8dc32 to 5e3203eec3 2026-11-05 05:01:54 +00:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.16 to Update dependency better-auth to v1.7.17 2026-11-05 05:02:00 +00:00
renovate-bot force-pushed renovate/better-auth-monorepo from 5e3203eec3 to 8fd0d4f851 2026-11-06 05:02:57 +00:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.17 to Update dependency better-auth to v1.7.18 2026-11-06 05:03:04 +00:00
renovate-bot force-pushed renovate/better-auth-monorepo from 8fd0d4f851 to e03870a416 2026-11-10 05:03:26 +00:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.18 to Update dependency better-auth to v1.7.19 2026-11-10 05:03:34 +00:00
renovate-bot force-pushed renovate/better-auth-monorepo from e03870a416 to 6fdf85c3b6 2026-11-11 05:01:44 +00:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.19 to Update dependency better-auth to v1.7.20 2026-11-11 05:01:53 +00:00
renovate-bot force-pushed renovate/better-auth-monorepo from 6fdf85c3b6 to 2428dd637c 2026-11-12 05:02:28 +00:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.20 to Update dependency better-auth to v1.7.21 2026-11-12 05:02:36 +00:00
renovate-bot force-pushed renovate/better-auth-monorepo from 2428dd637c to 2f8e861c4b 2026-11-13 05:01:42 +00:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.21 to Update dependency better-auth to v1.7.22 2026-11-13 05:01:49 +00:00
renovate-bot force-pushed renovate/better-auth-monorepo from 2f8e861c4b to 069a2bba12 2026-11-14 05:02:04 +00:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.22 to Update dependency better-auth to v1.7.23 2026-11-14 05:02:26 +00:00
renovate-bot force-pushed renovate/better-auth-monorepo from 069a2bba12 to 43a2f9eb1c 2026-11-15 05:02:41 +00:00 Compare
renovate-bot changed title from Update dependency better-auth to v1.7.23 to Update dependency better-auth to v1.7.25 2026-11-15 05:02:48 +00:00
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin 'renovate/better-auth-monorepo:renovate/better-auth-monorepo'
git switch 'renovate/better-auth-monorepo'

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch 'main'
git merge --no-ff 'renovate/better-auth-monorepo'
git switch 'renovate/better-auth-monorepo'
git rebase 'main'
git switch 'main'
git merge --ff-only 'renovate/better-auth-monorepo'
git switch 'renovate/better-auth-monorepo'
git rebase 'main'
git switch 'main'
git merge --no-ff 'renovate/better-auth-monorepo'
git switch 'main'
git merge --squash 'renovate/better-auth-monorepo'
git switch 'main'
git merge --ff-only 'renovate/better-auth-monorepo'
git switch 'main'
git merge 'renovate/better-auth-monorepo'
git push origin 'main'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MobiusReactor/TicTacToeV2!126
No description provided.