Decode a JWT in C# / .NET with System.IdentityModel.Tokens.Jwt
When ASP.NET Core rejects a bearer token, the first question is usually which claim caused the rejection. System.IdentityModel.Tokens.Jwt is distributed as a NuGet package: install it with dotnet add package System.IdentityModel.Tokens.Jwt.1 The JwtSecurityTokenHandler and JwtSecurityToken classes provide both a read-without-verify path and a full TokenValidationParameters-based validation path. Adding Microsoft.IdentityModel.JsonWebTokens via NuGet gives you the newer JsonWebToken API, which is preferred for .NET 6 and later. Paste any token into the decoder above for instant offline inspection without writing a line of C# or committing a token to shared server-backed logs.
Pasting production tokens into server-backed decoder tools exposes them to third parties. C# developers can decode any JWT locally in a console project using new JwtSecurityToken(token) without a network call. Because this approach reads the raw encoded payload, it works offline with no signing key. For production middleware in ASP.NET Core, JwtBearerAuthentication's AddAuthentication pipeline handles full validation automatically: but understanding the underlying claims object helps when debugging middleware configuration issues.
Library and setup
System.IdentityModel.Tokens.Jwt is distributed as a NuGet package: add it with dotnet add package System.IdentityModel.Tokens.Jwt. The JwtSecurityTokenHandler class is the primary entry point for both decoding and validation, providing a unified API surface that handles token parsing, signature verification, and claim extraction. For .NET 6 and later, Microsoft.IdentityModel.JsonWebTokens provides the newer JsonWebToken class with improved performance and async support, and both packages coexist so you can migrate incrementally without breaking existing code.
Choosing ReadJwtToken for debugging
Use ReadJwtToken when you need to inspect fixtures, logs, or a token captured during a failing request, because it gives you the same Claims collection your middleware will eventually read without requiring a signing key. Keep it out of protected endpoints, because it accepts any well-formed JWT regardless of issuer, audience, expiry, or signature, and a ReadJwtToken call inside a controller action that grants access based on the decoded sub or role claims effectively bypasses the entire authentication pipeline.
Decoding without verification
Reading a JWT payload without signature verification uses new JwtSecurityToken(token), which parses the header and payload directly from the Base64Url-encoded string without requiring a signing key, making it suitable for logging, debugging, or test fixture inspection where you trust the token source.2 The resulting JwtSecurityToken object exposes Claims as an IEnumerable<Claim>, the Header dictionary, and the Payload dictionary, giving you multiple ways to access any claim by type name using token.Claims.FirstOrDefault(c => c.Type == "sub")?.Value or by iterating over the full payload dictionary when you need to inspect every field. CapyToolkit's browser-based decoder performs the equivalent operation entirely offline, showing the same header and payload structure without sending your token to any server.
Reading specific claims
The Claims collection on a JwtSecurityToken maps each JWT claim to a .NET Claim object with Type and Value properties. Standard claims like sub, iss, and aud map to ClaimTypes constants in System.Security.Claims, which means your authorization code can reference ClaimTypes.Subject rather than a magic string. Building on this, JwtSecurityToken also exposes strongly typed properties: JwtSecurityToken.Subject (sub), ValidTo (exp as DateTime), IssuedAt (iat as DateTime), and Issuer (iss), so you can read registered claims with compile-time safety rather than string-based lookups.3 For provider-specific claims like realm_access or cognito:groups, access them via the Payload dictionary with payload["realm_access"] or by filtering Claims by type name, inspecting the dictionary first whenever a provider nests values so you choose the correct JSON shape.
Configuring TokenValidationParameters for RS256 in ASP.NET Core
Configuring RS256 token validation in ASP.NET Core uses the TokenValidationParameters object passed to AddJwtBearer, where you set IssuerSigningKeyResolver to a delegate that fetches JWKS from your provider and returns the matching SecurityKey by kid.4 Set ValidIssuer and ValidAudience to your expected values, and set RequireExpirationTime to true to enforce exp validation, ensuring that the middleware calls your resolver on each request and caches the key between calls for performance.
OIDC providers with AddMicrosoftIdentityWebApi
For Azure AD and other OIDC providers, Microsoft.IdentityModel.Protocols.OpenIdConnect handles JWKS discovery automatically. Call AddMicrosoftIdentityWebApi(configuration) instead of AddJwtBearer to configure OIDC-compliant validation in one line. The middleware fetches the JWKS from the provider's discovery document, validates tid, iss, and aud automatically, and refreshes keys on rotation without any manual configuration in your startup code. This single-call configuration replaces what would otherwise require dozens of lines of manual TokenValidationParameters setup, including JWKS endpoint URLs, issuer strings, audience values, and key rotation callbacks that are error-prone to maintain by hand.
Separating debug reads from production validation
Keep the decode-only JwtSecurityTokenHandler path separate from middleware validation, because reading a token without checking the signature is useful for local debugging but should never become the authorization path that protects production data. In ASP.NET Core, let the bearer middleware validate the token, then read HttpContext.User in your endpoint so every protected route follows the same key resolution and claim validation rules. Scattering ReadJwtToken calls across multiple controllers creates an inconsistent security model where some endpoints verify signatures and others trust raw payloads, making it nearly impossible to audit which routes are actually protected.
Centralising token reads through the authenticated middleware means every endpoint honours the same validation rules without per-controller exceptions. A new developer adding a route inherits the secure path by default rather than accidentally introducing a debug read in production. Periodic code review of where claims are read keeps the security model consistent as the codebase grows, and C# JWT claim reading with JwtSecurityToken shows the same header and claims that ReadJwtToken returns before you write any middleware.
When to use this
Use JwtSecurityToken for C# and ASP.NET Core projects that need to inspect JWT claims during middleware development, test fixture setup, or debugging authentication issues without running the full validation pipeline. The same header and claims ReadJwtToken would return are often faster to inspect without spinning up a console project.
Examples
Read JWT claims without verification in C#
using System.IdentityModel.Tokens.Jwt;
var token = "eyJ...";
var handler = new JwtSecurityTokenHandler();
var jwt = handler.ReadJwtToken(token);
var sub = jwt.Subject;
var exp = jwt.ValidTo;
var issuer = jwt.Issuer;
foreach (var claim in jwt.Claims)
{
Console.WriteLine($"{claim.Type}: {claim.Value}");
} handler.ReadJwtToken does not verify the signature. Use handler.ValidateToken for production validation.
Access a custom claim by type name
var handler = new JwtSecurityTokenHandler();
var jwt = handler.ReadJwtToken(token);
// Standard claim
var email = jwt.Claims
.FirstOrDefault(c => c.Type == "email")?.Value;
// Provider-specific claim
var role = jwt.Claims
.FirstOrDefault(c => c.Type == "role")?.Value; Provider-specific claim names (like "role" or "cognito:groups") are not mapped to ClaimTypes constants: match them by string.
- 1.
AzureAD, "azure-activedirectory-identitymodel-extensions-for-dotnet," github.com, accessed June 2026. https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet
- 2.
Microsoft, "JwtSecurityTokenHandler.ReadJwtToken Method," learn.microsoft.com, accessed June 2026. https://learn.microsoft.com/en-us/dotnet/api/system.identitymodel.tokens.jwt.jwtsecuritytokenhandler.readjwttoken
- 3.
Microsoft, "JwtSecurityToken Class," learn.microsoft.com, accessed June 2026. https://learn.microsoft.com/en-us/dotnet/api/system.identitymodel.tokens.jwt.jwtsecuritytoken
- 4.
Auth0, "ASP.NET Web API (OWIN): Authorization," auth0.com, accessed June 2026. https://auth0.com/docs/quickstart/backend/webapi-owin