In March 2026, a critical flaw tracked as CVE-2026-29000 landed in one of the places security teams least want surprises: the authentication layer. The issue affects pac4j-jwtوبالتحديد JwtAuthenticator component, and allows a remote attacker to authenticate as arbitrary users, including administrators, under affected configurations. The vulnerability was published by NVD and GitHub’s advisory database, and pac4j’s maintainer released patched versions for the 4.x, 5.x, and 6.x lines. (NVD)
What makes this bug different from routine JWT mistakes is not just the severity score. It is the shape of the failure. This is not a brute-force problem, not a secret-leak prerequisite, and not a narrow parsing edge case with little operational consequence. In the vulnerable flow, an attacker who knows the server’s RSA public key can craft a JWE-wrapped PlainJWT with arbitrary claims, bypass signature verification, and be treated as an authenticated principal. In other words, a key meant to be public becomes sufficient to mint trusted identity under the wrong validation path. (NVD)
That is why this CVE matters to more than pac4j users. It is a clean example of a broader class of identity failures that keep resurfacing across ecosystems: systems confusing confidentiality مع authenticity, or treating successfully parsed and decrypted data as though it had also been cryptographically proven to come from a trusted issuer. The pac4j bug is a modern reminder that when token validation is implemented as a chain of loosely connected steps, one nullable branch can quietly erase the strongest security guarantee in the entire flow. (CodeAnt)
What CVE-2026-29000 actually is
The official description is straightforward. NVD states that pac4j-jwt versions prior to 4.5.9, 5.7.9و 6.3.3 contain an authentication bypass vulnerability in JwtAuthenticator when processing encrypted JWTs. An attacker who possesses the server’s RSA public key can create a JWE-wrapped PlainJWT with arbitrary subject and role claims, bypass signature verification, and authenticate as any user, including administrators. GitHub’s advisory mirrors that description and marks the issue as critical. (NVD)
pac4j’s own advisory does not publish a long technical narrative, but it is unambiguous about the remedy: upgrade to 4.5.9, 5.7.9أو 6.3.3 or newer, depending on your branch. That brevity is common for vendor advisories, but it also hints at something defenders should take seriously: this is the kind of bug where patching is the control, not configuration finesse or defensive folklore. (pac4j.org)
Arctic Wolf’s bulletin adds an important operational detail. It notes that the flaw is exposed in deployments using RSA-based JWE together with JwtAuthenticator configured with both EncryptionConfiguration و SignatureConfiguration. That matters, because some organizations will search their codebase for the pac4j dependency, see it present, and assume they are automatically at risk. The dependency alone is not enough. The dangerous condition is a specific validation path: encrypted JWT processing that later relies on a signature step that can be silently skipped. (Arctic Wolf)
The affected versions and the real trigger conditions
The published affected ranges are consistent across NVD and GitHub Advisory Database: all versions before 4.5.9, versions 5.x before 5.7.9, and versions 6.x from 6.0.4.1 up to but not including 6.3.3 are vulnerable. The patched versions are exactly 4.5.9, 5.7.9و 6.3.3. (NVD)
The more interesting question is whether a given deployment is truly exploitable. Based on the public write-up and defender summaries, the answer is yes only when several conditions line up. The application must accept encrypted JWTs, the vulnerable code path must attempt to unwrap those tokens and then verify an inner signed token, and the server must rely on the resulting claims to build an authenticated user profile. If that is your stack, the vulnerability is not theoretical. It sits exactly where identity is established. (CodeAnt)
This distinction matters operationally. Security teams often divide CVEs into “library issue” and “reachable issue.” CVE-2026-29000 can be both. It is a library flaw, but when reachable, it is often reachable at the security boundary itself: API gateway filters, SSO adapters, web application security middleware, or backend services that trust bearer tokens directly. Those are not dusty corners of an architecture. They are the choke points that determine who the system thinks you are. (Arctic Wolf)
Why this bug is so dangerous, encryption is not identity
To understand the bug, it helps to separate three things that many implementations blur together under the broad label “JWT security.”
A جيه دبليو إس is a signed token. Its signature provides integrity and authenticity, assuming the issuer’s signing key is controlled and the verifier checks the signature correctly.
A جيه دبليو إي is an encrypted token. Its encryption provides confidentiality, meaning the payload is hidden from unauthorized readers.
A PlainJWT is a valid JWT type with no signature at all. It is not a fictional edge case. Libraries know how to parse it. It simply does not prove origin. (CodeAnt)
The pac4j failure lives exactly at that boundary. In the public technical write-up, the vulnerable flow decrypts a JWE and then tries to extract an inner signed JWT via toSignedJWT(). If the payload is actually a PlainJWT, Nimbus returns null. pac4j’s logic then skips the signature verification block because the signedJWT object is null, yet continues on to create an authenticated profile from the token’s claims. The claims are accepted; the authenticity proof is never established. (CodeAnt)
That is the whole lesson in one sentence: the server successfully decrypted attacker-controlled data and treated that success as sufficient evidence of identity. But decryption only proves the server could read the message. It does not prove who created it. The attacker is allowed to encrypt with the public key by design. The missing step is signature verification, and in the vulnerable flow that step can disappear. (CodeAnt)
This is why CVE-2026-29000 deserves attention even from teams that do not use pac4j. Similar failures appear over and over under different names: algorithm confusion, alg:none, issuer validation gaps, realm confusion, or acceptance of unsigned data after a parsing branch takes an unexpected path. The surface changes, but the engineering mistake is the same: untrusted token content crosses the identity boundary without the proof the architecture assumes exists. (NVD)
The vulnerable execution path, step by step
The public research disclosure gives enough detail to reconstruct the dangerous logic without republishing weaponized exploit instructions. In simplified form, the server receives a bearer token, decrypts the outer JWE, attempts to parse the inner token as a signed JWT, conditionally performs signature verification only if the parsed object is non-null, and then builds the authenticated profile from the token claims. If the inner token is an unsigned PlainJWT, toSignedJWT() returns null, so the signature check never runs. The profile creation still happens. (CodeAnt)
That sequence can be summarized safely like this:
// Simplified logic pattern behind the vulnerable flow
jwt = parseIncomingToken(rawToken);
if (isEncrypted(jwt)) {
decrypted = decrypt(jwt);
signedJwt = decrypted.getPayload().toSignedJWT();
if (signedJwt != null) {
jwt = signedJwt;
}
}
// Signature verification only happens when signedJwt != null
if (signedJwt != null) {
verifySignature(signedJwt);
}
// Claims are still used to build identity
createAuthenticatedProfile(jwt);
The problem is not that the code used a null check. The problem is that the null check guarded the only operation that proves authenticity, while the downstream identity-building step remained reachable. In authentication code, that is a design error, not just a local bug. Identity should be impossible to construct unless every required validation stage has produced a positive result. (CodeAnt)
A useful mental model is this: parsing and decrypting are preparatory actions; they are not authorization to trust the claims. Production identity code should make that impossible to forget. If the control flow can reach “build profile” from “token parsed” without “token authenticated,” you already have a latent security bug, whether or not a CVE has been assigned yet.

