Microsoft Titan JWT Vulnerability: Why You Should Never Use upn as a Local Username

Researcher forged a JWT upn claim to gain admin access via Microsoft's missing signature verification and field misuse.
A security researcher discovered two compounding flaws in Microsoft's Titan internal service: the server failed to validate JWT signatures, allowing arbitrary payload tampering, and it used the `upn` field directly for local permission lookups — meaning forging `upn` as `admin` bypassed authorization entirely. The article explains why `upn` is mutable and unsuitable as a permissions primary key, and why immutable identifiers like `oid` should be used instead. The core lesson: security vulnerabilities often arise from multiple plausible design decisions stacking up.
How a Tampered JWT Unlocked an Internal Service
A security researcher gained access to one of Microsoft's internal services simply by setting the upn claim in a JWT (JSON Web Token) to admin. Behind this seemingly straightforward attack chain lie two critical security design flaws: missing signature verification was the key that opened the door, while using upn as a local lookup key was the real vulnerability.
This type of issue is far from uncommon in authentication systems, but this incident involving Microsoft's Titan service clearly illustrates the cascading consequences when developers misunderstand the trust boundaries of token fields.

Breaking Down the Vulnerability: Missing Signature + Field Misuse
Missing Signature: The Door Was Unlocked
The security of a JWT fundamentally depends on signature verification. A standard JWT consists of three parts — a header, a payload, and a signature. The claims in the payload are essentially plaintext data encoded in Base64; anyone can read or even modify them. What actually prevents tampering is the server's strict verification of the signature.
In this case, the internal service failed to properly validate the token signature, leaving the door wide open for attackers. The researcher was free to forge payload content — including overwriting the upn (User Principal Name) field with admin. Once signature verification fails, no claim in the token can be trusted.
The signature algorithm is declared in the JWT header, and this opens the door to a classic attack variant: setting the algorithm to
none, which causes some implementations to skip signature verification entirely. Another common attack involves switching the algorithm from the asymmetricRS256(RSA signature) to the symmetricHS256(HMAC signature), then "signing" the token with the server's RSA public key — if the server implementation is flawed, it will use that same public key for HMAC verification, allowing an attacker to forge valid tokens. This demonstrates that signature verification must not only check the signature value itself, but also enforce a fixed allowlist of permitted algorithms rather than trusting whatever algorithm the token header declares.
upn as a Lookup Key: A Flawed Trust Assumption
If the missing signature was the key that opened the door, then using upn as a local username for permission lookups is where the vulnerability's real damage originates.
upn is, by nature, a user-controllable or at least forgeable identifier. When a service directly maps the upn value to a local user account or permission record, and that value can be manipulated by an attacker — say, forged as admin — the entire authorization logic is bypassed. The system incorrectly assumes the request comes from an administrator and grants access it never should have.
Why You Should Never Use upn as a Local Username
The most valuable lesson from this incident can be distilled into a single rule: never use upn as a local username.
The reason comes down to the stability and uniqueness guarantees of identifiers. upn was designed as a user-friendly login name. It can change when a user's email address changes or when a domain is migrated — it is not guaranteed to remain constant over a system's lifetime, nor is it a strictly immutable unique identifier. A more reliable approach is to use immutable, globally unique identifiers such as oid / sub (object ID or subject identifier) for user mapping and permission lookups.
When a developer uses a semantically mutable field — one that may be externally controlled — as the primary key in a permissions system, they plant the seeds of identity confusion and privilege escalation. In this case, that risk was activated by the missing signature verification, but even with proper signature validation, using upn as a lookup key can still cause account confusion in certain scenarios.
In Microsoft's identity platform,
oid(Object ID) is the immutable, unique identifier for each user within an Azure AD tenant. Even if a user changes their email address, UPN, or display name,oidnever changes.sub(Subject) is the principal identifier defined by the OAuth 2.0 standard, which remains stable for a given user within the same application.upn, by contrast, corresponds to the user'suser@domain.comformat login name and can change due to corporate domain rebranding, account renaming, or cross-tenant migration. In multi-tenant scenarios, two different tenants can easily have users with the sameupn, so usingupnfor global matching can cause cross-tenant identity confusion. The correct mapping strategy is to use the combination ofoid+tid(Tenant ID) as the stable primary key for a user.
Practical Takeaways for Developers
From this vulnerability, developers building authentication systems can extract several general principles:
- Enforce strict signature verification: Any JWT received from a client must undergo complete signature validation — including the algorithm, key, and expiration time — without exception or shortcuts.
- Distinguish mutable from immutable identifiers: Permission lookups and user mapping should be based on immutable unique IDs, not on mutable fields like
upnor email addresses. - Principle of least trust: All claims in a token should be treated as untrusted data until validation is complete, especially any fields used in authorization decisions.
- Defense in depth: Even if a single validation step fails, downstream authorization logic should operate under independent security assumptions, preventing one oversight from causing a complete collapse.
This type of vulnerability rarely stems from a single mistake — it's typically the result of multiple seemingly reasonable design decisions compounding on each other. Tightening either link in the chain — lax signature verification or careless field usage — could have broken the attack. The value of secure design lies precisely in the rigor applied to these details.
Related articles

Chain of Thought Isn't Just for AI: How Humans Can Use CoT to Improve Thinking Quality
Chain of Thought isn't just a prompt trick for large models. Starting from a Hacker News discussion, this article analyzes why CoT works for human thinking too, breaks down actionable ways to transfer it, and highlights the key differences between human CoT and machine CoT.
Cloudflare Teams Up with OpenAI: Closi…
Cloudflare Teams Up with OpenAI: Closing the Loop from Vulnerability Discovery to Defense with AI
Cloudflare uses OpenAI Daybreak to investigate security vulnerabilities and generate reviewable remediation plans, while developers use Codex to boost coding efficiency and quality.

Orca Multi-Agent Orchestration in Practice: A Guide to Parallel Collaboration with Claude and Codex
Orca is Stable AI's free open-source agent workbench supporting 34 agents like Claude Code and Codex in parallel. Learn its Git worktree isolation, multi-model orchestration, and mobile management.