Apr 20, 2026·7 min read·27 visits
A logic flaw in Nhost's OAuth implementation allows attackers to take over existing accounts by registering an unverified matching email address on third-party identity providers like Discord or Bitbucket.
Nhost is vulnerable to a critical Improper Authentication flaw (CWE-287) that permits full account takeover. The vulnerability exists in the OAuth authentication flow, where multiple provider adapters fail to enforce email verification checks before automatically linking incoming external identities to existing local accounts.
Nhost provides an open-source backend utilizing Go for its authentication services. The platform supports federated authentication, allowing users to sign in via third-party OAuth providers such as Discord, Bitbucket, AzureAD, and EntraID. A core feature of this authentication subsystem is automated account linking. When an incoming OAuth identity presents an email address that matches an existing local user record, Nhost automatically merges the profiles and provisions an authenticated session.
This architecture fundamentally relies on the assumption that external identity providers rigorously verify email ownership before returning the profile payload to the application. However, multiple OAuth adapters within the Nhost codebase failed to validate the verification status flag provided by the external services. This oversight introduces a severe CWE-287 (Improper Authentication) vulnerability into the sign-in flow.
Because the verification flag is ignored, an attacker can register an account on a supported third-party service using a target victim's email address. The attacker does not need to complete the email confirmation process on the external platform. When the attacker initiates an OAuth login flow into the target Nhost application, the system ingests the unverified email, executes the automated account linking procedure, and grants the attacker a valid session belonging to the victim.
The underlying flaw exists at the intersection of adapter-level data parsing and controller-level business logic. The providerFlowSignIn function located in services/auth/go/controller/sign_in_id_token.go is responsible for handling incoming identity profiles. This function executed the InsertUserProvider method to link accounts based exclusively on an email match. The logic did not evaluate the profile.EmailVerified boolean before proceeding with the linking and session generation operations.
Compounding this controller-level deficiency, several individual provider adapters improperly managed the verification state during the initial data ingestion phase. The Discord and EntraID adapters mapped incoming JSON payloads into internal structs that omitted the verified and email_verified fields entirely. Due to these omissions, the Go JSON decoder silently discarded the true verification state broadcast by the provider API. The adapters subsequently defaulted the EmailVerified property to true whenever an email string was present in the payload.
Other adapters exhibited distinct but equally problematic behaviors. The Bitbucket adapter executed a fallback mechanism that accepted unconfirmed email addresses if no confirmed addresses were available in the user profile, explicitly marking these unconfirmed addresses as verified. The AzureAD adapter utilized unverified fields, such as preferred_username or User Principal Name (UPN), as the primary email source when the standard email field was empty. The combination of these adapter inaccuracies and the controller's unconditional acceptance logic enabled the authentication bypass.
The pre-patch implementation of providerFlowSignIn bypassed necessary authorization checks during the OAuth callback phase. The controller unconditionally registered the provider linkage and issued a new session. The vulnerable implementation is shown below:
func (ctrl *Controller) providerFlowSignIn(
ctx context.Context,
user sql.AuthUser,
providerFound bool,
provider string,
providerUserID string,
logger *slog.Logger,
) (*api.Session, *APIError) {
if !providerFound {
// profile.EmailVerified is NEVER checked prior to this insertion
ctrl.wf.InsertUserProvider(ctx, user.ID, provider, providerUserID, logger)
}
session, _ := ctrl.wf.NewSession(ctx, user, nil, logger)
return session, nil
}The fix, introduced in commit ec8dab3f2cf46e1131ddaf893d56c37aa00380b2, addresses both the type system and the control flow. The developers replaced the binary EmailVerified bool with a strict EmailVerificationStatus enumeration (Unknown, Verified, Unverified). This structural change forces adapters to explicitly declare the verification state rather than relying on boolean zero-value defaults.
Additionally, the patch introduced the ensureProviderLinkAllowed guard function. This routine is now invoked before any database operations occur. If the provider profile does not explicitly carry a verified status, the application aborts the authentication flow:
func (ctrl *Controller) ensureProviderLinkAllowed(
ctx context.Context,
profile oidc.Profile,
provider string,
logger *slog.Logger,
) *APIError {
if profile.EmailVerified.IsVerified() {
return nil
}
logger.WarnContext(ctx, "refusing to link provider: email not verified by provider")
return ErrUnverifiedUser
}This fix is robust because it centralizes the security enforcement within the main controller while simultaneously correcting the data extraction logic inside the specific provider adapters.
Exploiting this vulnerability requires zero interaction from the target victim and necessitates only public knowledge of the victim's registered email address. The attacker first selects a vulnerable OAuth provider supported by the target Nhost application, such as Discord. The attacker creates a new account on this third-party provider or modifies an existing account, updating the profile email to match the target victim's email address.
Crucially, the external provider will dispatch an email confirmation link to the victim's inbox. The attacker ignores this step and leaves the email address unverified within the provider's ecosystem. The attacker then accesses the target Nhost application and initiates the "Sign in with Discord" OAuth flow.
The Nhost application receives the profile payload from Discord. Due to the missing struct field in the Discord adapter, Nhost incorrectly assumes the provided email string is verified. The controller queries the local database, identifies the victim's account via the matching email, and commits the OAuth provider identity to the victim's user record. Nhost then issues a JSON Web Token (JWT) or session cookie to the attacker, providing them with full, unauthenticated access to the victim's data and privileges.
The impact of this vulnerability is critical, warranting a CVSS v4.0 score of 9.3. Successful exploitation results in complete account takeover. An attacker assumes the identical authorization context as the victim, obtaining full read and write access to all resources owned by that user.
If the victim possesses administrative privileges within the Nhost-backed application, the attacker inherits those rights. This facilitates extensive lateral movement, data exfiltration, and potential compromise of the entire application environment. The exploit is executed entirely over the network with low attack complexity, requiring no specialized tools or prerequisite authentication state.
The vulnerability is highly reliable because the account linking process is deterministic. The flaw's existence across multiple popular OAuth adapters (Discord, AzureAD, EntraID, Bitbucket) broadens the attack surface, allowing threat actors multiple avenues of exploitation depending on which providers the application administrator has enabled.
Organizations deploying Nhost must upgrade their installations to a patched version immediately. The vulnerability is resolved in commit ec8dab3f2cf46e1131ddaf893d56c37aa00380b2. For teams building from source, this corresponds to a pseudo-version post-dating April 17, 2026. Applying this update introduces the centralized ensureProviderLinkAllowed guard and the updated provider adapters.
For environments where immediate patching is strictly impossible, administrators should disable the vulnerable OAuth adapters (Discord, Bitbucket, AzureAD, EntraID) via the application configuration. Restricting authentication strictly to username/password mechanisms or unaffected OAuth providers will close the attack vector. This mitigation degrades functionality but secures existing user accounts.
Development teams managing custom OAuth adapters for Nhost must perform immediate code audits. Custom implementations must be verified to explicitly extract and evaluate the verified or email_verified parameters from the upstream identity provider's API response. Relying solely on the presence of an email attribute is insufficient and will reintroduce the vulnerability.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
github.com/nhost/nhost Nhost | < 0.0.0-20260417112436-ec8dab3f2cf4 | 0.0.0-20260417112436-ec8dab3f2cf4 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-287 |
| Attack Vector | Network |
| CVSS v4.0 | 9.3 |
| Impact | Complete Account Takeover |
| Exploit Status | Proof of Concept |
| Authentication Required | None |
Improper Authentication: Software does not prove or insufficiently proves that a claim to be a correct identity is valid.
A stored Cross-Site Scripting (XSS) vulnerability exists within plone.restapi, the REST API package for Plone content management system. By supplying a spoofed input MIME type (text/x-html-safe), an attacker can mislead the rendering layer (plone.app.textfield) into assuming that the supplied content is already sanitized. This causes the system to skip the safe_html transform, allowing arbitrary JavaScript to execute in the victim's browser when they view the compromised page.
An untrusted search path vulnerability in the GlobalDatabasePlugin component of the AWS Advanced JDBC Wrapper for Amazon Aurora PostgreSQL allows authenticated, low-privilege database users to hijack administrative session queries. By defining a custom function in a writable schema such as the public schema, an attacker can hijack queries executed automatically during driver-level topology detection. When a highly privileged database user connects to the database utilizing an affected version of the wrapper, the custom function executes under their security context, enabling remote privilege escalation to rds_superuser.
CVE-2026-27771 represents a critical security flaw in Gitea and Forgejo (up to and including version 1.26.1) involving missing authorization checks (CWE-862). Unauthenticated remote attackers can query, enumerate, and download private container images from the OCI-compliant container registry. Additionally, unauthorized users can retrieve private or internal source repository URLs via the Composer package registry metadata API. A public proof-of-concept exists, and threat metrics indicate highly active scanning and exploitation risks.
A missing authorization vulnerability in the Formie plugin for Craft CMS prior to version 3.1.28 allows low-privileged Control Panel users to read and modify sensitive administrative settings, configuration options, and third-party integrations.
CVE-2026-53598 is a directory traversal and arbitrary file read vulnerability in Microsoft Prompty ecosystem loaders across multiple languages. Prior to version 2.0.0-beta.2, the loaders resolved `${file:...}` reference strings inside frontmatter configuration blocks without enforcing that the target file paths resided within authorized directories. This deficiency allows an attacker-controlled configuration file to read sensitive operating system and application files through absolute paths, directory traversal, or symbolic link escapes. The issue is addressed across the Python, C#, Node.js/TypeScript, and Rust ecosystems.
A directory traversal vulnerability exists in the copy subcommand of the proot-distro utility. Due to incomplete path sanitization, local attackers or malicious scripts can read from or write to arbitrary files outside the container rootfs, bypassing isolation barriers and potentially gaining unauthorized access or persistent execution on the host system.