The Intent.AspNetCore.Identity.JWT module combines ASP.NET Core Identity with JWT (JSON Web Token) authentication, providing a complete authentication solution for modern Web APIs with token-based security.
This module bridges Intent.AspNetCore.Identity (user management) with Intent.Security.JWT (token-based auth) to provide a complete authentication system.
Overview
This module generates the necessary code to issue JWT tokens upon successful user authentication and validate those tokens on subsequent API requests. It provides a stateless authentication mechanism ideal for SPAs, mobile apps, and microservices.
Architecture
What Gets Generated
The module integrates Identity with JWT by generating:
Token Generation Service
public interface ITokenService
{
Task < TokenResponse > GenerateTokenAsync (
ApplicationUser user ,
CancellationToken cancellationToken = default );
}
public class TokenService : ITokenService
{
private readonly UserManager < ApplicationUser > _userManager ;
private readonly JwtSettings _jwtSettings ;
public TokenService (
UserManager < ApplicationUser > userManager ,
IOptions < JwtSettings > jwtSettings )
{
_userManager = userManager ;
_jwtSettings = jwtSettings . Value ;
}
public async Task < TokenResponse > GenerateTokenAsync (
ApplicationUser user ,
CancellationToken cancellationToken = default )
{
var claims = new List < Claim >
{
new Claim ( ClaimTypes . NameIdentifier , user . Id . ToString ()),
new Claim ( ClaimTypes . Name , user . UserName ),
new Claim ( ClaimTypes . Email , user . Email ),
new Claim ( JwtRegisteredClaimNames . Jti , Guid . NewGuid (). ToString ())
};
// Add role claims
var roles = await _userManager . GetRolesAsync ( user );
claims . AddRange ( roles . Select ( role => new Claim ( ClaimTypes . Role , role )));
// Add user claims
var userClaims = await _userManager . GetClaimsAsync ( user );
claims . AddRange ( userClaims );
var key = new SymmetricSecurityKey (
Encoding . UTF8 . GetBytes ( _jwtSettings . Secret ));
var credentials = new SigningCredentials (
key ,
SecurityAlgorithms . HmacSha256 );
var expires = DateTime . UtcNow . AddMinutes ( _jwtSettings . ExpiryInMinutes );
var token = new JwtSecurityToken (
issuer : _jwtSettings . Issuer ,
audience : _jwtSettings . Audience ,
claims : claims ,
expires : expires ,
signingCredentials : credentials );
return new TokenResponse
{
Token = new JwtSecurityTokenHandler (). WriteToken ( token ),
Expiry = expires
};
}
}
Login Endpoint
[ HttpPost ( "login" )]
[ AllowAnonymous ]
[ ProducesResponseType ( typeof ( TokenResponse ), StatusCodes . Status200OK )]
[ ProducesResponseType ( StatusCodes . Status401Unauthorized )]
public async Task < ActionResult < TokenResponse >> Login (
[ FromBody ] LoginDto dto ,
CancellationToken cancellationToken )
{
var user = await _userManager . FindByEmailAsync ( dto . Email );
if ( user == null )
return Unauthorized ( "Invalid credentials" );
var result = await _signInManager . CheckPasswordSignInAsync (
user ,
dto . Password ,
lockoutOnFailure : true );
if ( ! result . Succeeded )
{
if ( result . IsLockedOut )
return Unauthorized ( "Account is locked out" );
if ( result . IsNotAllowed )
return Unauthorized ( "Account is not allowed to sign in" );
return Unauthorized ( "Invalid credentials" );
}
var tokenResponse = await _tokenService . GenerateTokenAsync ( user , cancellationToken );
return Ok ( tokenResponse );
}
Key Features
Token Generation Generate JWT tokens with user claims and roles
Automatic Validation Validate tokens on every protected endpoint
Role Claims Include user roles in JWT claims
Token Refresh Support for refresh tokens and token renewal
JWT Configuration
Configure JWT settings in appsettings.json:
{
"JwtSettings" : {
"Secret" : "your-secret-key-min-32-characters-long" ,
"Issuer" : "https://yourdomain.com" ,
"Audience" : "https://yourdomain.com" ,
"ExpiryInMinutes" : 60
}
}
Settings class:
public class JwtSettings
{
public string Secret { get ; set ; }
public string Issuer { get ; set ; }
public string Audience { get ; set ; }
public int ExpiryInMinutes { get ; set ; }
}
Complete Authentication Flow
1. User Registration
[ HttpPost ( "register" )]
[ AllowAnonymous ]
public async Task < ActionResult < TokenResponse >> Register (
[ FromBody ] RegisterDto dto ,
CancellationToken cancellationToken )
{
var user = new ApplicationUser
{
UserName = dto . Email ,
Email = dto . Email ,
FirstName = dto . FirstName ,
LastName = dto . LastName
};
var result = await _userManager . CreateAsync ( user , dto . Password );
if ( ! result . Succeeded )
return BadRequest ( result . Errors );
// Assign default role
await _userManager . AddToRoleAsync ( user , "User" );
// Generate token for immediate login
var tokenResponse = await _tokenService . GenerateTokenAsync ( user , cancellationToken );
return Ok ( tokenResponse );
}
2. Token Usage
Client includes token in requests:
GET /api/orders HTTP / 1.1
Host : api.example.com
Authorization : Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type : application/json
3. Protected Endpoints
[ Authorize ]
[ HttpGet ]
public async Task < ActionResult < List < OrderDto >>> GetMyOrders (
CancellationToken cancellationToken )
{
var userId = User . FindFirstValue ( ClaimTypes . NameIdentifier );
var orders = await _orderService . GetUserOrdersAsync (
Guid . Parse ( userId ),
cancellationToken );
return Ok ( orders );
}
[ Authorize ( Roles = "Admin" )]
[ HttpGet ( "all" )]
public async Task < ActionResult < List < OrderDto >>> GetAllOrders (
CancellationToken cancellationToken )
{
var orders = await _orderService . GetAllOrdersAsync ( cancellationToken );
return Ok ( orders );
}
Token Refresh
Implement refresh token functionality:
public class RefreshTokenService : IRefreshTokenService
{
private readonly IApplicationDbContext _dbContext ;
private readonly ITokenService _tokenService ;
public async Task < TokenResponse > RefreshTokenAsync (
string refreshToken ,
CancellationToken cancellationToken )
{
var storedToken = await _dbContext . RefreshTokens
. Include ( x => x . User )
. FirstOrDefaultAsync (
x => x . Token == refreshToken && ! x . IsRevoked ,
cancellationToken );
if ( storedToken == null || storedToken . ExpiryDate < DateTime . UtcNow )
throw new UnauthorizedException ( "Invalid refresh token" );
// Generate new token
var newToken = await _tokenService . GenerateTokenAsync (
storedToken . User ,
cancellationToken );
// Revoke old refresh token
storedToken . IsRevoked = true ;
await _dbContext . SaveChangesAsync ( cancellationToken );
return newToken ;
}
}
Refresh token endpoint:
[ HttpPost ( "refresh-token" )]
[ AllowAnonymous ]
public async Task < ActionResult < TokenResponse >> RefreshToken (
[ FromBody ] RefreshTokenDto dto ,
CancellationToken cancellationToken )
{
try
{
var response = await _refreshTokenService . RefreshTokenAsync (
dto . RefreshToken ,
cancellationToken );
return Ok ( response );
}
catch ( UnauthorizedException )
{
return Unauthorized ( "Invalid refresh token" );
}
}
Claims-Based Authorization
Use claims for fine-grained authorization:
public class ClaimsService
{
private readonly UserManager < ApplicationUser > _userManager ;
public async Task AddClaimAsync ( Guid userId , string claimType , string claimValue )
{
var user = await _userManager . FindByIdAsync ( userId . ToString ());
if ( user == null )
throw new NotFoundException ( "User not found" );
await _userManager . AddClaimAsync (
user ,
new Claim ( claimType , claimValue ));
}
}
Authorize based on claims:
[ Authorize ( Policy = "CanEditOrders" )]
[ HttpPut ( "{id}" )]
public async Task < ActionResult > UpdateOrder (
[ FromRoute ] Guid id ,
[ FromBody ] UpdateOrderDto dto ,
CancellationToken cancellationToken )
{
await _orderService . UpdateAsync ( id , dto , cancellationToken );
return NoContent ();
}
Policy configuration:
services . AddAuthorization ( options =>
{
options . AddPolicy ( "CanEditOrders" , policy =>
policy . RequireClaim ( "Permission" , "Orders.Edit" ));
options . AddPolicy ( "IsAdminOrManager" , policy =>
policy . RequireRole ( "Admin" , "Manager" ));
});
Token Validation
Extract user information from token:
public class TokenValidator
{
private readonly IHttpContextAccessor _httpContextAccessor ;
public Guid GetCurrentUserId ()
{
var userIdClaim = _httpContextAccessor . HttpContext ? . User
. FindFirstValue ( ClaimTypes . NameIdentifier );
if ( string . IsNullOrEmpty ( userIdClaim ))
throw new UnauthorizedException ( "User not authenticated" );
return Guid . Parse ( userIdClaim );
}
public List < string > GetCurrentUserRoles ()
{
return _httpContextAccessor . HttpContext ? . User
. FindAll ( ClaimTypes . Role )
. Select ( c => c . Value )
. ToList () ?? new List < string >();
}
public bool HasClaim ( string claimType , string claimValue )
{
return _httpContextAccessor . HttpContext ? . User
. HasClaim ( claimType , claimValue ) ?? false ;
}
}
Security Best Practices
Use a strong, random secret key (minimum 32 characters)
Store in environment variables or Azure Key Vault
Never commit secrets to source control
Rotate keys periodically
Keep access tokens short-lived (15-60 minutes)
Use refresh tokens for longer sessions
Implement token revocation for logout
Always use HTTPS in production
Set secure cookie flags
Implement HSTS headers
Validate all claims on the server
Don’t trust client-side role checks
Use policy-based authorization
Client Integration
JavaScript/TypeScript
class AuthService {
private token : string | null = null ;
async login ( email : string , password : string ) : Promise < void > {
const response = await fetch ( 'https://api.example.com/auth/login' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({ email , password })
});
if ( ! response . ok ) {
throw new Error ( 'Login failed' );
}
const data = await response . json ();
this . token = data . token ;
localStorage . setItem ( 'token' , data . token );
}
async fetchProtectedResource () : Promise < any > {
const response = await fetch ( 'https://api.example.com/api/orders' , {
headers: {
'Authorization' : `Bearer ${ this . token } ` ,
'Content-Type' : 'application/json'
}
});
return await response . json ();
}
logout () : void {
this . token = null ;
localStorage . removeItem ( 'token' );
}
}
C# Client
public class ApiClient
{
private readonly HttpClient _httpClient ;
private string _token ;
public async Task LoginAsync ( string email , string password )
{
var request = new LoginDto { Email = email , Password = password };
var response = await _httpClient . PostAsJsonAsync ( "auth/login" , request );
response . EnsureSuccessStatusCode ();
var tokenResponse = await response . Content . ReadFromJsonAsync < TokenResponse >();
_token = tokenResponse . Token ;
_httpClient . DefaultRequestHeaders . Authorization =
new AuthenticationHeaderValue ( "Bearer" , _token );
}
public async Task < List < OrderDto >> GetOrdersAsync ()
{
var response = await _httpClient . GetAsync ( "api/orders" );
response . EnsureSuccessStatusCode ();
return await response . Content . ReadFromJsonAsync < List < OrderDto >>();
}
}
Troubleshooting
Verify token is included in Authorization header
Check token hasn’t expired
Ensure secret key matches between token generation and validation
Verify issuer and audience settings
User is authenticated but lacks required role/claim
Check role assignments in database
Verify policy requirements
Check clock skew between servers
Verify token format (should start with “Bearer ”)
Ensure JWT middleware is configured before Authorization middleware
Installation
This module is installed automatically when you have:
Intent.AspNetCore.Identity
Intent.Security.JWT
Dependencies
Intent.AspNetCore.Identity
Intent.Security.JWT
Intent.Application.Identity
Next Steps
Security.JWT Configure JWT bearer authentication
Identity Learn about ASP.NET Core Identity
Swashbuckle Add authentication to Swagger UI
Controllers Protect your API endpoints