Code Security

Mar 6, 2026

When an AI Code Reviewer Flags a CVSS-10 Authentication Bypass

Amartya | CodeAnt AI Code Review Platform
Sonali Sood

Founding GTM, CodeAnt AI

How a Null Check Led to a Critical JWT Vulnerability in pac4j

Modern software security increasingly depends on open-source libraries. Authentication frameworks, cryptographic tooling, serialization libraries, and HTTP frameworks power thousands of production systems today. When vulnerabilities appear in these components, the ripple effects can reach across entire ecosystems.

This reality is precisely why our security research team at CodeAnt AI began asking a deceptively simple question:

When a vulnerability in an open-source package gets patched, does the patch actually eliminate the underlying issue?

  • Not whether the version number changed.

  • Not whether the advisory was published.

  • But whether the actual code path that enabled the vulnerability was truly fixed.

As part of this research effort, we began analyzing patched CVEs across major package ecosystems including Maven, npm, PyPI, and NuGet. Instead of simply reading advisories, we examined patch diffs, surrounding logic, and execution flows to understand whether security-sensitive assumptions were actually enforced.

During one of these audits, our AI code reviewer flagged a seemingly minor anomaly in the Java authentication library pac4j-jwt.

That anomaly turned out to be a complete authentication bypass.

The vulnerability has now been assigned CVE-2026-29000 with a CVSS score of 10.0 (Critical). An attacker could authenticate as any user, including administrators, using nothing more than a server’s public RSA key, the key intentionally designed to be publicly accessible.

This article walks through how the issue was discovered, why the bug occurred, and what it reveals about a broader pattern in modern application security.

For the complete technical disclosure and patch details, see the original research write-up by the CodeAnt AI Security Research team.

The Research Question That Started It

The vulnerability was discovered during an internal project focused on post-patch security verification.

Security advisories usually describe a vulnerability and its fix, but rarely answer deeper questions such as:

  • Does the patch address every execution path?

  • Could similar logic paths remain vulnerable?

  • Do assumptions in the code still allow spec-compliant but unexpected inputs?

Instead of manually auditing each project, we used our AI code reviewer to analyze patches and surrounding code paths in libraries that had previously received CVEs.

The goal was not to rediscover known vulnerabilities, but to detect security-relevant anomalies in the surrounding logic.

During this process, the AI flagged a suspicious pattern in pac4j-jwt’s token validation flow.

Specifically, it identified a null check guarding the entire signature verification block.

On its own, this may sound harmless. But in authentication logic, even small control-flow decisions can dramatically affect security guarantees.

A security engineer on our team reviewed the flagged code path, traced the execution flow, and quickly realized the implications.

If the condition evaluated to false, signature verification would never execute.

And yet the authentication flow would still proceed.

That observation led to a deeper investigation.

How JWT Authentication Is Intended to Work

To understand the vulnerability, we first need to understand how pac4j-jwt typically processes authentication tokens.

Many production deployments use a two-layer JWT structure.

Layer 1: Encryption (JWE)

The outer layer is an encrypted JWT, known as JWE (JSON Web Encryption).

Encryption ensures confidentiality. The token contents cannot be read while in transit. Only the server holding the private key can decrypt the token.

Layer 2: Signature (JWS)

Inside the encrypted wrapper sits a signed JWT (JWS).

The signature proves that the token was created by a trusted authority possessing the signing key.

The intended authentication flow looks like this:



Each step provides a different security property:

  • Encryption → confidentiality

  • Signature → authenticity and integrity

Encryption alone does not prove who created the token. It only ensures the contents cannot be read. Authenticity must always be verified through signature validation.

This distinction is crucial.

And it is exactly where the vulnerability appeared.

The Code Path That Broke Authentication

While analyzing the token validation logic in JwtAuthenticator.java, the AI reviewer flagged a condition surrounding signature verification.

Simplified, the code flow looked like this:



The critical line was the call to:

This method comes from the Nimbus JOSE+JWT library.

Its behavior is correct and well-documented:

  • If the payload is a signed JWT, it returns a parsed object.

  • If the payload is not signed, it returns null.

