CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-WXW3-Q3M9-C3JR
6.5

GHSA-WXW3-Q3M9-C3JR: Login CSRF via Insufficient OAuth State Verification in Better Auth

Amit Schendel
Amit Schendel
Senior Security Researcher

May 15, 2026·6 min read·3 visits

PoC Available

Executive Summary (TL;DR)

Better Auth fails to verify the OAuth state parameter during callback processing when using the cookie storage strategy without PKCE, enabling Login CSRF attacks.

Better Auth's OAuth implementation contains a logic flaw in its handling of the state parameter when utilizing the cookie-backed state storage strategy. The application fails to cryptographically bind the generated OAuth state nonce to the stored session metadata, leading to insufficient verification during the callback phase. This omission permits Login Cross-Site Request Forgery (CSRF) and account association attacks when Proof Key for Code Exchange (PKCE) is disabled.

Vulnerability Overview

Better Auth provides authentication solutions for web applications, including comprehensive OAuth 2.0 integrations. The framework supports multiple state storage mechanisms to accommodate different application architectures. One of these mechanisms is a cookie-backed strategy configured via storeStateStrategy: "cookie".

The vulnerability resides in the OAuth 2.0 callback processing mechanism. When configured with the cookie storage strategy, the application fails to adequately validate the state parameter returned by the identity provider's authorization server. The validation logic checks the integrity of the cookie but ignores the incoming state value.

This omission breaks the primary CSRF protection mechanism specified by the OAuth 2.0 standard. Attackers exploit this by forging authentication responses and forcing victim browsers to process attacker-controlled authorization codes. The application accepts the code and executes the authentication flow in the victim's session.

Exploitation requires specific environmental and user conditions. The victim must possess an active, unexpired oauth_state cookie generated by initiating a legitimate authentication attempt. The target application must also be operating without Proof Key for Code Exchange (PKCE) enabled.

Root Cause Analysis

The OAuth 2.0 standard dictates that the state parameter acts as a nonce to bind the authorization request to the specific user agent initiating the flow. The client application must verify that the state returned in the callback exactly matches the state issued in the initial authorization request. This verification prevents CSRF attacks.

During the initial authorization phase, Better Auth generates a state nonce and encrypts session metadata into an oauth_state cookie. This metadata includes configuration details such as the callback URL and expiration time. However, the generated state nonce is omitted from the encrypted payload.

During the callback phase, the parseGenericState function decrypts the oauth_state cookie to retrieve the stored metadata. The application checks the cookie's integrity and verifies that the expiration timestamp is valid. It successfully retrieves the necessary context to continue the OAuth flow.

The critical flaw occurs during the state verification step. The application reads the state query parameter from the callback URL but performs no comparison against the original nonce. Because the original nonce was never stored in the cookie, the framework blindly accepts any state value provided in the request, provided the cookie itself is valid.

Code Analysis

Prior to the patch, the state generation process populated the cookie with metadata but discarded the actual state nonce. The parsing logic subsequently decrypted the cookie and returned the metadata without possessing a reference value to verify the incoming state parameter against. This created a structural blind spot in the authentication flow.

Commit 9deb7936aba7931f2db4b460141f476508f11bfd resolves this flaw by explicitly binding the state nonce to the session data. The generateGenericState function now injects oauthState: state into the data structure before applying encryption. This ensures the reference value travels securely with the session metadata.

The patch introduces strict verification logic within the parseGenericState function. The code extracts the oauthState value from the decrypted cookie and performs an exact equality check against the incoming state parameter. If the values diverge, the application halts the flow:

if (!parsedData.oauthState || parsedData.oauthState !== state) {
    throw new StateError("State mismatch: OAuth state parameter does not match stored state", {
        code: "state_security_mismatch",
        details: { state },
    });
}

The fix extends protection to the broader proxy routing logic. The oAuthProxy is updated to enforce the cryptographic binding. It validates that the newly extracted stateData.oauthState perfectly matches the statePackage.state retrieved from the incoming HTTP request.

Exploitation Methodology

The attack sequence begins with the attacker initiating an OAuth flow on their own device against the target application. The attacker authenticates with the identity provider and captures the resulting callback URL. This URL contains a valid authorization code and an associated state parameter generated for the attacker's session.