Why the public key is enough
For many engineers, the phrase “attacker only needs the server’s public key” sounds wrong the first time they read it. Public keys are meant to be public. That is exactly why the bug is so instructive. There is no contradiction here.
In an RSA-based JWE scheme, the public key is used to encrypt content destined for the holder of the private key. So if an application accepts encrypted tokens, an attacker can often generate a syntactically valid encrypted wrapper using the public key that the ecosystem expects to be distributed. That part is normal. What should stop the attacker is the second layer: the inner token should also require a valid signature from a trusted signer. In the vulnerable pac4j path, that second proof can vanish when the inner token is a PlainJWT and the parser returns null. (CodeAnt)
This is why the issue should not be misread as “RSA is broken” or “public keys are too exposed.” The cryptography is doing exactly what it is supposed to do. The application logic is not. The vulnerability sits in the gap between being able to send confidential data to the server و being authorized to assert identity to the server. CVE-2026-29000 collapses those into one step. (CodeAnt)
Real-world impact, where this becomes a production incident
On paper, “authenticate as any user” is already severe. In practice, the impact depends on where pac4j-jwt sits in your architecture. If it is embedded in an internet-facing web application, the obvious risk is direct account takeover. If it sits in an API gateway or SSO integration point, the consequences are larger: forged identity can propagate to downstream services that assume the edge has already done the right thing. (Arctic Wolf)
Consider a few common deployment patterns.
If the token’s الفرعية claim maps directly to an application user, the attacker may impersonate any named account. If the token also carries role claims and the application trusts them, privilege escalation to administrative functions becomes possible. If downstream services use forwarded headers or session material derived from pac4j’s profile object, the initial bypass can become a cross-service trust failure rather than a single-app flaw. None of that requires code execution to be dangerous. Identity bypasses often produce cleaner, quieter, and more durable compromise paths than noisy memory corruption exploits. (NVD)
This also complicates detection. When an attacker uses a forged token that your application itself accepts as valid, the logs may show what looks like a legitimate login or API session under a real username. Traditional WAF signatures and exploit heuristics are not especially helpful when the attack surface is the identity grammar your stack already expects. The anomaly is semantic, not necessarily syntactic.
A concise risk table for engineering teams
| البُعد | Why it matters for CVE-2026-29000 |
|---|---|
| Attack preconditions | Remote attacker, no prior privileges, but reachable affected pac4j-jwt configuration is required (NVD) |
| Core failure | Signature verification can be skipped when a decrypted inner token is a PlainJWT and toSignedJWT() returns null (CodeAnt) |
| What attacker controls | Subject, roles, and other claims inside the forged token under affected validation paths (NVD) |
| Likely business impact | Account takeover, admin impersonation, authorization bypass, cross-service trust abuse (NVD) |
| Best immediate control | Upgrade to 4.5.9, 5.7.9, or 6.3.3 or newer (pac4j.org) |
| Long-term lesson | Treat decryption, parsing, signature verification, issuer validation, and profile creation as separate mandatory gates |
How to tell if your environment is exposed
The fastest first check is dependency inventory. If you ship org.pac4j:pac4j-jwt and the version falls in the vulnerable ranges, you have enough evidence to open an incident and prioritize patching. That alone does not prove exploitability, but it is enough to justify urgent review. GitHub Advisory Database and pac4j’s own advisory provide the version boundaries you need. (pac4j.org)
A simple Maven check is often the quickest path:
mvn dependency:tree | grep pac4j-jwt
If you use Gradle:
./gradlew dependencies --configuration runtimeClasspath | grep pac4j-jwt
If you maintain SBOMs, query them directly for org.pac4j:pac4j-jwt and compare against the patched versions. This should include transitive dependencies. pac4j-jwt is a library; in larger Java stacks it may arrive through framework modules rather than an explicit direct declaration.
The second check is configuration reachability. Review whether your authentication path accepts جيه دبليو إي and whether JwtAuthenticator is configured with both encryption and signature handling. Arctic Wolf explicitly identifies that combination as the impacted deployment pattern. If your application only consumes signed JWTs and never takes the encrypted branch, your practical risk may be lower. If it accepts encrypted bearer tokens at the internet edge, treat the condition as urgent until disproven. (Arctic Wolf)
The third check is behavioral. Look for authentication flows where profile creation is downstream of a parser/decrypter but not hard-coupled to a positive signature verification result. Even after patching pac4j, this is worth reviewing across your own custom filters and middleware. CVE-2026-29000 is one concrete bug, but many organizations have local token glue code that repeats the same trust mistake.

