Aug 31, 2026·6 min read·83 visits
A logical error in @hono/oauth-providers allows attackers to bypass OAuth state checks when the state is omitted on both sides, leading to unauthenticated login CSRF and forced account linking.
An authentication bypass vulnerability in @hono/oauth-providers prior to version 0.8.6 allows unauthenticated remote attackers to perform login Cross-Site Request Forgery (CSRF) and forced account linking. Due to a logical 'fail-open' comparison flaw, the middleware validates OAuth callbacks when the state parameter is omitted from both the client cookie and the request query parameters, completely bypassing standard anti-CSRF protections.
The @hono/oauth-providers package is an authentication middleware library designed for the Hono web framework. It provides developers with ready-to-use integrations for popular social identity providers. In a standard deployment, this middleware handles both the initial authorization redirect and the subsequent token exchange callback within the OAuth 2.0 flow.
Prior to version 0.8.6, the library exposed a critical vulnerability in its callback validation mechanism. By failing to ensure that anti-CSRF state tokens were actually present before comparing them, the middleware introduced a bypass on its callback endpoints. This allowed unauthenticated external actors to exploit the authentication state of arbitrary users.
The vulnerability is classified under CWE-352 (Cross-Site Request Forgery) and CWE-1275 (Sensitive Cookie with Improper SameSite Attribute). It affects multiple built-in social login integrations, including Google, GitHub, Facebook, Discord, Twitch, LinkedIn, and MS Entra. The X (formerly Twitter) provider is not vulnerable due to its native implementation of Proof Key for Code Exchange (PKCE).
To secure OAuth callbacks against request forgery, applications generate a cryptographically secure random string, store it in an HTTP cookie, and transmit it to the provider in the authorization request. Upon successful authentication, the identity provider redirects the user back to the application with this exact state parameter. The application must then strictly verify that the incoming parameter matches the value stored inside the cookie.
In affected versions of @hono/oauth-providers, this validation was structured around a simple inequality check. If the browser initiated a request without a preceding login flow, the local state cookie did not exist, evaluating to undefined. Similarly, if the attacker engineered a callback request that omitted the state parameter entirely, the incoming parameter also resolved to undefined.
The middleware executed the comparison c.req.query('state') !== storedState to detect discrepancies. When evaluating undefined !== undefined in JavaScript or TypeScript, the expression resolves to false. Because the inequality condition was false, the middleware bypassed the safety block and assumed verification had succeeded.
This flaw was exacerbated by a weak activation heuristic that only triggered the check if the request URL contained a question mark (?). Additionally, standard global CSRF middleware failed to mitigate this vector. Such protections usually ignore top-level HTTP GET navigations, which are the standard method for processing OAuth redirects.
The vulnerability resided in the individual provider files (such as googleAuth.ts, githubAuth.ts, and discordAuth.ts). The comparison and cookie initialization logic allowed for passive validation bypass and exposed state secrets across origins.
Below is the comparison of the vulnerable implementation versus the patched codebase:
// VULNERABLE CODE PATH (0.8.5 and prior)
if (c.req.url.includes('?')) {
const storedState = getCookie(c, 'state')
if (c.req.query('state') !== storedState) {
throw new HTTPException(401)
}
}// PATCHED CODE PATH (0.8.6+)
if (auth.code) {
const storedState = getCookie(c, 'state')
const state = c.req.query('state')
if (!storedState || !state || state !== storedState) {
throw new HTTPException(401)
}
}By refactoring the activation hook to if (auth.code), the validator fires only when an authorization token is actively presented for exchange. The corrected logic checks both strings for truthiness (!storedState || !state) before performing the string comparison. If either the cookie or the query parameter is absent, the execution fails closed.
The patch also hardened the cookie configuration. The previous deployment left secure: true commented out and omitted the sameSite attribute. The updated code enforces secure: true and sameSite: 'Lax', protecting the state token from cross-site leakage during navigation.
An attacker can exploit this vulnerability through two primary attack vectors: Login CSRF or Forced Account Linking. Both approaches rely on driving a target user to a specially crafted link that performs a top-level GET navigation to the callback endpoint.
In a Login CSRF scenario, the attacker begins by initiating a genuine OAuth session using their own provider credentials. They intercept the redirect request to capture their own valid authorization code, preventing their own browser from executing it. They then construct a malicious URL targeting the victim's browser, completely omitting the state query parameter:
https://target-app.com/auth/discord/callback?code=ATTACKER_CODE
When the victim visits this link, the application processes the request. Because the victim did not start an OAuth session, no state cookie exists in their browser. The validation logic evaluates undefined !== undefined, passes validation, and associates the victim's session with the attacker's social identity.
In a Forced Account Linking scenario, the attacker intercepts a code intended to link an identity. They force the victim to execute the callback, linking the attacker's credentials to the victim's pre-existing profile. The attacker can then authenticate directly to the victim's account.
The impact of this vulnerability is assessed at Medium severity with a CVSS 3.1 score of 5.4 (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N). The vulnerability requires user interaction to succeed, as a victim must click an attacker-controlled link or navigate to a compromised landing page.
If exploited, the confidentiality and integrity of the victim's user data are compromised. Under a Login CSRF attack, the victim is forced into an account session controlled by the attacker. Any sensitive actions, billing information, or private communications entered by the victim during this period are visible to the attacker.
If used to execute forced account linking, the impact escalates to complete account takeover. The attacker links their own social identity to the victim's target account, establishing a persistent back door. This allows the attacker to log into the victim's account at will, bypassing normal authentication credentials.
To fully resolve this vulnerability, security administrators must upgrade @hono/oauth-providers to version 0.8.6 or higher. The update overwrites the vulnerable validation files and implements the fail-closed logic across all supported authentication backends.
In environments where upgrading dependencies immediately is not possible, a pre-validation middleware can serve as an effective mitigation. This custom middleware must inspect incoming requests to the callback URLs and verify that if an authorization code is present, a state parameter must also be supplied in the query string:
app.get('/auth/google/callback', async (c, next) => {
const state = c.req.query('state')
const code = c.req.query('code')
if (code && !state) {
return c.text('Unauthorized: State parameter missing', 401)
}
await next()
})Security teams can detect potential exploitation by scanning web server logs for requests sent to OAuth callback endpoints that contain a code parameter but lack a state parameter. Since legitimate client integrations always submit a state, the absence of this variable indicates a high-probability attack attempt.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
@hono/oauth-providers honojs | < 0.8.6 | 0.8.6 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-352 |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 Score | 5.4 (Medium) |
| Exploit Status | Proof of Concept (PoC) available |
| CISA KEV Status | Not Listed |
| Affected Providers | google, github, facebook, discord, twitch, linkedin, msentra |
The web application does not sufficiently verify whether a request was intentionally sent by the user, enabling attackers to perform actions on behalf of the victim.
A path traversal vulnerability (CWE-22) in Contao CMS allows unauthenticated remote attackers to bypass directory boundary restrictions in ImagesController and access files within the project directory.
Contao Open Source CMS versions 4.0.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 contain a Cross-Site Request Forgery (CSRF) vulnerability in backend parameter handling. The `RequestTokenListener` component validates anti-CSRF tokens solely for HTTP POST requests, while GET-based declarative guards run only when an `act` parameter is present in the query string. Consequently, custom backend actions dispatched via alternative parameters such as `key=` can execute without CSRF token verification when triggered by an authenticated user.
Contao CMS versions 4.1.0 through 5.3.49 and 5.4.0-RC1 through 5.7.11 fail to validate form submission tokens and enforce rate limiting when processing activation email resend requests via HTTP POST, enabling resource exhaustion and account state enumeration.
An information disclosure vulnerability in Contao CMS allows unauthenticated site visitors to view protected page titles, URLs, and text excerpts through search queries when protected page indexing is disabled after previously being enabled.
In Vikunja prior to version 2.6.0, relation creation via the CalDAV endpoint fails to invoke the TaskRelation.CanCreate authorization check. This missing access control allows an authenticated user to establish unauthorized relationships and perform write operations against any task, provided its unique identifier (UID) is known.
A cross-project information disclosure vulnerability in Vikunja allows authenticated users with read access to one project to view private task details from unauthorized projects via subtask expansion parameters.