dotnet-jwt-authentication
Configures JWT Bearer authentication for .NET APIs. Includes token generation, validation, refresh tokens, and user context extraction from claims.
git clone --depth 1 https://github.com/ronnythedev/dotnet-clean-architecture-skills /tmp/dotnet-jwt-authentication && cp -r /tmp/dotnet-jwt-authentication/skills/12-dotnet-jwt-authentication ~/.claude/skills/dotnet-jwt-authenticationSKILL.md
# JWT Authentication Setup
## Overview
This skill implements JWT (JSON Web Token) authentication for .NET APIs:
- **Access Token** - Short-lived JWT returned in response body
- **Refresh Token** - Stored in HttpOnly cookie (secure, not accessible via JavaScript)
- **Options Pattern** - Configurable expiration via JwtOptions
- **Token Rotation** - New refresh token issued on each refresh
- **Security Audit** - Comprehensive event tracking for compliance
- **Token generation** - Create access and refresh tokens
- **Token validation** - Validate incoming tokens
- **User context** - Extract user info from claims
## Quick Reference
| Component | Purpose | Location |
|-----------|---------|----------|
| `IJwtService` | Token generation interface | Application/Abstractions |
| `JwtService` | Token generation implementation | Infrastructure/Authentication |
| `JwtOptions` | configuration (expiration, issuer, etc.) | Infrastructure/Authentication |
| `JwtBearerOptionsSetup` | Configure JWT validation | Infrastructure/Authentication |
| `IUserContext` | Current user info | Application/Abstractions |
| `UserContext` | Extract from HttpContext | Infrastructure/Authentication |
| `IRefreshTokenRepository` | Refresh token storage | Domain/Identity |
| `CookieSettings` | Cookie configuration | Infrastructure/Authentication |
---
## Authentication Structure
```
/Application/Abstractions/
├── Authentication/
│ ├── IJwtService.cs
│ ├── IUserContext.cs
│ ├── TokenResponse.cs
│ └── AuthenticationErrors.cs
/Infrastructure/
├── Authentication/
│ ├── JwtOptions.cs
│ ├── JwtService.cs
│ ├── JwtBearerOptionsSetup.cs
│ ├── UserContext.cs
│ ├── CookieSettings.cs
│ └── RefreshTokenCookieManager.cs
```
---
## Template: JWT Configuration Options
```csharp
// src/{name}.infrastructure/Authentication/JwtOptions.cs
namespace {name}.infrastructure.authentication;
public sealed class JwtOptions
{
public const string SectionName = "Jwt";
public string Issuer { get; init; } = string.Empty;
public string Audience { get; init; } = string.Empty;
public string SecretKey { get; init; } = string.Empty;
public int AccessTokenExpirationMinutes { get; init; } = 60;
public int RefreshTokenExpirationDays { get; init; } = 7;
public CookieSettings Cookie { get; init; } = new();
}
public sealed class CookieSettings
{
/// <summary>
/// Name of the refresh token cookie
/// </summary>
public string Name { get; init; } = "X-Refresh-Token";
/// <summary>
/// Cookie domain (leave empty for current domain)
/// </summary>
public string? Domain { get; init; }
/// <summary>
/// Cookie path
/// </summary>
public string Path { get; init; } = "/api/v1/auth";
/// <summary>
/// SameSite policy (Strict recommended for healthcare)
/// </summary>
public SameSiteMode SameSite { get; init; } = SameSiteMode.Strict;
/// <summary>
/// Require HTTPS (always true in production)
/// </summary>
public bool SecureOnly { get; init; } = true;
}
```
### appsettings.json
```json
{
"Jwt": {
"Issuer": "your-app-name",
"Audience": "your-app-name",
"SecretKey": "your-secret-key-at-least-32-characters-long-for-security",
"AccessTokenExpirationMinutes": 60,
"RefreshTokenExpirationDays": 7,
"Cookie": {
"Name": "X-Refresh-Token",
"Domain": "",
"Path": "/api/v1/auth",
"SameSite": "Strict",
"SecureOnly": true
}
}
}
```
---
## Template: JWT Service Interface
```csharp
// src/{name}.application/Abstractions/Authentication/IJwtService.cs
using {name}.domain.users;
namespace {name}.application.abstractions.authentication;
public interface IJwtService
{
/// <summary>
/// Generate access and refresh tokens for a user
/// </summary>
TokenGenerationResult GenerateTokens(
User user,
IEnumerable<string> roles,
IEnumerable<string>? permissions = null);
/// <summary>
/// Generate tokens with custom claims
/// </summary>
TokenGenerationResult GenerateTokens(
Guid userId,
string email,
IEnumerable<string> roles,
IDictionary<string, string>? additionalClaims = null);
/// <summary>
/// Hash a refresh token for secure database storage
/// </summary>
string HashRefreshToken(string refreshToken);
/// <summary>
/// Verify a plain refresh token against its hash
/// </summary>
bool VerifyRefreshToken(string plainToken, string hashedToken);
/// <summary>
/// Get access token expiration time
/// </summary>
DateTime GetAccessTokenExpiry();
/// <summary>
/// Get refresh token expiration time
/// </summary>
DateTime GetRefreshTokenExpiry();
}
```
---
## Template: Token Response
```csharp
// src/{name}.application/Abstractions/Authentication/TokenResponse.cs
namespace {name}.application.abstractions.authentication;
/// <summary>
/// Response containing access token (refresh token is set via HttpOnly cookie)
/// </summary>
public sealed record TokenResponse(
string AccessToken,
DateTime AccessTokenExpiration,
string TokenType = "Bearer");
/// <summary>
/// Internal response including refresh token (for cookie setting)
/// </summary>
public sealed record TokenGenerationResult(
string AccessToken,
string RefreshToken,
DateTime AccessTokenExpiration,
DateTime RefreshTokenExpiration);
```
---
## Template: JWT Service Implementation
```csharp
// src/{name}.infrastructure/Authentication/JwtService.cs
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using {name}.application.Abstractions.Authentication;
using {name}.application.Abstractions.Clock;
using {name}.domain.identity;
namespace {name}.infrastructure.authentication;
internal sealed class JwtService : IScaffolds a complete .NET solution following Clean Architecture principles with proper layer separation (API, Application, Domain, Infrastructure). Creates project structure, dependency injection setup, and cross-cutting concerns configuration.
Generates CQRS Commands with Handlers, Validators, and Request DTOs following Clean Architecture patterns. Commands represent actions that modify state and return Result types for proper error handling.
Generates CQRS Queries with Handlers and Response DTOs for read operations. Uses Dapper for optimized read queries, bypassing the domain model for better performance.
Generates Domain Entities following DDD principles with factory methods, private setters, domain events, and proper encapsulation. Supports aggregate roots, child entities, and value objects.
Generates Repository interfaces and implementations following the Repository pattern. Provides data access abstraction for aggregate roots with EF Core implementations.
Generates Entity Framework Core configurations using Fluent API. Maps domain entities to database tables with proper relationships, constraints, and conventions.
Generates RESTful API Controllers with proper routing, versioning, authorization, and MediatR integration. Follows REST conventions and Clean Architecture patterns.
Generates Minimal API endpoints following Microsoft's recommended approach. Creates fast, testable HTTP APIs with minimal code using MapGet/MapPost/MapPut/MapDelete. Preferred over controller-based APIs for new projects.