That includes PlainJWT, which is a valid JWT type defined in the specification.

The pac4j code then performs a conditional check:



But here’s the critical detail.

If signedJWT is null, the code simply skips signature verification entirely.

And then continues processing the token.

Eventually, the authentication logic proceeds to create a user profile using the token’s claims — without ever verifying authenticity.

This effectively means:

signature verification can silently disappear from the authentication flow.

Turning the Bug Into an Exploit

Once this behavior was understood, the next question was obvious:

Can an attacker actually control whether the inner payload becomes a signed JWT or not?

The answer turned out to be trivial.

Instead of crafting a signed JWT, an attacker can simply create an unsigned PlainJWT.

PlainJWT is a valid token type supported by the JWT specification and many libraries.

Because it is not signed, toSignedJWT() returns null.

Which triggers the bypass.

The attacker workflow becomes surprisingly simple.

Step 1: Obtain the Server’s Public Key

In RSA-based JWT encryption setups, the public key is usually available via:

  • JWKS endpoints (/.well-known/jwks.json)

  • configuration files

  • TLS certificate inspection

  • public repositories

This is normal. Public keys are meant to be public.

Step 2: Craft Arbitrary Claims

The attacker can construct any claims they want.

Examples include:

  • admin identity

  • elevated roles

  • extended expiration

Example malicious claim set:



Step 3: Create an Unsigned JWT

Instead of signing the token, the attacker creates a PlainJWT.

This produces a syntactically valid JWT with no signature.

Step 4: Encrypt the Token Using the Public Key

Next, the attacker wraps the unsigned token inside a JWE envelope encrypted with the server’s public key.

From the outside, the token looks like a normal encrypted authentication token.

When the server receives it:

  1. Decryption succeeds

  2. toSignedJWT() returns null

  3. Signature verification is skipped

  4. Claims are accepted

Authentication succeeds.

Demonstrating the Bypass

Our security team implemented a proof-of-concept exploit using the real pac4j-jwt configuration.

The attacker only required the server’s public RSA key.

The result:

[BYPASS] Authenticated as: admin
[BYPASS]

[BYPASS] Authenticated as: admin
[BYPASS]

  • No private key.

  • No shared secret.

  • No brute-force attack.

  • Only the public key.

Why This Bug Matters Beyond One Library

At first glance, this vulnerability may appear specific to pac4j.

In reality, it represents a broader pattern seen across modern software systems.

Every individual component in this chain behaved correctly.

The JWT specification allows PlainJWT.

The Nimbus library correctly returned null when encountering one.

The pac4j code correctly checked for null.

Yet when combined, these pieces created a condition where authentication guarantees collapsed.

This type of vulnerability emerges not from broken primitives, but from incorrect assumptions about how components interact.

Developers often implement logic around expected inputs.

Attackers focus on inputs that are allowed by the specification but not anticipated by the implementation.

This mismatch is where many modern security vulnerabilities appear.

Responsible Disclosure and Rapid Response

After validating the issue and building a working proof-of-concept, we privately disclosed the vulnerability to pac4j maintainer Jérôme Leleu on February 28.

The report included:

  • full technical analysis

  • exploit proof-of-concept

  • proposed remediation approach

The response came the following day.

The maintainer confirmed the issue and began preparing patches across multiple version lines.

Within two business days:

  • patched releases were shipped for pac4j 4.x, 5.x, and 6.x

  • a security advisory was published

  • our research team was credited for the discovery

This level of responsiveness is rare in security disclosure processes and highlights the dedication of open-source maintainers who safeguard critical infrastructure used by thousands of applications.

If Your Application Uses pac4j-jwt

Applications may be vulnerable if they meet the following conditions:

  • use pac4j-jwt

  • implement RSA-based encrypted JWTs (JWE)

  • configure both encryption and signature configurations

  • rely on JwtAuthenticator

Affected users should upgrade immediately to:

  • 4.5.9 or newer

  • 5.7.9 or newer

  • 6.3.3 or newer