Safe defensive validation, what to test without turning this into an offensive playbook
If you own an authorized staging environment, the right validation question is not “Can I reproduce a public exploit?” It is “Does my system reject encrypted bearer tokens whose inner content is unsigned or otherwise unauthenticated?” That framing keeps the exercise defensive and controlled.
For code review and unit testing, the acceptance criteria should look like this:
- A token that decrypts successfully but contains an unsigned inner JWT must be rejected.
- Profile creation must never occur unless the signature validation step has returned success.
- Role and subject claims must not be trusted until token type, signature, issuer, audience, and temporal claims have all passed validation.
- Negative cases should be explicit tests, not assumptions hidden inside framework code.
A lightweight unit-test style pseudocode example:
@Test
void encryptedUnsignedInnerJwtMustBeRejected() {
String token = buildEncryptedTokenForTestWithoutSignature(); // lab-only fixture
AuthenticationResult result = authenticator.authenticate(token);
assertFalse(result.isAuthenticated());
assertEquals("invalid_token", result.getReason());
}
For production observability, a safer runtime approach is to instrument the validation pipeline so that every accepted token logs a structured record indicating token type, whether it was encrypted, whether a signature was verified, the issuer, the key ID used for verification, and the final profile subject. That creates a forensic path if a future token-validation defect appears elsewhere.
Patch now, then harden the validation model
The mandatory fix is to upgrade to a patched version: 4.5.9, 5.7.9أو 6.3.3 or newer. That comes directly from pac4j and the advisory databases. (pac4j.org)
But patching should not be the end of the conversation. Identity libraries fail in patterns, and mature teams use incidents like this to harden architecture, not just dependencies.
Start with token-type policy. If your application expects a signed inner token, reject anything else explicitly. Do not rely on type coercion helpers or parser-return conventions to decide what should be accepted. The acceptable token forms should be narrow and intentional.
Next, separate stages by outcome, not by object existence. “Parser returned non-null” and “signature validated” are not adjacent abstractions. One is syntactic, the other is cryptographic. Your code should reflect that separation clearly enough that a missing verification step is impossible to overlook in review.
Then harden claims trust. Even after a valid signature, do not over-trust role claims without checking issuer, audience, tenant or realm boundaries, and whether downstream services should consume raw upstream roles at all. CVE-2026-29000 is an authentication bypass, but many JWT incidents become larger because authorization consumes claims too eagerly.
Finally, update dependency governance. Any Java service exposing authentication endpoints should have automated checks for critical security advisories in security-sensitive libraries, including auth frameworks, JWT libraries, serializers, and reverse proxy modules.
Defensive code and pipeline checks you can adopt immediately
A simple dependency policy gate in CI can help stop vulnerable auth libraries from shipping:
#!/usr/bin/env bash
set -euo pipefail
VERSION="$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec 2>/dev/null || true)"
mvn -q dependency:tree -Dincludes=org.pac4j:pac4j-jwt > deps.txt
if grep -E 'org\\.pac4j:pac4j-jwt:jar:(4\\.[0-5]\\.[0-8]|5\\.[0-7]\\.[0-8]|6\\.(0\\.4\\.1|0\\.5|0\\.6|0\\.7|0\\.8|0\\.9|1\\..*|2\\..*|3\\.[0-2]))' deps.txt; then
echo "Blocked: vulnerable pac4j-jwt version detected"
exit 1
fi
echo "No known vulnerable pac4j-jwt version detected by policy gate"
You can also add secure-code review rules around profile creation. Any function named like createProfile, buildPrincipal, toUserأو authenticate should be reviewed to ensure its inputs come only from validated claims.
A simple Semgrep-style rule concept for local adaptation:
rules:
- id: profile-created-before-verification
message: Profile creation appears reachable before cryptographic verification
severity: ERROR
languages: [java]
patterns:
- pattern-inside: |
class $C {
...
}
- pattern: createJwtProfile(...)
This is intentionally broad and should be tuned for your codebase. The goal is not precision on day one. The goal is to make identity-boundary code highly visible in review.

