Back to Blog
2026-07-18

CVE-2026-59208: How n8n's Token Exchange Confused One Issuer's JWT for Another

A critical cross-issuer confusion flaw in n8n's RFC 8693 token exchange implementation let attackers forge valid JWTs from one identity provider to hijack accounts in an entirely different tenant — no credentials needed.

🔓 CVE-2026-59208: How n8n's Token Exchange Confused One Issuer's JWT for Another

⚠️ Threat Badge: Critical — CVSS 9.1 (Critical) | Exploitation: Confirmed | Status: Patched June 24, 2026


The Hook: A Valid Signature, the Wrong Tenant

In June 2026, a red team engagement looked different from most. The attack didn't come from a phishing email, a stolen password, or a misconfigured S3 bucket. It started with a correctly-signed JSON Web Token — one that any standard JWT library would accept as perfectly legitimate.

The token was signed by Issuer A, and it carried a sub claim for alice@co.com. The problem: n8n accepted that token and issued a session as alice@co.com inside Issuer B's tenant — a different organization, a different trust boundary, entirely unauthorized.

No replay. No man-in-the-middle. No cryptographic exploit. Just a trust boundary that wasn't enforced where it mattered most.

This is CVE-2026-59208, a cross-issuer confusion vulnerability in n8n's enterprise OAuth token exchange feature, disclosed publicly on July 9, 2026. It was found by Strix, an AI-powered penetration testing agent — a detail that itself says something significant about where automated security testing is heading.


What Is RFC 8693 Token Exchange?

Before the vulnerability makes sense, you need to understand the feature it lives inside.

RFC 8693 — OAuth 2.0 Token Exchange — is an IETF standard that lets one OAuth client trade in a credential for a different credential, often with a more restricted scope or audience. The classic use case: a backend service accepts a SAML assertion or a JWT from a federated identity provider, validates it, and issues its own short-lived access token so the calling client never directly holds the original assertion.

In n8n's implementation, this enables enterprise OEM integrations: a partner organization signs a short-lived JWT with its own key, sends it to n8n's token exchange endpoint, and n8n maps the sub claim to a local account within the partner's tenant and issues a session — all without a second login screen.

The flow, as designed:

1. Partner (Issuer A) signs a JWT with its own private key
   └─ sub: alice@co.com

2. Attacker sends that JWT to n8n's token exchange endpoint
   └─ n8n verifies signature against Issuer A's public key ✓

3. n8n looks up alice@co.com in its user store
   └─ Finds alice@co.com — but in Issuer B's tenant

4. n8n issues a session as alice@co.com in Issuer B's tenant
   └─ Attacker now has access to the wrong organization

The flaw: n8n correctly verified the token's cryptographic signature, but it never confirmed that the issuer who signed the token was the same issuer that the sub claim belonged to.


Why "Signature Valid" ≠ "Trust Granted"

The vulnerability is a textbook example of a confused deputy problem wearing OAuth clothing. The system correctly answered the question "is this cryptographic signature valid?" but never asked the question "is this the right issuer for this identity claim?"

In a single-tenant deployment, this doesn't matter — there's only one issuer, so any valid signature is implicitly from the right party. But n8n's enterprise OEM setup involves multiple tenants, each with their own configured identity provider. When n8n receives a JWT, it has:

  • The token itself (with iss, sub, aud claims)
  • A per-tenant configuration of trusted issuers and their public keys
  • A user directory scoped to each tenant

The vulnerable code path verified (1) and held (2) and (3), but it never bound them together correctly. It found the user matching the sub claim across all tenants, not just the tenant belonging to the issuer in the iss claim.


🔍 Real-World Impact

The n8n token exchange feature is designed for enterprise OEM deployments where a partner uses n8n's automation platform under a white-labelled arrangement. In these setups:

  • Each partner/tenant has its own configured OAuth issuer
  • Users authenticate to the partner's identity provider
  • The partner's IdP issues a signed JWT asserting the user's identity
  • n8n exchanges that JWT for a session without re-authentication

An attacker who obtains a valid JWT from any trusted issuer — perhaps through a separate breach, a misconfigured IdP, or a retired test tenant — could use it to authenticate as any user in any other tenant, as long as that user's sub claim value matched an account in the target tenant.

Key insight: The attack doesn't exploit a weakness in cryptographic signature validation. It exploits the absence of issuer-to-account binding. A correctly-signed token from the wrong issuer is enough.


Detecting and Validating Tokens Locally

Before relying on external services to inspect suspicious JWTs, security teams should decode and validate tokens locally — without sending sensitive session data to online decoder services.

The OpSecForge JWT Decoder runs entirely client-side:

Decode JWTs Without Exposing Secrets

Stop pasting your tokens into online decoders that log your payload. Use our fully client-side JWT decoder to inspect headers and payloads without sending data to any server.

Open JWT Decoder →

When reviewing JWTs in your environment, always check:

  • iss (issuer) — Does it match the identity provider you configured for this tenant?
  • sub (subject) — Does this identity belong to this issuer's tenant?
  • aud (audience) — Is n8n the intended audience of this token?
  • exp (expiry) — Is the token still within its validity window?
  • Algorithm (alg) — Reject alg: none outright; prefer RS256 or ES256

