Repository navigation
chore(deps): update dependency better-auth to v1.7.6 - #284
Open
renovate[bot] wants to merge 1 commit into
Open
renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
June 12, 2026 21:45
43c4488 to
5bb26b5
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
June 14, 2026 12:52
5bb26b5 to
0665d53
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
June 16, 2026 22:53
0665d53 to
fc67f4c
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
June 22, 2026 17:42
fc67f4c to
8261bb6
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
June 24, 2026 10:42
8261bb6 to
1a7938c
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
June 26, 2026 07:02
1a7938c to
e3c758b
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
June 26, 2026 19:41
e3c758b to
4bc8c30
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
June 30, 2026 01:51
4bc8c30 to
9156555
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
July 4, 2026 04:42
9156555 to
b5b33e9
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
July 10, 2026 06:07
b5b33e9 to
6357357
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
July 10, 2026 15:10
6357357 to
e6a8542
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
July 13, 2026 23:01
e6a8542 to
340ad43
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
August 5, 2026 11:07
340ad43 to
d3bc835
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
August 6, 2026 18:59
d3bc835 to
3bad485
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
August 19, 2026 02:41
3bad485 to
504e4ab
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
August 25, 2026 20:43
504e4ab to
f49a677
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
August 28, 2026 00:01
f49a677 to
d2bdfef
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
August 28, 2026 22:50
d2bdfef to
196911e
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
August 31, 2026 19:38
196911e to
da4f2bd
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
September 1, 2026 01:54
da4f2bd to
5c9ea0d
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
September 1, 2026 22:11
5c9ea0d to
ae3743b
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
2 times, most recently
from
September 9, 2026 23:00
2ede8ad to
9ab745e
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
September 20, 2026 05:24
9ab745e to
b7b9466
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
September 25, 2026 01:48
b7b9466 to
791411d
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
September 29, 2026 02:20
791411d to
4b0ea41
Compare
renovate
Bot
force-pushed
the
renovate/better-auth-group
branch
from
October 8, 2026 17:06
4b0ea41 to
1cdb5ae
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
1.5.6→1.7.6Release Notes
better-auth/better-auth (better-auth)
v1.7.6Compare Source
Patch Changes
#11325
af88385Thanks @Wadiou! - Admin pluginbannedUserMessagecan now be a function that receives the banned user, so sign-in errors can include details such as the ban reason.#11268
2fa501cThanks @bytaesu! - Support linking social accounts through the OAuth Proxy plugin.#11366
d41e2caThanks @bytaesu! - Use targeted PRAGMA queries when a Kysely dialect cannot introspect Cloudflare D1.#11016
3d0efa3Thanks @davbrito! - Support Vercel BotID checks on protected authentication routes in Vercel-hosted applications.#11333
631ac29Thanks @bytaesu! - Preserve logical model identity when a custom model name matches another schema key.#11324
8853419Thanks @XXMOHAMED012! - Passwords longer thanmaxPasswordLengthare now rejected withPASSWORD_TOO_LONGbefore 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.#11316
2ee1545Thanks @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.#11376
fc45d08Thanks @bytaesu! - Prevent older auth-query responses from replacing newer results when requests overlap.Updated dependencies [
41b7dc1,d41e2ca,631ac29,2b13e01]:v1.7.5Compare Source
Patch Changes
#11283
e56c45bThanks @bytaesu! - Log Cloudflare Turnstile error codes and binding mismatches on the server so CAPTCHA verification failures can be diagnosed.#11209
8d37cc3Thanks @siam923! - Remove the unused optionalbetter-sqlite3peer dependency to prevent installation conflicts.#11272
348fc26Thanks @bytaesu! - Use MySQL's reported byte lengths when validating indexes on existing string columns.#11203
cb627ebThanks @dshukertjr! - Add adatabase.schemaNameoption for direct PostgreSQL connections. When set, the adapter and the CLI qualify every statement with that schema, soauth generatewrites a schema-qualified migration that creates the schema before its tables instead of relying on the connection'ssearch_path.#11270
133f6a2Thanks @bytaesu! - Prevent PostgreSQL migrations from treating tables in other schemas or views in the active schema as Better Auth tables.Updated dependencies [
e18bc83,cb627eb,dae97ed]:v1.7.4Compare Source
Patch Changes
#11205
3f890ebThanks @bytaesu! - Support Vitest 5 in the testing utilities while retaining support for previously supported Vitest versions.#11224
c1756a2Thanks @bytaesu! - Addexperimental.instrumentation.enabledto disable Better Auth OpenTelemetry span creation per auth instance. Instrumentation remains enabled by default and independent of usage reporting.#11217
9b9638eThanks @onmax! - AllowtestUtilsauth helpers to accept additional session fields through thesessionoption, including required fields without defaults and per-session overrides of configured defaults.Updated dependencies [
3ff842a,b905bfe,c1756a2]:v1.7.3Compare Source
Patch Changes
#11060
3660f06Thanks @bytaesu! - Handle malformed custom-scheme callback URLs without excessive processing.#11037
5bd7096Thanks @bytaesu! - Prevent repeated TOTP enrollment from replacing an active authenticator and its backup codes.#11120
7ec7146Thanks @onmax! - PreventgetSessionfrom failing when cookie caching is disabled while clients still have cached session cookies.#9908
76d311fThanks @harshil1712! - Add Cloudflare as a built-in social provider, with support for client-secret authentication and PKCE clients without a secret.#11188
c47b765Thanks @bytaesu! - Normalize Auth0 domains without a potentially slow trailing-slash regular expression.#11084
2d5c63dThanks @bytaesu! - Prevent duplicate session requests and hydration mismatches when using the Vue client with NuxtuseFetch.#11147
a9d8c12Thanks @bytaesu! - AddisPasswordCompromisedfor checking passwords against Have I Been Pwned in custom server-side flows, while ignoring padded response entries with zero occurrences.#10988
9fc7498Thanks @bytaesu! - Run callback hooks after proxied OAuth sign-ins and preserve server state when callback cookies are unavailable. The legacy/oauth-proxy-callbackendpoint is deprecated and will be removed in the next minor release.#11178
be0e007Thanks @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: falseto disable runtime validation.auth migraterefuses to apply changes when required unwritten columns need manual repair.#11069
0bb0dbfThanks @bytaesu! - Improve dynamic organization role permission check performance.#11153
2220ee7Thanks @bytaesu! - Restore sign-in compatibility with 1.6 databases by identifying accounts with(providerId, accountId)and removing theissuerrequirement 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
issuernullable or remove the column so sign-ups and account linking can succeed.auth migratedoes not perform this cleanup. Follow the upgrade guide for database-specific steps.#10978
5fe5bc2Thanks @BetterAndBetterII! - Skip a generic OAuth provider when discovery fails instead of taking down the rest of the auth API.#10963
74a7369Thanks @thisismert! - Track email OTP sign-ins in the last login method plugin.#11085
e16b40aThanks @bytaesu! - Provide type-safe NuxtuseFetchintegration for the Vue client'suseSessionhook.#11066
c0444dcThanks @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.2Compare Source
Patch Changes
#10875
d5d889bThanks @bytaesu! - Fix programmatic migrations failing on Cloudflare D1 while preserving existing-index validation across supported databases.#10982
b4ad5a1Thanks @bytaesu! - Built-in placeholder emails now consistently use the namespaced{identifier}@{namespace}.placeholder.invalidformat.#10934
c7a5c1aThanks @bytaesu! - Cookie-cache reads now warn when signed session data is invalid instead of silently appearing as a signed-out session.#10879
78f0c39Thanks @starslingdev! - Test suites usinggetTestInstancenow run faster because the shared fixture avoids production password-hashing costs by default. CustomemailAndPassword.passwordimplementations continue to take precedence.#10823
ce8a3abThanks @sosyz! - Ensure permanently banning a user clears any expiration from a previous temporary ban.#10907
a021eafThanks @heliohm! - A client created with more plugins is again assignable to a client type declaring fewer plugins, as in 1.6.#10959
c8dcfa5Thanks @bytaesu! - Allow same-origin form submissions from pages usingReferrer-Policy: no-referrerwhile continuing to reject untrusted request origins.#10979
fced1a5Thanks @bytaesu! - Allow relative callback and redirect URLs to use standard path, query, and fragment syntax while preserving open-redirect protections.#10041
f6891a2Thanks @GautamBytes! - Allow~in relative callback URLs validated by trusted origin checks.#10877
649818aThanks @bytaesu! - Prevent disabled MyISAM indexes from satisfying migration index checks.Updated dependencies [
557e19b,64da15b,d5d889b,b4ad5a1,ea77118,5aea9f7,fced1a5,e1d4011]:v1.7.1Patch Changes
#10863
845bbd1Thanks @gustavovalverde! -auth migrateno longer attempts to add a required column with no default value to a table that already has rows. It stops with an error naming the column and the backfill to run first. Previously the generated statement failed on SQLite, Postgres, and SQL Server; on MySQL it filled the new column with an empty string for every existing row and reported success. Ifauth migratealready ran against a MySQL database on 1.7, run the check in the upgrade guide's account identity section.getMigrationsthrows the newUnsafeMigrationError(exported frombetter-auth/db/migration) for this refusal, so callers can distinguish it from other migration errors such as an index-definition conflict.auth generatestill emits the statements for external migration tooling, with a comment banner naming any column that needs a manual backfill first.A required field whose database column is still nullable logs a warning instead of blocking the migration.
A CLI command that fails now prints its error and exits with a non-zero code instead of an unhandled promise rejection.
Updated dependencies []:
v1.7.0Minor Changes
#8733
4e8e4c7Thanks @bytaesu! - AddhydrateSessionto seed the client with a server-fetched session souseSessionreturns data on the first render.#9930
0cbaf81Thanks @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.onLinkAccountfires 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
addOAuthServerContextAPI, read back on the callback viagetOAuthState().serverContext. UnlikeadditionalData, 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 throughadditionalData.#10004
b36c38fThanks @bytaesu! - The captcha plugin now requires endpoint entries to match full auth paths unless they use wildcard patterns. This prevents requests like/sign-in//emailfrom bypassing captcha while preserving trailing-slash matches like/sign-in/email/. To protect multiple routes, replace partial paths like/sign-inwith explicit wildcards such as/sign-in/*or/sign-in/**.#10746
6782647Thanks @gustavovalverde! - OAuth device grants now useoauthDeviceAuthorization()alongsideoauthProvider()ormcp(). This single integration replaces both the standalonedeviceCodeGrant()plugin and the shared-grant configuration. Standalone Device Authorization no longer accepts or stores RFC 8707 resources, andonDeviceAuthRequestreceives onlyclientIdandscope. The OAuth integration rejects resource indicators that are not absolute, fragment-free URIs.The OAuth integration replaces the optional
resourcecolumn withoauthClientIdandresources. 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
763a267Thanks @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
bf39cbfThanks @GautamBytes! - Add a server-onlyauth.api.consumePhoneNumberOTPAPI for custom phone OTP flows that need to verify and consume a code without creating or updating users or sessions.#10330
081d3c3Thanks @ping-maxwell! - Allow the username plugin's separatedisplayUsernamefield to be omitted bysetting
displayUsername: falseon both the server and client plugins.#10059
49b5cf6Thanks @GautamBytes! - Device Authorization now creates unique database indexes fordeviceCodeanduserCode, 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_errorif it cannot create a uniquedeviceCodeanduserCode. 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/devicelimiter allows 5 requests over a window equal to the configured code lifetime, while/device/tokenpolling keeps its separate interval behavior.#9645
e014029Thanks @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_methodparameter is gone and every authorization code is verified by hashing the verifier with SHA-256. The server no longer trusts anelectron-originheader to set the request Origin. The Electron client now sends a realOrigin(for examplemyapp:/), so upgrade the@better-auth/electronclient and server together and make sure your app's scheme is intrustedOrigins. The unuseddisableOriginOverrideoption is removed.Custom-scheme entries in
trustedOriginsnow match by scheme and authority instead of string prefix. A host-less entry such asmyapp://orexp://still trusts every host of that scheme, but a host-bearing entry such asmyapp://callbackmatches that host exactly, so it is no longer satisfied bymyapp://callback.attacker.tld.#9948
3d04fabThanks @yordis! - feat(generic-oauth): addrefreshTokenParamsconfig to forward extra params on token refreshMulti-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 arefreshTokenParamsoption (object or sync/async function) that is merged into the refresh request body, withgrant_typeandrefresh_tokenprotected 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.refreshAccessTokennow accepts an optional secondctxargument; the change is backwards compatible because existing implementations that take onlyrefreshTokenremain valid. See #7554.#9069
c7d2253Thanks @gustavovalverde! - Rewrite the generic OAuth plugin as a first-class social provider with OAuth 2.1 security defaults. Providers now usesignIn.social+callback/:idinstead of dedicated plugin endpoints, with PKCE required by default (OAuth 2.1), RFC 9207 issuer validation, OIDC auto-discovery withopenidscope injection, and typed provider IDs.Breaking changes:
signIn.oauth2({ providerId })replaced bysignIn.social({ provider })oauth2.link()replaced bylinkSocial()/api/auth/oauth2/callback/:idto/api/auth/callback/:idgenericOAuthClient()removed; generic OAuth providers now use the standard social client APIspkcedefaults totrue(wasfalse); setpkce: falsefor providers that reject PKCEauthorizationUrlParamsandtokenUrlParamsonly acceptRecord<string, string>issuerandrequireIssuerValidationconfig fields removed; issuer validation is automatic via OIDC discoverymapProfileToUserprofile typed asOAuth2UserInfo & Record<string, unknown>#9966
ec8a38cThanks @gustavovalverde! - genericOAuth providers configured with adiscoveryUrlnow verify the provider'sid_tokenagainst its published JWKS (signature, issuer, audience, and advertised algorithms) and bind it to the authorization request with a server-generated OIDCnonce. A sign-in whoseid_tokenfails verification, or does not echo the expectednonce, is rejected.Set
disableIdTokenNonceBinding: trueon a provider that does not return thenonceclaim in the authorization-code flow.These providers also accept client-submitted id_token sign-in through
signIn.social({ idToken }), which previously returnedID_TOKEN_NOT_SUPPORTED.Providers configured with explicit endpoints instead of
discoveryUrlare unchanged.#9368
430c895Thanks @GautamBytes! - Generic OAuth users can now sign out from the configured OpenID provider when they callauthClient.signOut(). When a provider exposes a discovered or configured logout endpoint, Better Auth redirects to it and includes the storedid_token_hintwhen available. PasscallbackURLor configurepostLogoutRedirectURIfor the return flow, with optionalstate, or setdisableRedirectto handle the returnedurlyourself. When multiple linked providers support logout, Better Auth selects the most recently updated account. SetdisableProviderLogout: trueto keep sign-out local.#9431
523f95cThanks @pi0! - feat: makeAuthinstance fetchable#10577
5c45abcThanks @gustavovalverde! - MCP clients that hit a scope wall now learn exactly which scopes to ask for. Missing protected scopes produce a403with an RFC 6750insufficient_scopeWWW-Authenticatechallenge that names every missing scope. Clients can union those scopes into one authorization request instead of opening one browser redirect per scope.requiredScopesthroughRequireMcpAuthOptionsor the matchingcreateMcpProtectedRequestHandlerverifier option. Exact membership remains the default;isScopeSatisfiedcan define hierarchical policies.createInsufficientScopeErrorwhen an operation determines its required scopes dynamically.createResourceServerChallengeconverts that signal and recognized token failures into safe RFC 6750 challenges.challengeScopesonly as the unauthenticated challenge hint.Handler-produced responses, ordinary permission denials, configuration failures, and unrelated thrown values keep their original status and identity.
#10403
dbd302eThanks @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.issuerbut preservesAccount.accountIdas the provider-assigned account identifier. Account-specific APIs select the localAccount.idthrough theaccountIdrequest property; token and provider-profile APIs can instead select the signed account cookie withuseAccountCookie: true. Credential accounts uselocal:credentialand the linked user's stableidas their provider identity.OAuth provider identity now comes from raw verified profiles. OpenID Connect discovery uses
sub, plain OAuth usesid, and providers can declareaccountSubjectfor another immutable field; Better Auth no longer switches betweensubandidat runtime.getUserInfo().userno longer carries provider identity, andmapProfileToUsercannot returnid. Read the selected identity fromaccountInfo.account.accountIdinstead ofaccountInfo.user.id. The genericmicrosoftEntraIdhelper 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
subclaim, and SAML uses the signedNameID;mapping.idis removed from both configurations. A manual SAML configuration without metadata XML must setidpMetadata.entityID, becausesamlConfig.issueridentifies 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
8784c1cThanks @ping-maxwell! - Database joins have moved out ofexperimentalinto a stable option atadvanced.database.joins(default:false).If you previously set
experimental: { joins: true }, update your config to: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
e53582cThanks @gustavovalverde! - The MCP plugin moves out ofbetter-authinto 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 (createMcpAuthClientand its adapters) is removed; MCP protocol and transport clients come from the official version 2@modelcontextprotocol/clientand@modelcontextprotocol/serverpackages. The OAuth endpoints move from/mcp/*to/oauth2/*, with discovery at/.well-known/oauth-authorization-serverand 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
withMcpAuthtorequireMcpAuth. The standalone protected-resource factory is renamed frommcpHandlertocreateMcpProtectedRequestHandler; pass one flatMcpProtectedRequestHandlerOptionsobject withissuer, a singleaudience, optionaljwtVerifyOptions, token-verification fields, and challenge fields. Its callback receivesaccessTokenClaims.requireMcpAuthverifies the access token against the published JWKS, validates DPoP proofs for DPoP-bound tokens, and passes the verified access-token claims to your handler.createInsufficientScopeErrornow validates a custom description against the RFC 6750error_descriptioncharacter set when the error is constructed. Invalid descriptions throwTypeError("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, configurecreateMcpHandlerwithlegacy: "reject", wrap it withrequireMcpAuth, and export onlyPOST. Remove MCP-routeGETandDELETEexports and session-store options such asredisUrl. 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 thejwt()plugin, which is now required for token signing; and move options that were nested underoidcConfigto flat options onmcp({ ... }). The database models change:oauthApplicationbecomesoauthClient, with newoauthRefreshTokenandoauthClientAssertiontables. Regenerate or migrate your schema withnpx auth migrateornpx auth generate.#10204
0683a5fThanks @GautamBytes! - Microsoft sign-in now identifies Entra accounts with the stableoidclaim in both the built-inmicrosoftprovider and the Generic OAuthmicrosoftEntraIdhelper. Tokens without a validoidare rejected, and the Generic OAuth helper refuses to initialize unless Microsoft discovery provides ID-token verification metadata. Existing Microsoft account rows created fromsubmust be migrated before upgrading.#9305
e7eb45bThanks @gustavovalverde! - feat(oauth): per-requestadditionalParamsandloginHintparity acrosssignIn.social,linkSocial, andsignIn.ssoUnified 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'sidentity_provider=Google, or Microsoft'sdomain_hintcould only be set as static server configuration.New capabilities
signIn.social,linkSocial, andsignIn.ssoacceptadditionalParams: Record<string, string>. Values are appended to the authorization URL as query parameters.linkSocialalso acceptsloginHint, matching the surface ofsignIn.socialandsignIn.sso.OAuthProvider.createAuthorizationURLgainsadditionalParamsin its input contract; every built-in provider forwards it to the shared helper.additionalParamswith the config-levelauthorizationUrlParams; call-time wins on key collision.identityProvider?: stringconfig option that maps to theidentity_providerquery parameter, avoiding magic strings.Security
createAuthorizationURLhelper silently drops any caller-supplied key inRESERVED_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.nonceis reserved so a caller cannot replace the OIDC nonce Better Auth generates when binding a discovery provider'sid_tokento the authorization request.wechat→appid,tiktok→client_key) additionally filter those keys so a caller cannot swap the configured OAuth app.atlassian→audience,notion→owner) are merged last so caller-suppliedadditionalParamscannot override them. Configured defaults that represent operator intent (e.g. Googleinclude_granted_scopes, CognitoidentityProvider) remain caller-overridable.signIn.ssorejectsadditionalParamswith 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
ZodRecordhandling to the OpenAPI generator soz.record()fields emittype: objectwith typedadditionalProperties. Incidentally fixes a long-standing bug whereadditionalDatawas rendered astype: string.Refactors
discord,roblox,zoom, andslackproviders now delegate to the sharedcreateAuthorizationURLhelper and inherit its RFC behavior and reserved-key guard.tiktokandwechatkeep their manual URL construction (non-standard OAuth2 parameter names and URL fragment requirements) but threadadditionalParamswith the same reserved-key filter.Closes #2351.
Closes #5441.
Closes #5592.
Closes #5604.
Supersedes #4992 and #5443.
#10127
7c7313cThanks @gustavovalverde! - OAuth sign-in, account linking, callback, and proxy flows now buildredirect_urifrom the current request base URL whenbaseURL.allowedHostsis configured. Built-in social providers and generic OAuth providers now use the resolved request host for redirects in multi-host deployments.Custom
OAuthProviderimplementations can omitcallbackPathwhen using the shared/callback/<provider-id>route. SetcallbackPathonly for custom callback routes.#10039
aedcb97Thanks @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_tokensat registration,dpop_jkton the authorization request, or by targeting a resource configured withdpopBoundAccessTokensRequired. Issued tokens carrycnf.jkt, returntoken_type: "DPoP", and stay bound through refresh-token rotation, introspection, and userinfo.Resource servers verify DPoP requests with
verifyAccessTokenRequest, which checks theAuthorization: DPoPscheme, 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.verifyAccessTokenRequestandrequireMcpAuthuse that store by default; build one withcreateDpopReplayStore(internalAdapter)or pass a customdpop.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
verifyAccessTokenis renamed toverifyBearerToken, both inbetter-auth/oauth2and as theoauthProviderResourceClientaction, and it rejects DPoP-bound tokens. UseverifyAccessTokenRequeston any endpoint that may receive them. The resource-request input type is renamed fromAccessTokenRequestInputtoResourceRequestInput, and the DPoP algorithm option issigningAlgorithmseverywhere.Run a schema migration for the DPoP token-binding fields: the
confirmationcolumn on the access-token and refresh-token tables. DPoP-bound clients also gaindpopBoundAccessTokensand resourcesdpopBoundAccessTokensRequired. No dedicated replay table is added; proof replay reuses the verification store.#9828
4f53b61Thanks @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-providerverifyIdTokenmethod. Each provider declares anidTokenconfig 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
idTokenconfig, and the client id_token path returnsID_TOKEN_NOT_SUPPORTED. PayPal sign-in through the redirect flow is unchanged.Custom providers that implement
UpstreamProviderdirectly replace the removedverifyIdTokenmethod with anidTokenconfig:For verification that cannot use a local JWKS, pass
idToken: { verify: async (token, nonce) => boolean }. TheverifyIdTokenanddisableIdTokenSignInprovider options are unchanged.#9079
6f2948eThanks @gustavovalverde! - feat(oauth-provider): computeat_hashin ID tokens per OIDC Core §3.1.3.6ID tokens issued alongside an access token now include the
at_hashclaim, 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 frombetter-auth/pluginsto resolve the current JWKS signing key (including its algorithm). When using a customjwt.signcallback, the signed ID token's header is validated against the declared algorithm to preventat_hashmismatches.#10135 [
f68044d](https://redirect.github.com/betterConfiguration
📅 Schedule: (in timezone America/Toronto)
* 0-3 1 * *)🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.