Jwt Auth

By the end of this lesson you'll understand exactly what a JSON Web Token is made of, be able to read a token apart in plain C#, check whether it has expired, and issue and validate real tokens in ASP.NET Core with JwtSecurityTokenHandler and AddJwtBearer — the foundation of stateless API security.

Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.

Part of the free C# course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

A JWT is a tamper-proof festival wristband . When you arrive, the box office checks your ticket once and snaps a wristband on you (the server checks your password once and issues a token). The band is printed with your details — your name, your zone, the day it expires — so anyone can read it (the payload is not secret). But it's also embossed with a special foil the festival prints (the signature ): a guard at any stage can glance at it and know it's genuine and untampered without phoning the box office (no database lookup). Try to peel it off and re-stick it on a friend and the foil tears — the signature no longer matches. And the band expires at midnight: after that, no stage lets you in, and you'd need to go back to the box office for a fresh one.

A JWT is a single string: three Base64Url-encoded segments joined by dots — header.payload.signature . Base64Url is a URL-safe way of encoding bytes as text; it is encoding, not encryption , so the header and payload can be read by anyone who has the token. The signature is the only part that needs a secret to produce.

The claims in the payload come in two flavours: registered claims with standard short names, and custom claims you invent. Common registered claims:

1. Reading a Token Apart

Before you reach for any library, it helps to see that a JWT really is just a string. Splitting it on the . character gives you the three Base64Url segments. The header and payload are only encoded , so you can read them — which is exactly why you must never put a password or secret in the payload. Read this worked example, run it, then you'll split a token yourself.

Your turn. The program below is almost complete — fill in the two ___ blanks using the hints in the comments, then run it and check your output against the expected lines.

2. Claims and Signing

The payload carries claims — statements about the user such as "subject is user-123" and "role is Admin". The signature is what makes the token trustworthy: the server runs the header and payload through a one-way function together with a secret, and attaches the result. Change a single character of the payload and the signature no longer matches, so the token is rejected.

There are two families of signing. HMAC (e.g. HS256 ) uses one shared secret to both sign and verify — simple, fast, and ideal when the same service issues and checks tokens. RSA/ECDSA (e.g. RS256 ) uses a private key to sign and a separate public key to verify — so many services can validate tokens without ever holding the secret that creates them. That's the right choice for microservices and third-party APIs.

HMAC (symmetric): one secret signs and verifies. If any verifier is compromised, the attacker can also forge tokens — because verifying and signing use the same key. Keep that key on the issuing service only.

RSA / ECDSA (asymmetric): a private key signs, a public key verifies. You can hand the public key to every downstream service freely — it can prove a token is genuine but cannot create one. This is why identity providers publish their public keys at a well-known URL.

3. Issuing & Validating with JwtSecurityTokenHandler

In .NET, the type that builds and checks tokens is JwtSecurityTokenHandler . To issue a token you wrap your secret in a SymmetricSecurityKey , list your claims, set an expiry, and call WriteToken . To validate one you pass it through ValidateToken with a set of TokenValidationParameters describing what "valid" means — the right issuer, the right audience, an unexpired lifetime, and a signature that matches your key. This worked example does both in one run.

4. Checking Expiry Yourself

The exp claim is stored as a Unix timestamp — the number of seconds since 1 January 1970. The library checks expiry for you, but knowing how to do it by hand makes the concept concrete and is useful when you decode a token outside ASP.NET. DateTimeOffset.FromUnixTimeSeconds(...) turns those seconds into a real date you can compare against DateTimeOffset.UtcNow .

Your turn. Fill in the two ___ blanks below to convert the timestamp and decide whether it's in the past:

5. JWT Bearer Authentication in ASP.NET Core

In a real API you rarely call JwtSecurityTokenHandler by hand on every request. Instead you register JWT Bearer authentication once with AddAuthentication(...).AddJwtBearer(...) and the framework validates the Authorization: Bearer ... header automatically. Add app.UseAuthentication() before app.UseAuthorization() , then protect any endpoint with RequireAuthorization() (or the [Authorize] attribute on a controller).

This snippet is a complete ASP.NET Core minimal API — paste it into a web project's Program.cs . The validation rules mirror the ones from section 3; the difference is the framework now applies them to every incoming request for you.

No. The header and payload are only Base64Url encoded , so anyone with the token can decode and read them. Only the signature uses a secret. Never put sensitive data in a claim.

Q: If anyone can read it, what stops someone editing the payload?

The signature. If you change even one character, the signature no longer matches the secret, and validation fails. That's how the server trusts a token without a database lookup.

