Security

Issuing and validating JWT access tokens in ASP.NET Core

This guide walks through issuing signed 15-minute access tokens for ASP.NET Core Identity users with JsonWebTokenHandler and validating them strictly for issuer, audience, lifetime and signature on the JwtBearer side. It also flags the common traps: claim type mapping that hides sub, calling AddIdentity in an API, and authentication middleware registered in the wrong order.

Published
Reading time
15 min read
agent run #24
agent run #24
Go to original dev.to

Report this story

Is something wrong with the summary, or do you own this content? Your message comes straight to my inbox.

Type of message

Key points

  • Issuing and validating go through the same JsonWebTokenHandler, which JwtBearer has used by default since .NET 8
  • HS256 needs at least a 32-byte key; shorter keys fail at token creation with IDX10720, so validate length at startup
  • Access tokens expire in 15 minutes and validation allows a 30-second clock skew alongside strict issuer, audience and lifetime checks
  • A JWT is signed, not encrypted: anyone can read the claims, so keep secrets and personal data out of them
  • Turn off claim type mapping or the raw sub claim is hidden and ICurrentUser.UserId comes back empty
  • Skipping base.OnModelCreating(builder) in IdentityDbContext<AppUser> makes EF Core fail with a missing primary key

Starting from a scenario where an Angular app and a mobile client share one Store.Api, the guide builds registration, login and endpoint protection on ASP.NET Core Identity. Tokens are created with JsonWebTokenHandler, the same handler JwtBearer has used for validation since .NET 8, so both sides stay on one code path. Because HS256 needs a key of at least 32 bytes, the article checks key length at startup and keeps the secret in user secrets or an environment variable rather than appsettings.json.

The token carries sub, email, role and jti claims, lives for 15 minutes, and validation allows a 30-second clock skew alongside strict issuer and audience checks. A key caveat: a signed JWT is not encrypted, so anyone holding it can read the claims and no sensitive data belongs there. Services get an ICurrentUser abstraction instead of touching HttpContext, which only works once claim type mapping is switched off so the raw sub claim stays readable.

Real-world use

  • A multi-client API serving both a web and a mobile app, where cookies do not work and Bearer tokens are required
  • Layered applications where services such as order placement or auditing need the caller's identity without depending on HttpContext
  • An auth service that reads time from TimeProvider so expiry can be tested with FakeTimeProvider instead of waiting
  • A login flow that counts failed attempts and locks an account for 15 minutes after five failures

Why it matters

It collects the mistakes teams most often make when wiring authentication for the first time, from claim mapping to middleware order to short signing keys, all of which show up in production as 401s or security gaps.

This summary was generated by the agent from the original post on DEV Community: #aspnetcore. The full text and all rights belong to its author and publisher. Go to original

Related stories

Feedback

Found a bug or have an idea? Your message lands straight in my inbox.

Type of message