Open actionable advisories and recorded vendor fixes. This public package view has no inventory attached and cannot establish whether your deployment is affected. A vendor fix never resolves your finding; verify your installed version and the source guidance.
Open advisories
26 actionable advisories, newest first. Withdrawn advisories and news are excluded. Coverage is partial.
Am I affected? You are affected if you use `@better-auth/drizzle-adapter` from 1.6.17 through versions before 1.7.7 (including the 1.7.0 prereleases from beta.6 through rc.6) or `@better-auth/kysely-adapter` 1.6.17 through 1.6.31, and keep rate-limit records in a PostgreSQL database. All of these must be true: - Rate limiting is enabled. - Rate-limit records are stored in the database. - The database is PostgreSQL, used through one of the adapter versions above. You are not affected if you store rate-limit records in memory (the default) or in secondary storage. Summary Database-backed rate limiting on PostgreSQL can let a burst of concurrent requests go over the configured maximum. When many requests arrive at the same time, several of them can pass the limit check together. This weakens the rate limit on password sign-in, two-factor verification, one-time codes, password resets, and other rate-limited endpoints. Impact An attacker who sends many requests at once can make…
Recommended action: Upgrade to 1.7.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Am I affected? You may be affected if you use `better-auth` from 1.5.0-beta.12 through versions before 1.7.7 with the OAuth Proxy plugin. You are affected only if all of these are also true: - On a participating environment, the OAuth Proxy plugin has no dedicated `secret`, or its `secret` equals that environment's global Better Auth secret. - Sign-in state is stored in a cookie (`account.storeStateStrategy: "cookie"`). With a database or secondary storage, this is only the case if you chose it explicitly. - Implicit account linking is on (the default). You are not affected by this path if every participating environment uses a proxy `secret` distinct from its global Better Auth secret, if sign-in state is stored in the database, or if you use a version before 1.5.0-beta.12. Summary The OAuth Proxy plugin trusts any user profile it can decrypt. In the affected setup, Better Auth encrypts sign-in state, including data the caller chose, with the same secret and hands it to…
Recommended action: Upgrade to 1.7.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Am I affected? You may be affected if you use `better-auth` from `1.4.0-beta.18` through versions before `1.7.7` with the Magic Link plugin and social or Generic OAuth sign-in. You are affected if all of these are true: - Magic Link is enabled with default `storeToken: "plain"`, or with `storeToken: "hashed"` and global `verification.storeIdentifier: "hashed"` (including a hashed default). - At least one built-in social provider or Generic OAuth provider is enabled. - OAuth state uses `account.storeStateStrategy: "database"`, the default when a database or secondary storage is configured. Cookie-backed state blocks this path, subject to the OAuth Proxy condition below. With custom hashers, confirm the flows cannot resolve to the same stored identifier. Summary Better Auth accepts an OAuth sign-in state value as a Magic Link token. An attacker can start an OAuth sign-in with any email address and use the returned state value to finish a Magic Link sign-in for that address.…
Recommended action: Upgrade to 1.7.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Am I affected? You may be affected if you use `better-auth` from `1.3.8-beta.3` up to, but not including, `1.7.0-rc.3`, and you enable the `deviceAuthorization()` plugin. This range includes all 1.6 releases. You are affected only if all of these are also true: - An attacker can start a device request: `validateClient` is not configured, or the attacker can use a valid public client. - Before allowing approval, your approval page does not show, from server-checked data, which client is asking and the exact permissions it requests. - Users may approve a code or complete verification link they did not start and cannot compare with a device they have. - Your application accepts the session token that the device flow returns, for example through the bearer plugin. You are not affected if you do not enable the opt-in `deviceAuthorization()` plugin. Summary The device authorization plugin lets a user sign in on one device by approving a short code on another. In affected versions,…
Recommended action: Upgrade to 1.7.0-rc.3 (pre-release) or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Am I affected You are affected if all of the following hold: - You run a better-auth version below 1.6.22, or a 1.7.0-beta below 1.7.0-beta.10. - You enable the magic-link plugin or the email-OTP plugin. - You also enable email and password sign-up with open registration. - An account can exist at an address before its owner first signs in with the passwordless flow. Summary An attacker can keep password access to a victim's account after the victim starts using it. The attack runs in three steps. First, with open registration, the attacker signs up using the victim's email and a password the attacker picks. The account stays unverified, so the attacker cannot use it yet. Later the real owner signs in with a magic link or an email OTP. That step marks the account verified, and the attacker's password now works on it. Details When an account already exists for an address, magic-link verification and email-OTP sign-in both sign in to that account. They mark it verified and issue a…
Recommended action: Upgrade to 1.7.0-beta.10 (pre-release) or 1.6.22 by release line, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Am I affected? Users are affected if all of the following are true: - They configure secondaryStorage on betterAuth(...) (Redis, KV, or any external session cache). - session.storeSessionInDatabase is left unset or set to false (the default). - Their application's deployment uses one or more of: - The admin plugin and calls auth.api.removeUser(...) or authClient.admin.removeUser(...). - The anonymous plugin and exposes /delete-anonymous-user or relies on the after-link hook to clean up the anonymous user. - The @better-auth/scim plugin and exposes DELETE /scim/v2/Users/:userId. If storeSessionInDatabase is true, sessions are also written to the database, and the database delete cascades; users are not affected. Fix: 1. Upgrade to better-auth@<patched-version or later (and @better-auth/scim@<patched-version if they use SCIM). 2. If they cannot upgrade, see workarounds below. Summary When secondaryStorage is configured and storeSessionInDatabase is false, three user-deletion endpoints…
Recommended action: Upgrade to 1.6.11 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Am I affected? Users are affected if all of the following are true: - Their project depends on @better-auth/oauth-provider at a version = 1.6.0, < 1.6.11, or uses the embedded plugin in better-auth = 1.4.8-beta.7, < 1.6.0, or enables the legacy oidc-provider or mcp plugins from better-auth/plugins. - Their application exposes /api/auth/oauth2/token (or the legacy plugins' /oauth2/token and /mcp/token) as a token endpoint to OAuth/OIDC clients, including internal MCP clients (Claude Desktop, custom MCP tool callers, AI agents). - Their application has not implemented an external mitigation: a load-balancer-level idempotency cache keyed by code, a database trigger that rejects duplicate token issuance for the same authorization code, or a custom adapter override that performs an atomic compare-and-delete. Fix: 1. Upgrade to @better-auth/oauth-provider@1.6.11 or later. If developers use the legacy plugin paths from better-auth/plugins, upgrade better-auth to 1.6.11 or later. 2. If…
Recommended action: Upgrade to 1.6.11 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Am I affected? Users are affected if all of the following are true: - Their project depends on @better-auth/oauth-provider at a version = 1.6.0, < 1.6.11, or uses the embedded plugin in better-auth = 1.4.8-beta.7, < 1.6.0. - At least one OAuth client served by their application's authorization server requests the offlineaccess scope, so refresh tokens are minted. - Concurrent redemption of the same refresh token is reachable: an SPA shares one refresh token across browser tabs without a mutex, a mobile client retries after a transient failure, an attacker who has stolen a refresh token times two requests, or a service worker queues offline requests. If developer applications do not request offlineaccess for any client, no refresh tokens are minted and they are not exposed. Fix: 1. Upgrade to @better-auth/oauth-provider@1.6.11 or later. 2. If developers cannot upgrade, see workarounds below. Summary The OAuth provider's POST /oauth2/token endpoint, on the refreshtoken grant, performs…
Recommended action: Upgrade to 1.6.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Am I affected? Users are affected if all of the following are true: - Their application uses better-auth at a version below the patched release. - Their application enables oidcProvider() from better-auth/plugins/oidc-provider or mcp() from better-auth/plugins/mcp (the mcp plugin delegates to oidcProvider and inherits both defaults). - For the algorithm-negotiation impact: relying parties of the application's OIDC server use a JWT verification library that performs algorithm negotiation from the discovery document without pinning to a specific signing algorithm. - For the PKCE impact: the authorization URL is exposed to any party other than the user agent and the application's OP. If the application only uses @better-auth/oauth-provider (the canonical replacement) and have not enabled the legacy plugins, it is not affected. The new package's discovery document excludes none and its authorize schema rejects plain at parse time. Fix: 1. Upgrade to better-auth@1.6.11 or later. 2.…
Recommended action: Upgrade to 1.6.11 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Am I affected? Check each condition. Users are affected when all of the first three hold. - Their application enables the oidc-provider plugin or the mcp plugin from better-auth/plugins. The mcp plugin wraps the same provider and carries the same defect. Both are on the migration path to @better-auth/oauth-provider, which is not affected. - Their application better-auth version is 1.6.12 or earlier on the stable line, or any 1.7.0-beta build on the pre-release line. Both release lines carry the same defect. - Their application consent page is a client component that reads redirectURI from the authClient.oauth2.consent(...) response and assigns it to a browser navigation target such as window.location.href, location.assign, or location.replace. Two conditions raise an application's exposure: - It sets allowDynamicClientRegistration: true. Client registration is then unauthenticated, so any visitor can plant the malicious client. The default is false, which limits planting to…
Recommended action: Upgrade to 1.7.0-beta.4 (pre-release) or 1.6.13 by release line, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Am I affected? Users are affected if all of the following are true: - Their application uses better-auth at a version < 1.6.11 on the stable line, or any current next pre-release. - emailAndPassword.enabled: true is set in their application's betterAuth({ ... }) configuration. - At least one OAuth or SSO provider is configured (any built-in social provider, or genericOAuth(...), or any provider via @better-auth/sso). - account.accountLinking.disableImplicitLinking is not set to true. - account.accountLinking.enabled is not set to false. Setting either disableImplicitLinking: true or enabled: false closes the hole at the cost of breaking the standard "add another login method" UX. emailAndPassword.requireEmailVerification: true does not mitigate, because the link-time emailVerified flip promotes the attacker's row to verified, after which the password login becomes usable. Fix: 1. Upgrade to better-auth@1.6.11 or later. 2. If developers cannot upgrade, see workarounds below. Summary…
Recommended action: Upgrade to 1.6.11 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Am I affected? Users are affected if all of the following are true: - Their application uses better-auth with the organization plugin (import { organization } from "better-auth/plugins/organization"). - Their application enables a sign-up surface that allows arbitrary unverified email registration. Most commonly emailAndPassword: { enabled: true } without requireEmailVerification: true. - Their application has not set requireEmailVerificationOnInvitation: true on the organization() options. - Their application invitation distribution flow allows anyone other than the invited mailbox owner to obtain the invitationId. Examples: admin UI surfacing the link, copy-paste into chat, forwarded email, mail-forwarding rules at the recipient's domain, link previews logging the URL, or a custom sendInvitationEmail integration that sends to a non-owner channel. If their application set emailAndPassword: { enabled: true, requireEmailVerification: true } so unverified rows cannot reach a usable…
Recommended action: Upgrade to 1.6.11 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Am I affected? Users are affected if all of the following are true: - Their application uses better-auth and has enabled at least one of: oidcProvider() (imported from better-auth/plugins/oidc-provider), or mcp() (imported from better-auth/plugins/mcp). - Their application has at least one confidential OAuth client registered (any client with type: "web" | "native" | "user-agent-based" in the oauthApplication table, or any trustedClients entry without type: "public"). Public clients with PKCE are not affected. - Their application uses better-auth at a version below the patched release. If an application only uses @better-auth/oauth-provider (the canonical replacement for oidc-provider) and the mcp plugin is not enabled, it is not affected. Fix: 1. Upgrade to better-auth@1.6.11 or later. 2. Migrate from the deprecated oidcProvider() to @better-auth/oauth-provider when feasible. The new package enforces client authentication on both grants by default. 3. If developers cannot upgrade…
Recommended action: Upgrade to 1.6.11 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Am I affected? You are affected if all of the following are true: - You use better-auth at a version = 1.6.0, < 1.6.11. - The deviceAuthorization plugin is enabled in your auth config (deviceAuthorization() in your plugins array). - A third party can observe a pending user code before the legitimate user completes verification. The standard device-flow UX displays user codes to humans, so realistic exposure includes shoulder-surfing, screen-share, voice or video calls, support-chat transcripts, referrer headers, and shared logs. If your application does not enable the deviceAuthorization plugin, you are not affected. Fix: 1. Upgrade to better-auth@1.6.11 or later. 2. If you cannot upgrade, see workarounds below. Summary Better Auth's deviceAuthorization plugin treated any authenticated session as the owner of any pending device code. The ownership gate on POST /device/approve and POST /device/deny short-circuited whenever the row's userId was unset, and the GET /device verification…
Recommended action: Upgrade to 1.6.11 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Am I affected? Users are affected if all of the following are true: - Their app uses better-auth at a version < 1.4.17, or at a v1.5 prerelease tagged <= 1.5.0-beta.8. - The apps authentication endpoints serve clients reachable over IPv6. Most managed hosts including Cloudflare, Vercel, Fly.io, AWS Application Load Balancer, and Google Cloud Load Balancing advertise IPv6 by default. - The app's rate-limit configuration is enabled (the production default) and relies on the leftmost x-forwarded-for value (the stock setup) or any other configured IP-bearing header. If users are on 1.4.16 specifically, the normalizeIP helper exists in your version but the IPv6 prefix length defaults to /128. Stock config still permits prefix rotation because no prefix mask is applied. Either upgrade to 1.4.17 or set advanced.ipAddress.ipv6Subnet: 64 in the config. If applications do not use the rate limiter, or if the deployment serves only IPv4 clients, the prefix-rotation vector does not apply. The…
Recommended action: Upgrade to 1.5.0-beta.9 (pre-release) or 1.4.17 by release line, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Am I affected? Users are affected if all of the following are true: - The application uses better-auth at a version below 1.6.2 (or @better-auth/sso paired with such a version). - betterAuth({ account: { storeStateStrategy } }) is set to "cookie". The default "database" is not affected. - The application wires at least one OAuth provider through genericOAuth({ config }) with pkce: false, or it supplies a custom getToken or tokenUrl that does not require the stored codeVerifier. Stock social providers with PKCE on are not affected. - The provider returns arbitrary code values to the configured callback URL. If users are on better-auth@1.6.2 or later, they are not affected. Fix: 1. Upgrade to better-auth@1.6.2 or later (current stable is 1.6.10). 2. If users cannot upgrade, see workarounds below. Summary In parseGenericState, the cookie branch decrypted the oauthstate cookie and validated expiry, but did not compare the incoming OAuth state query parameter to the nonce that…
Recommended action: Upgrade to 1.6.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Under certain configurations, sessions may be considered valid before two-factor authentication (2FA) is fully completed. This can allow access to authenticated routes without verifying the second factor. --- Description When two-factor authentication is enabled, the authentication flow correctly identifies users who require additional verification and defers full authentication until the second factor is completed. However, when session.cookieCache is enabled, the session generated during the initial sign-in step may be cached as valid prior to 2FA verification. Subsequent session lookups may then return this cached session without re-evaluating the 2FA requirement. This results in a situation where session validity can be established before all authentication constraints are satisfied. --- Impact An attacker (or user) with valid primary credentials may gain access to protected application routes without completing the required second authentication factor. Any application…
Recommended action: Upgrade to 1.4.9 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
An issue in the underlying router library rou3 can cause /path and //path to be treated as identical routes. If your environment does not normalize incoming URLs (e.g., by collapsing multiple slashes), this can allow bypasses of disabledPaths and path-based rate limits. Details Better Auth uses better-call, which internally relies on rou3 for routing. Affected versions of rou3 normalize paths by removing empty segments. As a result: /sign-in/email //sign-in/email ///sign-in/email …all resolve to the same route. Some production setups automatically collapse multiple slashes. This includes: Vercel with Nextjs (default) Cloudflare - when normalize to urls origin is enabled (https://developers.cloudflare.com/rules/normalization/settings/#normalize-urls-to-origin) In these environments and other configurations where //path reach Better Auth as /path, the issue does not apply. Fix Updating rou3 to the latest version resolves the issue: better-call previously depended on "rou3":…
Recommended action: Upgrade to 1.4.5 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Affected versions of Better Auth allow an external request to configure baseURL when it isn’t defined through any other means. This can be abused to poison the router’s base path, causing all routes to return 404 for all users. This issue is only exploitable when baseURL is not explicitly configured (e.g., BETTERAUTHURL is missing) and the attacker is able to make the very first request to the server after startup. In properly configured environments or typical managed hosting platforms, this fallback behavior cannot be reached. Details A combination of X-Forwarded-Host and X-Forwarded-Proto is implicitly trusted. This allows the first request to configure baseURL whenever it is not explicitly configured. Here's the code that reads the headers: <img width="631" height="219" alt="headers" src="https://github.com/user-attachments/assets/b3fb0078-a62f-4058-9d0b-4afbd30c4953" / Here's the call to getBaseURL(), the result is assigned to ctx.baseURL. <img width="838" height="414"…
Recommended action: Upgrade to 1.4.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
A vulnerability was identified in the multi-session plugin for Better Auth, specifically in the /sign-out after-hook. The hook trusts raw multi-session cookies and forwards the extracted values directly to internalAdapter.deleteSessions without verifying the cookie signature. Because cookie values are not validated with getSignedCookie (or any equivalent check), an attacker can supply a forged multi- cookie to trigger deletion of arbitrary session tokens.
Recommended action: Upgrade to 1.4.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
A critical authentication bypass was identified in the API key creation and update endpoints. An attacker could create or modify API keys for arbitrary users by supplying a victim’s user ID in the request body. Due to a flaw in how the authenticated user was derived, the endpoints could treat attacker-controlled input as an authenticated user object under certain conditions. Details The vulnerability originated from fallback logic used when determining the current user. When no session was present, the handler incorrectly allowed request-body data to populate the user context used for authorization decisions. Because server-side validation only executed when authentication was required, privileged fields were not properly protected. As a result, the API accepted unauthenticated requests that targeted other users. This same pattern affected both the API key creation and update routes. Impact Unauthenticated attackers could generate or modify API keys belonging to any user.…
Recommended action: Upgrade to 1.3.26 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
An open redirect has been found in the originCheck middleware function, which affects the following routes: /verify-email, /reset-password/:token, /delete-user/callback, /magic-link/verify, /oauth-proxy-callback. Details In the matchesPattern function, url.startsWith( can be deceived with a url that starts with one of the trustedOrigins. Open Redirect PoCs /reset-password/:token <img width="481" alt="image" src="https://github.com/user-attachments/assets/46e7871a-1dad-4375-af94-0446e29aaab6" / <br/ <img width="518" alt="image 1" src="https://github.com/user-attachments/assets/83abfb53-6fc9-4d1f-918d-9b4ce093c808" / /verify-email <img width="549" alt="image" src="https://github.com/user-attachments/assets/7dd424b7-42a4-4616-aa73-fcc2e3eeb309" / <br/ <img width="436" alt="image" src="https://github.com/user-attachments/assets/54f11636-0a3e-4e83-9a09-57c5e8ba98cd" / /delete-user/callback <img width="545" alt="image"…
Recommended action: Upgrade to 1.2.10 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
A bypass was discovered in the trustedOrigins validation logic—affecting both absolute URL entries and wildcard domain patterns. This flaw allows an attacker to construct a malicious callbackURL that passes origin checks and triggers an open redirect. Because redirect endpoints include sensitive tokens (such as password-reset tokens), this vulnerability can enable one-click account takeover if a victim clicks a crafted link.
Recommended action: Upgrade to 1.1.21 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
The application is vulnerable to an open redirect due to improper validation of the callbackURL parameter in the email verification endpoint and any other endpoint that accepts callback url. While the server blocks fully qualified URLs (e.g., https://evil.com), it incorrectly allows scheme-less URLs (e.g., //malicious-site.com). This results in the browser interpreting the URL as https://malicious-site.com, leading to unintended redirection. bypass for : https://github.com/better-auth/better-auth/security/advisories/GHSA-8jhw-6pjj-8723 Affected Versions All versions prior to 1.1.19 Details The application’s email verification endpoint (/auth/verify-email) accepts a callbackURL parameter intended to redirect users after successful email verification. While the server correctly blocks fully qualified external URLs (e.g., https://evil.com), it improperly allows scheme-less URLs (e.g., //malicious-site.com). This issue occurs because browsers interpret //malicious-site.com as…
Recommended action: Upgrade to 1.1.20 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
The better-auth /api/auth/error page was vulnerable to HTML injection, resulting in a reflected cross-site scripting (XSS) vulnerability. Details The value of error URL parameter was reflected as HTML on the error page: https://github.com/better-auth/better-auth/blob/05ada0b79dbcac93cc04ceb79b23ca598d07830c/packages/better-auth/src/api/routes/error.ts#L81 Impact An attacker who exploited this vulnerability by coercing a user to visit a specially-crafted URL could execute arbitrary JavaScript in the context of the user's browser.
Recommended action: Upgrade to 1.1.16 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.