Q: Where should I store the token in a browser?

In an HttpOnly , Secure cookie, not localStorage . An HttpOnly cookie is invisible to JavaScript, which shuts down the most common XSS theft path.

Q: What's the difference between authentication and authorization?

Authentication answers "who are you?" (the token is valid and identifies a user). Authorization answers "are you allowed to do this?" (the user's role or claims permit the action). In ASP.NET, UseAuthentication comes before UseAuthorization .

Use HMAC (HS256) when the same service issues and validates tokens. Use RSA (RS256) when separate services need to validate tokens — they get the public key and can verify without ever holding the signing secret.

No blanks this time — just a brief and an outline. Using only plain logic (no libraries), validate the token: it must split into exactly three parts, its expiry must not be in the past, and its issuer must match the one you expect. Print ✅ Token accepted if all three pass, otherwise ❌ Token rejected . Run it and check it against the expected line in the comments.

Practice quiz

What are the three parts of a JWT, joined by dots?

  • key.value.hash
  • user.role.expiry
  • header.payload.signature
  • issuer.audience.subject

Answer: header.payload.signature. A JWT is a single string of three Base64Url segments: header, payload, and signature, separated by dots.

Is the payload of a JWT encrypted?

  • No, it is only Base64Url-encoded and readable by anyone
  • Yes, only the server can read it
  • Yes, with the signing key
  • Only if HTTPS is used

Answer: No, it is only Base64Url-encoded and readable by anyone. The header and payload are only encoded (Base64Url), not encrypted — anyone with the token can read them, so never put secrets in the payload.

What is the purpose of the signature?

  • To encrypt the payload
  • To store the user's password
  • To compress the token
  • To prove the token wasn't tampered with

Answer: To prove the token wasn't tampered with. The signature proves the token wasn't altered; change one character of the payload and the signature no longer matches, so validation fails.

What does the 'sub' registered claim represent?

  • The signing algorithm
  • The subject — who the token is about (the user id)
  • The expiry time
  • The audience

Answer: The subject — who the token is about (the user id). The 'sub' (subject) claim identifies who the token is about, typically the user id.

What does the 'exp' claim store?

  • A Unix timestamp (seconds) after which the token is invalid
  • The issuer name
  • The encryption mode
  • The user's role

Answer: A Unix timestamp (seconds) after which the token is invalid. The 'exp' (expiry) claim is a Unix timestamp in seconds; after that moment the token is considered expired.

How does HMAC (HS256) signing differ from RSA (RS256)?

  • HMAC uses a key pair; RSA uses one shared secret
  • They are identical
  • HMAC uses one shared secret to sign and verify; RSA uses a private key to sign and a public key to verify
  • RSA cannot verify tokens

Answer: HMAC uses one shared secret to sign and verify; RSA uses a private key to sign and a public key to verify. HMAC uses a single shared secret for both signing and verifying; RSA uses a private key to sign and a separate public key to verify.

Why is RSA (RS256) preferred when multiple services validate tokens?

  • It is faster than HMAC
  • Each verifier only needs the public key and cannot forge tokens
  • It produces shorter tokens
  • It encrypts the payload

Answer: Each verifier only needs the public key and cannot forge tokens. With RSA, downstream services get the public key — they can verify a token is genuine but cannot create one, unlike HMAC's shared secret.

Where should a JWT be stored in a browser to reduce XSS theft?

  • In localStorage
  • In a global JavaScript variable
  • In the URL
  • In an HttpOnly cookie, invisible to JavaScript

Answer: In an HttpOnly cookie, invisible to JavaScript. An HttpOnly cookie can't be read by client-side JavaScript, shutting down the most common XSS token-theft path that localStorage exposes.

What is the difference between authentication and authorization?

  • They are the same thing
  • Authentication asks 'who are you?'; authorization asks 'are you allowed?'
  • Authentication asks 'are you allowed?'; authorization asks 'who are you?'
  • Authentication is only for APIs

Answer: Authentication asks 'who are you?'; authorization asks 'are you allowed?'. Authentication establishes identity (who you are); authorization decides permission (whether you may do something). UseAuthentication comes before UseAuthorization.

In ASP.NET Core, which middleware must come first in the pipeline?

  • app.UseAuthorization() before app.UseAuthentication()
  • Order doesn't matter
  • app.UseAuthentication() before app.UseAuthorization()
  • Both must be registered as services only

Answer: app.UseAuthentication() before app.UseAuthorization(). Authentication (who are you?) must run before authorization (are you allowed?), so UseAuthentication() comes before UseAuthorization().