Organizations can quickly check their dependency tree using Maven or Gradle to determine whether pac4j-jwt is present in their build.

A Larger Security Research Effort

This vulnerability is part of a broader research initiative at CodeAnt AI focused on auditing whether security patches in widely used open-source packages actually eliminate the root cause of vulnerabilities.

Modern software supply chains rely on thousands of dependencies, many maintained by small teams or individual developers. Automated analysis tools can help uncover subtle logic flaws that manual review might miss.

The pac4j finding is one example of what this approach can uncover.

Additional research results from our audit across multiple ecosystems will be released as coordinated disclosure timelines are completed.

When AI Code Review Exposes Security Assumptions

The pac4j vulnerability demonstrates an important truth about modern application security.

Critical vulnerabilities rarely appear because cryptographic algorithms fail.

They appear when software makes incorrect assumptions about how those algorithms are used.

In this case:

  • JWT specifications allowed unsigned tokens

  • the parsing library behaved correctly

  • the authentication code handled null values correctly

Yet when these pieces interacted, signature verification silently disappeared from the authentication pipeline.

That small assumption created a CVSS-10 authentication bypass capable of granting administrative access.

This is exactly the type of vulnerability traditional static analysis tools often miss.

It emerges not from obvious insecure code, but from subtle control-flow conditions across multiple components.

During this research, the anomaly was first flagged by an AI code reviewer analyzing patch diffs and authentication logic paths.

Instead of searching for known vulnerability patterns, the system asked a deeper question:

What happens if the input behaves differently than the developer expected?

That question ultimately uncovered a critical authentication flaw.

As modern software stacks grow more complex and dependency chains expand, this type of analysis becomes increasingly important.

Security tools that can reason about execution paths, trust boundaries, and unexpected input behavior will play a growing role in identifying the vulnerabilities that matter most.

If you want to explore the full technical analysis and exploit details, the original CodeAnt AI security research disclosure is available here:

https://www.codeant.ai/security-research/pac4j-jwt-authentication-bypass-public-key

How a Null Check Led to a Critical JWT Vulnerability in pac4j

Modern software security increasingly depends on open-source libraries. Authentication frameworks, cryptographic tooling, serialization libraries, and HTTP frameworks power thousands of production systems today. When vulnerabilities appear in these components, the ripple effects can reach across entire ecosystems.

This reality is precisely why our security research team at CodeAnt AI began asking a deceptively simple question:

When a vulnerability in an open-source package gets patched, does the patch actually eliminate the underlying issue?

  • Not whether the version number changed.

  • Not whether the advisory was published.

  • But whether the actual code path that enabled the vulnerability was truly fixed.

As part of this research effort, we began analyzing patched CVEs across major package ecosystems including Maven, npm, PyPI, and NuGet. Instead of simply reading advisories, we examined patch diffs, surrounding logic, and execution flows to understand whether security-sensitive assumptions were actually enforced.

During one of these audits, our AI code reviewer flagged a seemingly minor anomaly in the Java authentication library pac4j-jwt.

That anomaly turned out to be a complete authentication bypass.

The vulnerability has now been assigned CVE-2026-29000 with a CVSS score of 10.0 (Critical). An attacker could authenticate as any user, including administrators, using nothing more than a server’s public RSA key, the key intentionally designed to be publicly accessible.

This article walks through how the issue was discovered, why the bug occurred, and what it reveals about a broader pattern in modern application security.

For the complete technical disclosure and patch details, see the original research write-up by the CodeAnt AI Security Research team.

The Research Question That Started It

The vulnerability was discovered during an internal project focused on post-patch security verification.

Security advisories usually describe a vulnerability and its fix, but rarely answer deeper questions such as:

  • Does the patch address every execution path?

  • Could similar logic paths remain vulnerable?

  • Do assumptions in the code still allow spec-compliant but unexpected inputs?

Instead of manually auditing each project, we used our AI code reviewer to analyze patches and surrounding code paths in libraries that had previously received CVEs.

The goal was not to rediscover known vulnerabilities, but to detect security-relevant anomalies in the surrounding logic.

During this process, the AI flagged a suspicious pattern in pac4j-jwt’s token validation flow.

Specifically, it identified a null check guarding the entire signature verification block.

On its own, this may sound harmless. But in authentication logic, even small control-flow decisions can dramatically affect security guarantees.

A security engineer on our team reviewed the flagged code path, traced the execution flow, and quickly realized the implications.

If the condition evaluated to false, signature verification would never execute.

And yet the authentication flow would still proceed.

That observation led to a deeper investigation.

How JWT Authentication Is Intended to Work

To understand the vulnerability, we first need to understand how pac4j-jwt typically processes authentication tokens.

Many production deployments use a two-layer JWT structure.

Layer 1: Encryption (JWE)

The outer layer is an encrypted JWT, known as JWE (JSON Web Encryption).

Encryption ensures confidentiality. The token contents cannot be read while in transit. Only the server holding the private key can decrypt the token.

Layer 2: Signature (JWS)

Inside the encrypted wrapper sits a signed JWT (JWS).

The signature proves that the token was created by a trusted authority possessing the signing key.

The intended authentication flow looks like this:


Each step provides a different security property:

  • Encryption → confidentiality

  • Signature → authenticity and integrity

Encryption alone does not prove who created the token. It only ensures the contents cannot be read. Authenticity must always be verified through signature validation.

This distinction is crucial.

And it is exactly where the vulnerability appeared.

The Code Path That Broke Authentication

While analyzing the token validation logic in JwtAuthenticator.java, the AI reviewer flagged a condition surrounding signature verification.

Simplified, the code flow looked like this:


The critical line was the call to:

This method comes from the Nimbus JOSE+JWT library.

Its behavior is correct and well-documented:

  • If the payload is a signed JWT, it returns a parsed object.

  • If the payload is not signed, it returns null.

That includes PlainJWT, which is a valid JWT type defined in the specification.

The pac4j code then performs a conditional check:


But here’s the critical detail.

If signedJWT is null, the code simply skips signature verification entirely.

And then continues processing the token.

Eventually, the authentication logic proceeds to create a user profile using the token’s claims — without ever verifying authenticity.

This effectively means:

signature verification can silently disappear from the authentication flow.

Turning the Bug Into an Exploit

Once this behavior was understood, the next question was obvious:

Can an attacker actually control whether the inner payload becomes a signed JWT or not?

The answer turned out to be trivial.

Instead of crafting a signed JWT, an attacker can simply create an unsigned PlainJWT.

PlainJWT is a valid token type supported by the JWT specification and many libraries.

Because it is not signed, toSignedJWT() returns null.

Which triggers the bypass.

The attacker workflow becomes surprisingly simple.

Step 1: Obtain the Server’s Public Key

In RSA-based JWT encryption setups, the public key is usually available via:

  • JWKS endpoints (/.well-known/jwks.json)

  • configuration files

  • TLS certificate inspection

  • public repositories

This is normal. Public keys are meant to be public.

Step 2: Craft Arbitrary Claims

The attacker can construct any claims they want.

Examples include:

  • admin identity

  • elevated roles

  • extended expiration

Example malicious claim set:


Step 3: Create an Unsigned JWT

Instead of signing the token, the attacker creates a PlainJWT.

This produces a syntactically valid JWT with no signature.

Step 4: Encrypt the Token Using the Public Key

Next, the attacker wraps the unsigned token inside a JWE envelope encrypted with the server’s public key.

From the outside, the token looks like a normal encrypted authentication token.

When the server receives it:

  1. Decryption succeeds

  2. toSignedJWT() returns null

  3. Signature verification is skipped

  4. Claims are accepted

Authentication succeeds.

Demonstrating the Bypass

Our security team implemented a proof-of-concept exploit using the real pac4j-jwt configuration.

The attacker only required the server’s public RSA key.

The result:

[BYPASS] Authenticated as: admin
[BYPASS]

  • No private key.

  • No shared secret.

  • No brute-force attack.

  • Only the public key.

Why This Bug Matters Beyond One Library

