A JWT is decoded in C# by splitting the compact token on '.' into three Base64url segments and JSON-parsing the first two — the same first step that any library or browser helper performs. The header carries the algorithm identifier and token type, the payload carries the registered and custom claims, and the signature segment is left alone because verifying it requires the signing key the issuing application owns. In a typical C# codebase you reach for System.IdentityModel.Tokens.Jwt and call JwtSecurityTokenHandler.ReadJwtToken, which performs the splitting, Base64url conversion, and JSON parsing for you and returns a JwtSecurityToken with a strongly typed Claims collection. If you would rather avoid a NuGet dependency, the same work fits in a handful of lines using Convert.FromBase64CharArray with the URL-safe alphabet and System.Text.Json.JsonDocument to walk the parsed objects. None of those steps verify the signature, so the decoded payload is only safe to trust when it came from a system whose keys and validation policy you already control. A local browser helper like JWT Decoder applies the identical decode-only operation to give you a quick read of the header and payload without uploading the token.

how to decode jwt token in c#
Decode a JWT Token in C# Without a NuGet Library

What a Compact JWT Actually Looks Like

A JWT in compact serialization is exactly three Base64url strings joined by periods: header.payload.signature. The header describes the algorithm and token type, the payload carries the claims, and the signature segment is the bytes the issuer produced with its signing key. RFC 7519 defines the structure and the registered claim names, while RFC 4648 defines the URL-safe Base64 alphabet of letters, digits, hyphen, and underscore that compact JWTs use, with padding normally omitted.

You do not need the signature to read what the token says. Both segments you care about are plain UTF-8 JSON after you replace the URL-safe alphabet characters and pad the length to a multiple of four. C# libraries handle that conversion for you, and so does JWT Decoder when you paste the same compact string into the browser.

Decode a JWT Token in C# Step by Step

  1. Capture the token as a string in header.payload.signature form, then call Split('.') to obtain three substrings.
  2. Take the first two substrings, swap - for + and _ for /, and pad each with = until its length is a multiple of four so the URL-safe string becomes valid standard Base64.
  3. Pass each padded string to Convert.FromBase64String to get the raw header and payload bytes, then decode those bytes as UTF-8 with Encoding.UTF8.GetString.
  4. Hand each decoded string to JsonDocument.Parse and read RootElement as a JSON object so the result is type-safe to walk.
  5. Inspect alg and typ from the header, then walk the registered claims iss, sub, aud, exp, nbf, iat, and jti on the payload, treating every value as unverified information.

Wrapped in a small helper, the same five steps look like this in a .NET console project:

var handler = new JwtSecurityTokenHandler();

var token = handler.ReadJwtToken(jwtString);

Console.WriteLine(token.Header.Alg);

foreach (var c in token.Claims) Console.WriteLine(c.Type + ": " + c.Value);

The ReadJwtToken call performs steps one through four internally and exposes the parsed header and a Claims collection that has already mapped the registered names to ClaimTypes equivalents. For a library-free path, replace the first two lines with your own Base64url-to-UTF-8 conversion and your own JsonDocument.Parse call on each segment. The end result is the same dictionary of header fields and payload claims you would see in any decoder, and you control every byte that passes through your code.

Three Decoding Approaches in C# Compared

ApproachPackage neededWhat it returnsSignature check
JwtSecurityTokenHandler.ReadJwtTokenSystem.IdentityModel.Tokens.JwtJwtSecurityToken with Header and ClaimsNo, unless you call ValidateToken
Manual Base64url + JsonDocumentNone beyond System.Text.JsonTwo JsonDocument objects for header and payloadNo, requires a hand-rolled HMAC or RSA check
JWT Decoder in the browserNone, runs locallyDecoded header and payload as JSON plus recognized registered claimsNo, labels every result as unverified

The library-free approach and the browser helper look almost identical because both stop at decoding. The differences are where the code runs and which inputs the parser accepts. A C# method will not tolerate a stray equals sign or a token that is missing one of the three segments; a careful implementation reports those as errors rather than guessing. JWT Decoder applies the same strict rules: it rejects padding characters, requires exactly three segments, and demands that the decoded header and payload both parse as JSON objects.

Reading the Header and Payload Once Decoded

The header normally contains two fields: alg, which names the signing algorithm such as HS256 or RS256, and typ, which is usually JWT. Treat any unexpected alg value as a red flag, because algorithm confusion is a common JWT attack pattern and a real validator should refuse the token before you act on its payload.

The payload carries RFC 7519 registered claims plus whatever custom claims the issuer chose to add. The registered names have specific meanings that the decoder recognizes:

ClaimRFC 7519 nameTypeWhat it usually means
issIssuerStringThe principal that issued the token
subSubjectStringThe principal the token is about, often a user id
audAudienceString or arrayThe intended recipient identifier
expExpiration TimeNumericDateUnix seconds after which the token must be rejected
nbfNot BeforeNumericDateUnix seconds before which the token must be rejected
iatIssued AtNumericDateUnix seconds when the token was created
jtiJWT IDStringA unique identifier used to prevent replay

NumericDate is a plain integer count of seconds since the Unix epoch, which is why exp, nbf, and iat all look like ten-digit numbers. JWT Decoder and most C# libraries render the same value as both the raw integer and an ISO 8601 timestamp so it is easier to compare against your system clock. The audience claim can appear as a single string or an array, and you have to check it against the identifier your application expects; nothing in the decoder enforces that match for you. The full set of registered names is defined in RFC 7519, which is the canonical source for what every claim is supposed to mean.

Where JWT Decoder Fits Alongside C# Code

C# decoding is the right tool when the token lives inside your service, when you want the parsed values to flow into a typed object, or when you need to drive validation logic from the same call. There are plenty of moments during development, however, when a token is sitting in a network panel, a log line, or a Postman response and you only want to read what it says. Pasting the string into JWT Decoder does exactly that: it splits the same three segments, runs the same Base64url-to-UTF-8 conversion in your browser, and renders the header and payload without uploading the token or contacting any endpoint.

The browser helper is intentionally narrow. It does not accept a signing key, it does not fetch a JWKS document, and it does not run your application's validation policy. Every successful decode is labelled as unverified, which makes it useful as a development inspection step rather than a substitute for the validation code you ship with your service. For a code-free reading workflow that mirrors the same idea, see the guide on decoding JWT tokens without code in your browser, which uses the same tool from a slightly different angle and is useful when you want to share a read-only inspection recipe with teammates who do not write C#.

Why Decoding Is Never Verification

Decoding a JWT in C#, in a browser, or on the command line never proves the token is authentic. A readable payload can be forged, expired, signed with a key you do not trust, or issued for an entirely different audience. JwtSecurityTokenHandler.ValidateToken is the call that performs verification, and it needs the signing key, the expected issuer, the expected audience, and a clock skew tolerance to do its job. JWT Decoder never makes an authentication decision and labels every result as unverified so a quick inspection step is not mistaken for a security check.

Tokens can also carry personal identifiers, tenant data, scopes, or other sensitive application details even when they are not passwords. Avoid pasting production credentials into shared screens, screenshots, or third-party online decoders that upload what you type. If you need to validate a token, use a maintained JWT library in the service that owns the security decision, verify the algorithm, key, issuer, audience, expiry, and any application-specific requirements there, and remove the token from any local input field when you are done.

For a deeper look, see Generate a JWT Token in Postman with HS256 Locally.