Skip to content

Aspnet Jwt

DodaTech 5 min read

title: ASP.NET Core JWT Authentication — Complete Guide to Token Auth description: 'Learn ASP.NET Core JWT authentication: issuing tokens, bearer validation, refresh tokens, claims-based auth, secured API endpoints, and token security best practices.' date: 2026-06-28 lastmod: 2026-06-28 weight: 25 tags: [backend, aspnet]


ASP.NET Core JWT authentication uses JSON Web Tokens for stateless API authentication, with bearer token validation, claims extraction, and secure token issuance.

## What You'll Learn

By the end of this tutorial, you'll configure JWT bearer authentication, issue access and refresh tokens, implement login/register with JWT, use claims for authorization, and secure API endpoints.

## Real-World Use

A mobile app calls a .NET Web API. Users log in once, receive a JWT (15 min expiry) and refresh token (7 days). The API validates the JWT on every request without database lookups.

## JWT Learning Path

```mermaid
flowchart LR
  A[Auth] --> B[JWT]
  B --> C[Web API]
  C --> D[Minimal API]
  D --> E[SignalR]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff

JWT Configuration

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"] ?? ""))
        };
    });

builder.Services.AddAuthorization();

Token Service

public interface ITokenService
{
    string GenerateAccessToken(IdentityUser user);
    string GenerateRefreshToken();
    ClaimsPrincipal? GetPrincipalFromExpiredToken(string token);
}

public class TokenService : ITokenService
{
    private readonly IConfiguration _config;
    public TokenService(IConfiguration config) => _config = config;
    
    public string GenerateAccessToken(IdentityUser user)
    {
        var key = new SymmetricSecurityKey(
            Encoding.UTF8.GetBytes(_config["Jwt:Key"] ?? ""));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        var claims = new[]
        {
            new Claim(ClaimTypes.NameIdentifier, user.Id),
            new Claim(ClaimTypes.Email, user.Email ?? ""),
            new Claim(ClaimTypes.Role, "User")
        };
        var token = new JwtSecurityToken(
            issuer: _config["Jwt:Issuer"],
            audience: _config["Jwt:Audience"],
            claims: claims,
            expires: DateTime.UtcNow.AddMinutes(15),
            signingCredentials: creds
        );
        return new JwtSecurityTokenHandler().WriteToken(token);
    }
    public string GenerateRefreshToken()
    {
        var randomBytes = new byte[64];
        using var rng = RandomNumberGenerator.Create();
        rng.GetBytes(randomBytes);
        return Convert.ToBase64String(randomBytes);
    }
}

Auth Controller with JWT

[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly ITokenService _tokenService;
    public AuthController(UserManager<IdentityUser> um, ITokenService ts)
    {
        _userManager = um;
        _tokenService = ts;
    }
    [HttpPost("login")]
    public async Task<IActionResult> Login(LoginDto dto)
    {
        var user = await _userManager.FindByEmailAsync(dto.Email);
        if (user == null || !await _userManager.CheckPasswordAsync(user, dto.Password))
            return Unauthorized();
        var accessToken = _tokenService.GenerateAccessToken(user);
        var refreshToken = _tokenService.GenerateRefreshToken();
        // Store refresh token (database or distributed cache)
        return Ok(new AuthResponse(accessToken, refreshToken, 15 * 60));
    }
    [HttpPost("refresh")]
    public async Task<IActionResult> Refresh(TokenRefreshDto dto)
    {
        // Validate refresh token from storage
        // Generate new access token
        // Return new token pair
    }
}

Securing Endpoints

[ApiController]
[Route("api/products")]
[Authorize]  // All actions require valid JWT
public class ProductsController : ControllerBase
{
    [HttpGet]
    public IActionResult GetAll()
    {
        var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
        var email = User.FindFirstValue(ClaimTypes.Email);
        return Ok(new { UserId = userId, Email = email });
    }
    [HttpPost]
    [Authorize(Roles = "Admin")]  // Admin-only
    public IActionResult Create(ProductDto product) { ... }
}

// Minimal API with JWT
app.MapGet("/api/profile", [Authorize] (HttpContext ctx) =>
{
    var userId = ctx.User.FindFirstValue(ClaimTypes.NameIdentifier);
    return Results.Ok(new { UserId = userId });
});

Refresh Token Storage

public class RefreshToken
{
    public int Id { get; set; }
    public string UserId { get; set; } = "";
    public string Token { get; set; } = "";
    public DateTime ExpiresAt { get; set; }
    public bool IsRevoked { get; set; }
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
// DbSet<RefreshToken> in AppDbContext
// Store on login, validate on refresh, revoke on logout

Common Mistakes

1. Weak Signing Key

Short or predictable keys allow token forgery. Use a 256-bit cryptographically random key.

2. Long Token Expiration

JWTs can't be revoked. Short expirations (15-60 min) limit damage if tokens leak.

3. Not Using HTTPS

JWT tokens transmitted over HTTP are vulnerable to interception. Always use HTTPS.

4. Storing Sensitive Claims in JWT

JWT payload is base64 (not encrypted). Never store passwords or secrets in claims.

5. Not Handling Token Renewal

Without refresh tokens, users must re-login every 15 minutes. Implement refresh token flow.

Practice Questions

1. How does JWT authentication work in ASP.NET Core?

The server validates the Bearer token in the Authorization header, extracts claims, and creates a User principal.

2. What is contained in a JWT token?

Header (algorithm), Payload (claims, expiration), Signature (verification).

3. How do you add custom claims to a JWT?

Include new Claim() objects in the claims array when creating the JwtSecurityToken.

4. What is the purpose of refresh tokens?

Refresh tokens allow issuing new access tokens without requiring the user to re-authenticate.

5. Challenge: Create a complete JWT auth flow with login, protected endpoint, and token refresh.

[HttpPost("login")]
public async Task<IActionResult> Login(LoginDto dto)
{
    // Validate credentials
    // Generate access token (15 min) and refresh token (7 days)
    // Store refresh token hashed
    // Return both tokens
}
[Authorize]
[HttpGet("profile")]
public IActionResult GetProfile()
{
    // Extract user ID from JWT claims
    // Return user data
}

FAQ

Should I store JWTs in localStorage or cookies?

httpOnly cookies are safer against XSS. localStorage is accessible by JavaScript.

How do I revoke a JWT?

JWTs are stateless and can't be revoked. Use short expirations + a token blacklist or refresh token revocation.

What is the difference between JWT and OAuth 2.0?

JWT is a token format. OAuth 2.0 is an authorization framework that can use JWT as the token format.

Can I use JWT with Identity?

Yes. Use Identity for user management and issue your own JWTs. Don't use Identity's cookie auth for APIs.

How do I handle token expiration in the client?

Intercept 401 responses, call the refresh endpoint, retry the original request with the new token.

Mini Project: JWT Auth API

Build a complete JWT authentication API with login, protected endpoints, and refresh.

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(o => o.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("super-secret-key-1234567890-32bytes!!")),
        ValidateIssuer = false,
        ValidateAudience = false
    });
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapPost("/login", (LoginDto dto) => { /* return JWT */ });
app.MapGet("/protected", [Authorize] () => "This is protected");

What's Next

ASP.NET Core Web API ASP.NET Core Minimal API

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro