Brief Summary: Laravel Passport CVE-2026-39976 Authentication Bypass via Client Credentials Token Confusion

A short review of CVE-2026-39976, a high severity authentication bypass in Laravel Passport versions 13.0.0 through 13.7.0 where machine to machine tokens can inadvertently authenticate as real users due to a JWT subject claim mismatch.

CVE Analysis

6 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-04-09

Brief Summary: Laravel Passport CVE-2026-39976 Authentication Bypass via Client Credentials Token Confusion
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

A machine to machine OAuth2 token should never grant access to a human user's account, but a flaw in Laravel Passport made exactly that possible. CVE-2026-39976 is an authentication bypass in Laravel Passport versions 13.0.0 through 13.7.0 where the token guard misinterprets a client_credentials token's subject claim as a user identifier, potentially resolving an unrelated real user and granting the token bearer that user's full privileges.

Laravel Passport is the official OAuth2 server implementation for the Laravel PHP framework, with over 85 million total downloads on Packagist. Given that 77.5 percent of PHP framework powered websites run Laravel, the potential blast radius of this vulnerability is significant for organizations relying on Passport for API authentication.

Technical Information

Root Cause: Semantic Mismatch in JWT Subject Handling

The vulnerability originates from a disconnect between two layers of the authentication stack. The League OAuth2 server library (league/oauth2-server), which Laravel Passport depends on, handles the actual token issuance. For client_credentials grants, there is no user involved in the flow; the token represents a machine, not a person. In this case, the League library sets the JWT sub (subject) claim to the client identifier rather than a user identifier.

Laravel Passport's TokenGuard::authenticateViaBearerToken() method then processes incoming bearer tokens. It extracts the sub claim and passes it directly to the user provider's retrieveById() method. The critical flaw is that the guard performs no validation to determine whether the sub value actually represents a user identifier or a client identifier. If a user record exists in the database whose primary key matches the client identifier value, the guard resolves that user and treats the request as fully authenticated on behalf of that user.

Configuration Dependent Exploitability

The practical exploitability of this vulnerability depends heavily on how client identifiers are configured within the application.

Default configuration (UUID client IDs): Laravel Passport uses UUIDs for client identifiers by default. Since user primary keys are typically auto incrementing integers, a UUID like 9a3b5c7d-1234-4e56-a789-0bcd1ef23456 will never match an integer user ID. In this configuration, the collision risk is extremely low and the vulnerability has minimal practical impact.

Integer client IDs: Applications that disable UUID client identifiers by setting Passport::clientUuids(false) switch to integer based client IDs. In this configuration, a client with ID 1 would cause any client_credentials token issued to that client to resolve as User ID 1. Since User ID 1 is often the first administrator account created during application setup, this represents a direct path to privilege escalation.

Improper middleware usage: Even with UUID client IDs, using the standard auth:api middleware instead of the dedicated client credentials middleware creates a situation where the guard logic processes client_credentials tokens through the user authentication path.

Attack Flow

  1. An attacker registers or obtains credentials for an OAuth2 client configured with the client_credentials grant type. This is a normal, expected operation.
  2. The attacker requests a token from the /oauth/token endpoint using the client_credentials grant. The server issues a valid, properly signed JWT where the sub claim contains the client's identifier.
  3. The attacker presents this token to an API endpoint protected by the auth:api middleware (rather than the EnsureClientIsResourceOwner middleware).
  4. The TokenGuard extracts the sub claim from the JWT and calls retrieveById() on the configured user provider.
  5. If a user exists whose primary key matches the client identifier (most likely in integer ID configurations), the guard authenticates the request as that user.
  6. The attacker now has full access to all resources and permissions belonging to the resolved user, without ever possessing the user's credentials.

The particularly insidious aspect of this vulnerability is that every token involved is cryptographically valid. The server issued it, the signature checks out, and the token has not been tampered with. The problem is purely in how the guard interprets the token's semantics.

Affected Systems and Versions

The vulnerability affects Laravel Passport versions 13.0.0 through 13.7.0 (all versions before 13.7.1).

The risk level varies by configuration:

ConfigurationClient ID FormatCollision RiskExploitation Potential
Default SetupUUIDExtremely LowMinimal
Passport::clientUuids(false)IntegerHighCritical
Standard auth:api middleware on client routesAnyModerateHigh

Applications are most vulnerable when they combine integer based client IDs with the use of auth:api middleware on routes that accept client_credentials tokens.

The fix is available in Laravel Passport version 13.7.1.

Vendor Security History

Laravel's security team demonstrated a rapid and coordinated response to this vulnerability. The issue was reported via GitHub (Issue #1900), two pull requests (#1901 and #1902) were submitted to address the flaw, and a GitHub Security Advisory (GHSA-349c-2h2f-mxf6) was published. The fix was released in version 13.7.1. The coordination between the Laravel Passport maintainers and the upstream league/oauth2-server project reflects a mature vulnerability disclosure and remediation process.

References

Detect & fix
what others miss

Security magnifying glass visualization