A JWT is decoded with a read-only API that performs NO validation (new JwtSecurityToken(tokenString), handler.ReadJwtToken(...), or new JsonWebToken(tokenString)).
Why AI tools produce this: AI coding tools generate this anti-pattern by default, it appears in a large share of AI-written auth code.
Why this matters
These constructors/methods only parse the token; they do not check the signature, issuer, audience, or expiry. Trusting the claims that come back lets an attacker forge any identity or role by hand-crafting an unsigned token (CWE-345). This is a common AI-generated shortcut for "reading the user id from the token" that silently skips verification.
Validate the token before trusting its claims: call handler.ValidateToken(token, tokenValidationParameters, out var validated) (or await handler.ValidateTokenAsync(...)) and read claims from the validated result. Only parse a token unvalidated when you are inspecting a token you just issued, never one received from a client.
VULNERABLE
vulnerable.cs
using System.IdentityModel.Tokens.Jwt;using Microsoft.IdentityModel.JsonWebTokens;public class TokenReader{ public string GetUserId(string incomingToken) { // ruleid: auth.csharp.jwt.read-without-validation var parsed = new JwtSecurityToken(incomingToken); return parsed.Subject; } public string GetIssuer(string incomingToken) { var handler = new JwtSecurityTokenHandler(); // ruleid: auth.csharp.jwt.read-without-validation var jwt = handler.ReadJwtToken(incomingToken); return jwt.Issuer; } public string GetAudience(string incomingToken) { // ruleid: auth.csharp.jwt.read-without-validation var jwt = new JsonWebToken(incomingToken); return jwt.Audiences.ToString(); }}
SAFE
safe.cs
using System;using System.IdentityModel.Tokens.Jwt;using System.Security.Claims;using Microsoft.IdentityModel.Tokens;public class TokenReader{ // Claims are read from a VALIDATED token, not a raw parse. public string GetUserId(string incomingToken, TokenValidationParameters parameters) { var handler = new JwtSecurityTokenHandler(); ClaimsPrincipal principal = handler.ValidateToken(incomingToken, parameters, out SecurityToken validated); return principal.FindFirst(ClaimTypes.NameIdentifier)?.Value; } // Multi-argument constructor CREATES a token we are about to sign/issue — this // is not a decode-and-trust of an incoming token, so it must not fire. public JwtSecurityToken Issue(string issuer, string audience, SigningCredentials creds) { return new JwtSecurityToken(issuer, audience, null, DateTime.UtcNow, DateTime.UtcNow.AddMinutes(5), creds); }}
Suppressing this rule
If a finding is a genuine false positive, scope the suppression to the exact line and leave a reason, never disable the rule project-wide. Disable directives are line-scoped by design.