The attacker deliberately halts their authentication flow, preventing their own client from consuming the captured authorization code. The attacker then constructs a malicious link using the target application's callback endpoint. This link incorporates the attacker's captured authorization code and arbitrary state parameter.

The attacker delivers this crafted link to the victim. The victim must possess a valid, unexpired oauth_state cookie for the target application. This cookie is naturally generated if the victim recently initiated an OAuth sign-in flow, even if they abandoned the process before completion.

When the victim's browser accesses the crafted link, the target application receives the request. The application reads the victim's valid cookie, bypasses the state verification, and processes the attacker's authorization code. The application authenticates the victim as the attacker or binds the attacker's external identity to the victim's profile.

Impact Assessment

The primary consequence of this vulnerability is Login Cross-Site Request Forgery (Login CSRF). An attacker forces a victim to log into the application under the attacker's account context. The victim unknowingly interacts with the application utilizing the attacker's profile and settings.

Login CSRF introduces severe data leakage and privacy risks. If the victim inputs sensitive information while operating under the attacker's account, the attacker retains full access to that data. This encompasses payment details, personal identification information, or proprietary documents uploaded during the session.

The vulnerability enables account association attacks in specific application configurations. If the application permits linking multiple identity providers to a single internal account, the flaw allows an attacker to bind their own identity provider account to the victim's application account. The attacker then secures persistent, unauthorized access to the victim's actual profile.

The vulnerability carries a CVSS v3.1 base score of 6.5, categorizing it as medium severity. The requirement for user interaction and the prerequisite of an active oauth_state cookie constrain the attack surface. Furthermore, applications utilizing the database storage strategy or enforcing PKCE are structurally immune.

Remediation

The definitive remediation involves upgrading the better-auth library to a version incorporating commit 9deb7936aba7931f2db4b460141f476508f11bfd. This patch enforces strict state verification during the callback parsing phase. Upgrading eliminates the vulnerability without requiring architectural changes.

Configuration adjustments provide effective workarounds for environments where immediate patching is unfeasible. Administrators must alter the storeStateStrategy configuration directive. Transitioning to the database strategy relies on primary key lookups rather than client-side cookies, inherently mitigating this specific CSRF vector.

Implementing Proof Key for Code Exchange (PKCE) offers a robust secondary defense layer. PKCE binds the authorization code to a specific client session via a cryptographically secure verifier generated during the initial request. Enabling PKCE via pkce: true prevents the attacker from successfully injecting an authorization code generated in a disparate session.

Security and operations teams should monitor authentication logs for anomalous patterns. The presence of state_security_mismatch errors indicates the application successfully blocked an invalid state transition. A sudden influx of these errors from distinct IP addresses signals potential exploitation attempts or systemic misconfigurations.

Official Patches

Better AuthFix commit for OAuth state mismatch

Fix Analysis (1)

Technical Appendix

CVSS Score
6.5/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N

Affected Systems

Applications using `better-auth` with `storeStateStrategy: "cookie"` and without PKCE enabled.

Affected Versions Detail

Product
Affected Versions
Fixed Version
better-auth
Better Auth
< 9deb7936aba7931f2db4b460141f476508f11bfdPost-commit 9deb7936aba7931f2db4b460141f476508f11bfd
AttributeDetail
CWE IDCWE-352, CWE-345
Attack VectorNetwork
CVSS Base Score6.5
ImpactLogin CSRF / Account Hijacking
Exploit StatusProof of Concept Available
Authentication RequiredNone

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1550Use Alternate Authentication Material
Defense Evasion
CWE-352
Cross-Site Request Forgery (CSRF)

The application fails to sufficiently verify the authenticity of data, allowing an attacker to execute unintended actions on behalf of the user.

Vulnerability Timeline

Vulnerability discovered by Gustavo Valverde
2026-04-09
Patch committed to main branch
2026-04-09
GitHub Advisory published
2026-04-09

References & Sources

  • [1]GitHub Advisory: Better Auth OAuth callback accepts mismatched state
  • [2]Better Auth Security Advisories
  • [3]Fix Commit

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.