⚡ Feature Grid: Related Security Tools

| Tool | What It Does | Why It Matters for This CVE | |------|-------------|---------------------------| | JWT Decoder | Decode and validate JWTs client-side | Inspect token claims (iss, sub, aud) before trusting them | | Env Sanitizer | Mask secrets in .env files | Prevent OAuth client secrets from leaking into logs or configs | | SHA Generator | Generate cryptographic hashes locally | Verify integrity of configuration artifacts | | Webhook Debugger | Verify webhook signatures | Confirm OAuth callback authenticity without external calls |


What the Fix Looks Like

The corrected token exchange implementation needs to bind issuer verification to account resolution. The fix introduces an explicit cross-check: when a JWT arrives, n8n must verify both the signature and that the verified issuer is the configured issuer for the tenant that owns the account named in the sub claim.

# VULNERABLE (simplified pseudocode)
def exchange_token(jwt, tenant_id):
    issuer = jwt["iss"]
    subject = jwt["sub"]

    # Verify signature against ANY trusted public key
    if not verify_signature(jwt):
        raise AuthenticationError("Invalid signature")

    # Look up subject across ALL tenants — no issuer check!
    account = db.find_account_by_email(subject)
    if not account:
        raise AuthenticationError("Account not found")

    return issue_session(account)


# FIXED (simplified pseudocode)
def exchange_token(jwt, tenant_id):
    issuer = jwt["iss"]
    subject = jwt["sub"]

    if not verify_signature(jwt):
        raise AuthenticationError("Invalid signature")

    # Resolve tenant from issuer config first
    issuer_config = db.find_issuer_config(issuer)
    if not issuer_config:
        raise AuthenticationError("Unknown issuer")

    # Only look up subject within the issuer's own tenant
    account = db.find_account_by_email_in_tenant(
        subject,
        tenant_id=issuer_config.tenant_id
    )
    if not account:
        raise AuthenticationError(
            "Account not found in issuer's tenant"
        )

    return issue_session(account)

The root cause fix is straightforward in retrospect — but it required recognizing that signature validity and issuer binding are two separate security checks, not one. OAuth implementations routinely make this mistake the moment multi-tenant, multi-issuer topologies enter the picture.


Broader Lesson: OAuth's Trust Boundary Problem

CVE-2026-59208 isn't an isolated bug. It belongs to a pattern that played out across multiple OAuth implementations in 2026:

"OAuth's failures this cycle aren't about a fragile parser the way SAML's are. They're about trust boundaries that are easy to state and surprisingly easy to get subtly wrong in code, especially the moment more than one issuer, more than one tenant, or more than one plugin enters the picture." — WorkOS Blog, July 2026

Five bugs, four codebases, one recurring mistake: checking that a token is valid instead of checking that it's valid for the identity it claims to represent.

This pattern shows up in:

| CVE | Product | Failure Mode | |-----|---------|-------------| | CVE-2026-57807 | miniOrange OAuth SSO (WordPress) | Auth bypass via alternate path in password recovery | | CVE-2026-48611 | Forum software OAuth | Default-installation auth bypass — OAuth reachable even when never configured | | CVE-2026-5721 | RabbitMQ management endpoint | OAuth client secret returned without auth from management API | | CVE-2026-59208 | n8n | Cross-issuer confusion in token exchange |


What You Should Do Now

  1. Check your n8n version. If you're running an enterprise deployment with OAuth token exchange enabled, confirm you're on the post-June 24 patch build. Token exchange is disabled by default, but if you've configured it, you're in scope.

  2. Audit all multi-tenant OAuth flows. If your application trusts JWTs from multiple issuers, verify that every token exchange or delegation path binds the issuer claim to the account resolution step. Missing this binding is the vulnerability pattern.

  3. Rotate any tokens from affected flows. If you had active sessions issued through n8n's token exchange during the vulnerable window, rotate them. Assume any token from a partner tenant could have been used cross-tenant.

  4. Add issuer-to-account binding to your OAuth code review checklist. For any implementation that handles tokens from more than one issuer, the review question is: "When we verify this signature, do we also confirm this issuer is allowed to vouch for this specific account?"

  5. Use local tools for sensitive token inspection. Before pasting a JWT into any online service, decode it locally. The JWT Decoder tool keeps your session data on your machine.


Summary

CVE-2026-59208 is a reminder that OAuth security is fundamentally a trust boundary problem, not a cryptographic one. n8n's token exchange correctly verified signatures — it just never checked whether the signing issuer was authorized to speak for the account it was presenting. In a multi-tenant world, that binding is everything.

The vulnerability was found by an AI pentesting agent, patched proactively by n8n before public disclosure, and serves as a clean case study for why OAuth implementations must treat issuer-to-account binding as a first-class security invariant — not an implementation detail.


Sources: WorkOS — OAuth's rough month: What five recent vulnerabilities say about token trust, IONIX — CVE-2026-59208 Threat Intelligence, StationX — Cloud Security Statistics 2026

Share this: