Automated Threat Modeling

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 because of 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. The token guard reads a client_credentials token's subject claim as a user identifier. That can resolve an unrelated real user and give 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. Laravel runs 77.5 percent of PHP-framework-powered websites, so the potential blast radius is large for organizations that rely on Passport for API authentication.

Technical information

Root cause: semantic mismatch in JWT subject handling

The vulnerability comes from a disconnect between two layers of the authentication stack. Laravel Passport depends on the League OAuth2 server library (league/oauth2-server), which handles the actual token issuance. A client_credentials grant has no user in the flow, and the token represents a machine rather than a person. For these grants, the League library sets the JWT sub (subject) claim to the client identifier instead of 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 guard never checks whether the sub value is a user identifier or a client identifier. If the database holds a user record whose primary key matches the client identifier value, the guard resolves that user and treats the request as fully authenticated on that user's behalf.

Configuration-dependent exploitability

Whether an attacker can exploit this in practice depends heavily on how the application configures client identifiers.

Default configuration (UUID client IDs): Laravel Passport uses UUIDs for client identifiers by default. User primary keys are typically auto-incrementing integers, so 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. Here, any client_credentials token issued to a client with ID 1 resolves as User ID 1. User ID 1 is often the first administrator account created during application setup, which makes this 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 sends client_credentials tokens through the guard's 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 whose 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 every resource and permission belonging to the resolved user, without ever holding that user's credentials.

Every token involved is cryptographically valid. The server issued it, the signature checks out, and nobody has tampered with it. The problem lies 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 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 responded to this vulnerability quickly and in coordination with others. The issue was reported on GitHub (Issue #1900), and two pull requests (#1901 and #1902) were submitted to address the flaw. A GitHub Security Advisory (GHSA-349c-2h2f-mxf6) followed, and the fix shipped in version 13.7.1. The Laravel Passport maintainers worked with the upstream league/oauth2-server project throughout, which reflects a mature disclosure and remediation process.

References

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Gerrit
  • CVS
  • Jira
  • Linear
  • Slack
  • Security Compass
Explore integrations
Security magnifying glass visualization