JWT Anatomy & JwtBearer Middleware
A JWT (JSON Web Token) consists of three Base64URL-encoded parts: Header (algorithm), Payload (claims), and Signature (HMAC or RSA). The server validates the signature on every request — no session lookup, fully stateless. Understanding claims lets you embed user identity and roles in the token itself.
JWT Structure & JwtBearer Configuration
// JWT structure (Base64URL decoded):
// Header: { "alg": "HS256", "typ": "JWT" }
// Payload: {
// "sub": "user-guid-here", ← subject (user ID)
// "email": "user@example.com",
// "role": ["Admin", "User"], ← roles → [Authorize(Roles="Admin")]
// "jti": "unique-token-id", ← JWT ID (for token revocation)
// "iat": 1711000000, ← issued at (Unix timestamp)
// "exp": 1711001800, ← expires (15 minutes from iat)
// "iss": "https://myapp.com", ← issuer
// "aud": "https://myapp.com" ← audience
// }
// Signature: HMACSHA256(base64Header + "." + base64Payload, secretKey)
// ━━ JwtBearer registration ━━
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidateAudience = true,
ValidAudience = builder.Configuration["Jwt:Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"]!)),
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero, // IMPORTANT: zero clock skew for short-lived tokens
RoleClaimType = ClaimTypes.Role, // map "role" claim → User.IsInRole("Admin")
NameClaimType = ClaimTypes.NameIdentifier,
};
// ━━ Extract token from cookies OR Authorization header ━━
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// 1. Try cookie first (httpOnly — immune to XSS)
if (context.Request.Cookies.TryGetValue("access_token", out var cookieToken))
context.Token = cookieToken;
// 2. Fallback to Authorization: Bearer header
return Task.CompletedTask;
},
OnChallenge = context =>
{
// Customize 401 response
context.HandleResponse();
context.Response.StatusCode = 401;
return context.Response.WriteAsJsonAsync(new
{
error = "Unauthorized",
message = "Valid JWT required"
});
}
};
});Tip
Tip
Practice JWT Anatomy JwtBearer Middleware in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
.NET provides a unified platform for building various application types
Practice Task
Note
Practice Task — (1) Write a working example of JWT Anatomy JwtBearer Middleware from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with JWT Anatomy JwtBearer Middleware is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready dotnet code.