CVE-2026-29000, The pac4j-jwt Authentication Bypass
Why this bug belongs in the same family as other high-impact JWT failures
CVE-2026-29000 is new, but the pattern is old. One of the classic JWT issues, CVE-2015-9235, involved verification bypass due to weak validation of the JWT algorithm type in رمز jsonwebtoken, enabling attackers to abuse algorithm confusion between asymmetric and symmetric verification modes. More recently, CVE-2024-54150 described a case where the wrong verification method could be used, such as using a public key as an HMAC secret, again leading to unauthorized access. And in CVE-2026-23552, Apache Camel’s Keycloak component accepted tokens across realms because it did not validate the issuer claim against the configured realm, breaking tenant isolation. (NVD)
These bugs are not identical, but they rhyme.
In algorithm-confusion bugs, the system trusts attacker-controlled metadata about how verification should happen.
في alg:none style bugs, the system accepts a token form that should have been disallowed for the deployment context.
In issuer-validation bugs, the system mistakes “well-formed token” for “token from the right authority.”
In CVE-2026-29000, the system mistakes “successfully decrypted token” for “cryptographically authenticated token.”
The engineering takeaway is bigger than JWT. Identity systems fail when verification logic is optional, branch-dependent, or coupled too weakly to the moment where claims become trusted identity. That is the pattern teams should be threat-modeling for, regardless of framework.
What defenders should look for in logs and telemetry
Because this is not a memory corruption exploit, the useful signals are often identity-level, not infrastructure-level.
Look for successful sessions where the bearer token path involved encrypted JWT handling, especially if those sessions map to high-privilege users and are followed by unusual administrative actions. Compare token metadata for accepted sessions before and after patching. If your instrumentation does not currently record whether a signature was verified, that gap itself is a finding.
Look for sudden role elevation in applications that rely on role claims from bearer tokens. Investigate whether accepted tokens carry unexpected issuer, key ID, or token-type combinations. If your services mint internal sessions after edge authentication, trace whether downstream access spikes correlate with one edge service that relies on pac4j-jwt.
Most importantly, do not assume that “successful login” means “benign login.” When the vulnerability is an authentication bypass, success may be the symptom.
Two practical paragraphs on where Penligent can help
For teams running many Java services, the hardest part of a CVE like this is rarely reading the advisory. The hard part is answering operational questions fast: which internet-facing services actually pull in pac4j-jwt, which ones expose bearer-token authentication paths, which environments accept encrypted JWTs, and whether the patch really closed the reachable path instead of just updating a version string. In that kind of workflow, an AI-driven penetration testing platform such as بنليجنت can be useful as a force multiplier for asset discovery, dependency-aware testing, authentication-flow mapping, and post-patch validation across multiple targets.
The value is strongest after triage. Once engineering identifies candidate services, automated testing can help verify that malformed or policy-breaking token paths are now rejected, correlate results across environments, and keep the remediation effort from degrading into one-off manual spot checks. That is where automation earns its keep in security engineering: not by replacing judgment, but by turning a library advisory into a repeatable verification program.
The bigger lesson, identity code cannot have soft-fail branches
The most important lesson in CVE-2026-29000 is not “upgrade pac4j.” You should do that, but the durable lesson is architectural.
Security-sensitive code often starts life as convenience code. One parser helper here, one null-tolerant branch there, one “we already decrypted it” assumption in the middle, one profile object built before all checks are finalized. None of these lines look dramatic in isolation. But identity boundaries are not forgiving. If any path exists where claims can become principal state before authenticity is proven, the whole system is one branch away from a headline.
A robust identity pipeline should make the secure path structurally obvious:
- Parse only what you explicitly support.
- Decrypt only when policy says encryption is expected.
- Verify signatures as a hard prerequisite, never a best-effort branch.
- Validate issuer, audience, time, tenant, and token type before trust.
- Build principal state only after all previous gates have passed.
- Log enough metadata to prove those gates actually ran.
CVE-2026-29000 is severe because it breaks that chain at the exact moment the application decides who you are. It is also valuable because it shows, with unusual clarity, how modern identity failures happen. Not through mystical crypto breakage. Through ordinary control flow that lets authenticated state emerge from unauthenticated input.
Recommended reading and references
- NVD, CVE-2026-29000 detail: (NVD)
- pac4j official security advisory: (pac4j.org)
- GitHub Advisory Database entry for pac4j-jwt: (جيثب)
- Public technical research disclosure by CodeAnt AI: (CodeAnt)
- Arctic Wolf security bulletin: (Arctic Wolf)
- CVE-2015-9235, jsonwebtoken verification bypass: (NVD)
- CVE-2024-54150, JWT verification method confusion: (NVD)
- CVE-2026-23552, issuer-validation failure in Apache Camel Keycloak component: (NVD)
- Penligent, CVE-2026-29000 in pac4j-jwt, an identity bypass hiding behind encryption: (بنليجنت)
- Penligent, CVE 2026 The Vulnerability Landscape, When Identity Breaks and Legacy Code Bites Back: (بنليجنت)
- Penligent, Technical Deep Dive, CVE-2026-23478 — The Critical Authentication Bypass in Cal.com: (بنليجنت)
- Penligent, API Testing: (بنليجنت)