At first glance, this vulnerability may appear specific to pac4j.

In reality, it represents a broader pattern seen across modern software systems.

Every individual component in this chain behaved correctly.

The JWT specification allows PlainJWT.

The Nimbus library correctly returned null when encountering one.

The pac4j code correctly checked for null.

Yet when combined, these pieces created a condition where authentication guarantees collapsed.

This type of vulnerability emerges not from broken primitives, but from incorrect assumptions about how components interact.

Developers often implement logic around expected inputs.

Attackers focus on inputs that are allowed by the specification but not anticipated by the implementation.

This mismatch is where many modern security vulnerabilities appear.

Responsible Disclosure and Rapid Response

After validating the issue and building a working proof-of-concept, we privately disclosed the vulnerability to pac4j maintainer Jérôme Leleu on February 28.

The report included:

  • full technical analysis

  • exploit proof-of-concept

  • proposed remediation approach

The response came the following day.

The maintainer confirmed the issue and began preparing patches across multiple version lines.

Within two business days:

  • patched releases were shipped for pac4j 4.x, 5.x, and 6.x

  • a security advisory was published

  • our research team was credited for the discovery

This level of responsiveness is rare in security disclosure processes and highlights the dedication of open-source maintainers who safeguard critical infrastructure used by thousands of applications.

If Your Application Uses pac4j-jwt

Applications may be vulnerable if they meet the following conditions:

  • use pac4j-jwt

  • implement RSA-based encrypted JWTs (JWE)

  • configure both encryption and signature configurations

  • rely on JwtAuthenticator

Affected users should upgrade immediately to:

  • 4.5.9 or newer

  • 5.7.9 or newer

  • 6.3.3 or newer

Organizations can quickly check their dependency tree using Maven or Gradle to determine whether pac4j-jwt is present in their build.

A Larger Security Research Effort

This vulnerability is part of a broader research initiative at CodeAnt AI focused on auditing whether security patches in widely used open-source packages actually eliminate the root cause of vulnerabilities.

Modern software supply chains rely on thousands of dependencies, many maintained by small teams or individual developers. Automated analysis tools can help uncover subtle logic flaws that manual review might miss.

The pac4j finding is one example of what this approach can uncover.

Additional research results from our audit across multiple ecosystems will be released as coordinated disclosure timelines are completed.

When AI Code Review Exposes Security Assumptions

The pac4j vulnerability demonstrates an important truth about modern application security.

Critical vulnerabilities rarely appear because cryptographic algorithms fail.

They appear when software makes incorrect assumptions about how those algorithms are used.

In this case:

  • JWT specifications allowed unsigned tokens

  • the parsing library behaved correctly

  • the authentication code handled null values correctly

Yet when these pieces interacted, signature verification silently disappeared from the authentication pipeline.

That small assumption created a CVSS-10 authentication bypass capable of granting administrative access.

This is exactly the type of vulnerability traditional static analysis tools often miss.

It emerges not from obvious insecure code, but from subtle control-flow conditions across multiple components.

During this research, the anomaly was first flagged by an AI code reviewer analyzing patch diffs and authentication logic paths.

Instead of searching for known vulnerability patterns, the system asked a deeper question:

What happens if the input behaves differently than the developer expected?

That question ultimately uncovered a critical authentication flaw.

As modern software stacks grow more complex and dependency chains expand, this type of analysis becomes increasingly important.

Security tools that can reason about execution paths, trust boundaries, and unexpected input behavior will play a growing role in identifying the vulnerabilities that matter most.

If you want to explore the full technical analysis and exploit details, the original CodeAnt AI security research disclosure is available here:

https://www.codeant.ai/security-research/pac4j-jwt-authentication-bypass-public-key

FAQs

What is CVE-2026-29000?

How does the pac4j JWT authentication bypass work?

Why does the exploit only require a public key?

Which pac4j versions are affected?

How was the vulnerability discovered?

Table of Contents

Start Your 14-Day Free Trial

AI code reviews, security, and quality trusted by modern engineering teams. No credit card required!

Share blog: