.agents/skills/security-jwt/SKILL.md
Comprehensive JWT authentication and authorization implementation with security best practices for .NET applications
npx skillsauth add afonsoft/VideoChat security-jwtInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use this skill when you need to:
Input: Application requirements and security policies
**Output: Complete JWT authentication configuration
Configure JWT authentication with these requirements:
1. Set up JWT token generation and validation
2. Configure authentication middleware
3. Define security policies and requirements
4. Set up token refresh mechanism
5. Configure secure cookie options
6. Add proper logging and error handling
7. Test authentication flow end-to-end
Input: User roles and access control requirements
**Output: Complete authorization system with policies
Implement authorization with these requirements:
1. Define role-based access control
2. Create custom authorization policies
3. Implement policy-based authorization
4. Add resource-based authorization
5. Configure authorization middleware
6. Test authorization scenarios
7. Handle unauthorized access properly
Input: Security requirements and threat model
**Output: Complete security middleware pipeline
Set up security middleware with these requirements:
1. Configure authentication and authorization order
2. Add CORS policies for cross-origin requests
3. Implement rate limiting and throttling
4. Add security headers middleware
5. Configure HTTPS enforcement
6. Add request logging and auditing
7. Test security pipeline effectiveness
// Interfaces
public interface IJwtService
{
string GenerateToken(User user);
string GenerateRefreshToken();
ClaimsPrincipal? GetPrincipalFromExpiredToken(string token);
bool ValidateToken(string token);
TokenValidationParameters GetTokenValidationParameters();
}
public interface ITokenService
{
Task<AuthenticationResult> AuthenticateAsync(LoginRequest request);
Task<AuthenticationResult> RefreshTokenAsync(string refreshToken);
Task RevokeTokenAsync(string token);
}
// JWT Service Implementation
public class JwtService : IJwtService
{
private readonly IConfiguration _configuration;
private readonly ILogger<JwtService> _logger;
private readonly SymmetricSecurityKey _key;
public JwtService(IConfiguration configuration, ILogger<JwtService> logger)
{
_configuration = configuration;
_logger = logger;
_key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]!));
}
public string GenerateToken(User user)
{
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Email, user.Email),
new Claim(ClaimTypes.Name, user.Name),
new Claim(ClaimTypes.Role, user.Role),
new Claim("username", user.Username),
new Claim("permissions", string.Join(",", user.Permissions)),
new Claim("last_password_change", user.LastPasswordChange?.ToString("O") ?? ""),
new Claim("jti", Guid.NewGuid().ToString())
}),
Expires = DateTime.UtcNow.AddMinutes(
_configuration.GetValue<int>("Jwt:ExpirationMinutes", 60)),
Issuer = _configuration["Jwt:Issuer"],
Audience = _configuration["Jwt:Audience"],
SigningCredentials = new SigningCredentials(_key, SecurityAlgorithms.HmacSha256Signature)
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
_logger.LogInformation("JWT token generated for user {UserId}", user.Id);
return tokenHandler.WriteToken(token);
}
public string GenerateRefreshToken()
{
var randomNumber = new byte[64];
using var rng = RandomNumberGenerator.Create();
rng.GetBytes(randomNumber);
return Convert.ToBase64String(randomNumber);
}
public ClaimsPrincipal? GetPrincipalFromExpiredToken(string token)
{
var tokenValidationParameters = GetTokenValidationParameters();
var tokenHandler = new JwtSecurityTokenHandler();
var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out SecurityToken securityToken);
if (securityToken is not JwtSecurityToken jwtSecurityToken ||
!jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
{
_logger.LogWarning("Invalid token algorithm");
return null;
}
return principal;
}
public bool ValidateToken(string token)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = GetTokenValidationParameters();
tokenHandler.ValidateToken(token, validationParameters, out SecurityToken validatedToken);
return validatedToken != null;
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Token validation failed");
return false;
}
}
public TokenValidationParameters GetTokenValidationParameters()
{
return new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = _configuration["Jwt:Issuer"],
ValidAudience = _configuration["Jwt:Audience"],
IssuerSigningKey = _key,
ClockSkew = TimeSpan.Zero
};
}
}
public class TokenService : ITokenService
{
private readonly IJwtService _jwtService;
private readonly IUserRepository _userRepository;
private readonly IRefreshTokenRepository _refreshTokenRepository;
private readonly ILogger<TokenService> _logger;
public TokenService(
IJwtService jwtService,
IUserRepository userRepository,
IRefreshTokenRepository refreshTokenRepository,
ILogger<TokenService> logger)
{
_jwtService = jwtService;
_userRepository = userRepository;
_refreshTokenRepository = refreshTokenRepository;
_logger = logger;
}
public async Task<AuthenticationResult> AuthenticateAsync(LoginRequest request)
{
try
{
// Validate user credentials
var user = await _userRepository.GetByUsernameAsync(request.Username);
if (user == null || !VerifyPassword(request.Password, user.PasswordHash))
{
_logger.LogWarning("Authentication failed for user {Username}", request.Username);
return AuthenticationResult.Failed("Invalid username or password");
}
// Check if account is locked
if (user.IsLockedOut)
{
_logger.LogWarning("Account locked for user {Username}", request.Username);
return AuthenticationResult.Failed("Account is locked");
}
// Check if password needs to be changed
if (user.MustChangePassword)
{
return new AuthenticationResult
{
Success = true,
RequiresPasswordChange = true,
Message = "Password change required"
};
}
// Generate tokens
var token = _jwtService.GenerateToken(user);
var refreshToken = _jwtService.GenerateRefreshToken();
// Save refresh token
var refreshTokenEntity = new RefreshToken
{
Token = refreshToken,
UserId = user.Id,
ExpiresAt = DateTime.UtcNow.AddDays(7),
CreatedAt = DateTime.UtcNow,
CreatedByIp = GetClientIpAddress()
};
await _refreshTokenRepository.AddAsync(refreshTokenEntity);
// Update last login
user.LastLoginAt = DateTime.UtcNow;
await _userRepository.UpdateAsync(user);
_logger.LogInformation("User {Username} authenticated successfully", request.Username);
return new AuthenticationResult
{
Success = true,
Token = token,
RefreshToken = refreshToken,
ExpiresAt = DateTime.UtcNow.AddMinutes(60),
User = new UserDto
{
Id = user.Id,
Username = user.Username,
Email = user.Email,
Name = user.Name,
Role = user.Role,
Permissions = user.Permissions
}
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Authentication failed for user {Username}", request.Username);
return AuthenticationResult.Failed("Authentication failed");
}
}
public async Task<AuthenticationResult> RefreshTokenAsync(string refreshToken)
{
try
{
// Validate refresh token
var storedToken = await _refreshTokenRepository.GetByTokenAsync(refreshToken);
if (storedToken == null || storedToken.IsRevoked || storedToken.ExpiresAt < DateTime.UtcNow)
{
_logger.LogWarning("Invalid or expired refresh token");
return AuthenticationResult.Failed("Invalid refresh token");
}
// Get user
var user = await _userRepository.GetByIdAsync(storedToken.UserId);
if (user == null || !user.IsActive)
{
_logger.LogWarning("User not found or inactive for refresh token");
return AuthenticationResult.Failed("User not found or inactive");
}
// Generate new tokens
var newToken = _jwtService.GenerateToken(user);
var newRefreshToken = _jwtService.GenerateRefreshToken();
// Revoke old refresh token
storedToken.IsRevoked = true;
storedToken.RevokedAt = DateTime.UtcNow;
await _refreshTokenRepository.UpdateAsync(storedToken);
// Save new refresh token
var newRefreshTokenEntity = new RefreshToken
{
Token = newRefreshToken,
UserId = user.Id,
ExpiresAt = DateTime.UtcNow.AddDays(7),
CreatedAt = DateTime.UtcNow,
CreatedByIp = GetClientIpAddress(),
ReplacedByToken = storedToken.Token
};
await _refreshTokenRepository.AddAsync(newRefreshTokenEntity);
_logger.LogInformation("Token refreshed for user {UserId}", user.Id);
return new AuthenticationResult
{
Success = true,
Token = newToken,
RefreshToken = newRefreshToken,
ExpiresAt = DateTime.UtcNow.AddMinutes(60),
User = new UserDto
{
Id = user.Id,
Username = user.Username,
Email = user.Email,
Name = user.Name,
Role = user.Role,
Permissions = user.Permissions
}
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Token refresh failed");
return AuthenticationResult.Failed("Token refresh failed");
}
}
public async Task RevokeTokenAsync(string token)
{
try
{
var storedToken = await _refreshTokenRepository.GetByTokenAsync(token);
if (storedToken != null)
{
storedToken.IsRevoked = true;
storedToken.RevokedAt = DateTime.UtcNow;
await _refreshTokenRepository.UpdateAsync(storedToken);
_logger.LogInformation("Token revoked for user {UserId}", storedToken.UserId);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Token revocation failed");
}
}
private bool VerifyPassword(string password, string hash)
{
return BCrypt.Net.BCrypt.Verify(password, hash);
}
private string GetClientIpAddress()
{
// Get client IP address from HttpContext
// This would typically be injected via IHttpContextAccessor
return "127.0.0.1";
}
}
// JWT Authentication 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"]!)),
ClockSkew = TimeSpan.Zero,
RoleClaimType = ClaimTypes.Role,
NameClaimType = ClaimTypes.Name
};
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
var logger = context.HttpContext.RequestServices
.GetRequiredService<ILogger<Program>>();
logger.LogWarning("Authentication failed: {Error}", context.Exception.Message);
return Task.CompletedTask;
},
OnTokenValidated = context =>
{
var logger = context.HttpContext.RequestServices
.GetRequiredService<ILogger<Program>>();
logger.LogDebug("Token validated for user {UserId}",
context.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value);
return Task.CompletedTask;
},
OnChallenge = context =>
{
var logger = context.HttpContext.RequestServices
.GetRequiredService<ILogger<Program>>();
logger.LogWarning("Authentication challenge: {Error}", context.ErrorDescription);
return Task.CompletedTask;
}
};
});
// Authorization Configuration
builder.Services.AddAuthorization(options =>
{
// Role-based policies
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
options.AddPolicy("UserOrAdmin", policy => policy.RequireRole("User", "Admin"));
// Custom policies
options.AddPolicy("CanManageUsers", policy =>
policy.RequireClaim("permissions", "users.manage"));
options.AddPolicy("CanViewReports", policy =>
policy.RequireClaim("permissions", "reports.view"));
// Age-based policy
options.AddPolicy("AtLeast18", policy =>
policy.RequireClaim("age", x => Convert.ToInt32(x) >= 18));
// Multi-claim policy
options.AddPolicy("PremiumUser", policy =>
policy.RequireRole("Premium")
.RequireClaim("subscription", "active"));
// Custom requirement policy
options.AddPolicy("AccountActive", policy =>
policy.AddRequirements(new AccountActiveRequirement()));
});
// Register custom policy handler
builder.Services.AddSingleton<IAuthorizationHandler, AccountActiveHandler>();
// Register services
builder.Services.AddScoped<IJwtService, JwtService>();
builder.Services.AddScoped<ITokenService, TokenService>();
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IRefreshTokenRepository, RefreshTokenRepository>();
// Custom requirement
public class AccountActiveRequirement : IAuthorizationRequirement
{
}
// Requirement handler
public class AccountActiveHandler : AuthorizationHandler<AccountActiveRequirement>
{
private readonly IUserRepository _userRepository;
public AccountActiveHandler(IUserRepository userRepository)
{
_userRepository = userRepository;
}
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
AccountActiveRequirement requirement)
{
var userIdClaim = context.User.FindFirst(ClaimTypes.NameIdentifier);
if (userIdClaim == null)
{
context.Fail();
return;
}
var userId = int.Parse(userIdClaim.Value);
var user = await _userRepository.GetByIdAsync(userId);
if (user == null || !user.IsActive || user.IsLockedOut)
{
context.Fail();
return;
}
context.Succeed(requirement);
}
}
// Resource-based requirement
public class SameUserRequirement : IAuthorizationRequirement
{
public int UserId { get; }
public SameUserRequirement(int userId)
{
UserId = userId;
}
}
public class SameUserHandler : AuthorizationHandler<SameUserRequirement, int>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
SameUserRequirement requirement,
int resource)
{
var userIdClaim = context.User.FindFirst(ClaimTypes.NameIdentifier);
if (userIdClaim == null)
{
context.Fail();
return Task.CompletedTask;
}
var currentUserId = int.Parse(userIdClaim.Value);
if (currentUserId == requirement.UserId)
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
return Task.CompletedTask;
}
}
public class SecurityHeadersMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<SecurityHeadersMiddleware> _logger;
public SecurityHeadersMiddleware(RequestDelegate next, ILogger<SecurityHeadersMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
// Add security headers
if (!context.Response.Headers.ContainsKey("X-Content-Type-Options"))
{
context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
}
if (!context.Response.Headers.ContainsKey("X-Frame-Options"))
{
context.Response.Headers.Add("X-Frame-Options", "DENY");
}
if (!context.Response.Headers.ContainsKey("X-XSS-Protection"))
{
context.Response.Headers.Add("X-XSS-Protection", "1; mode=block");
}
if (!context.Response.Headers.ContainsKey("Strict-Transport-Security"))
{
context.Response.Headers.Add("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
}
if (!context.Response.Headers.ContainsKey("Content-Security-Policy"))
{
context.Response.Headers.Add("Content-Security-Policy",
"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'");
}
if (!context.Response.Headers.ContainsKey("Referrer-Policy"))
{
context.Response.Headers.Add("Referrer-Policy", "strict-origin-when-cross-origin");
}
// Remove server information
context.Response.Headers.Remove("Server");
await _next(context);
}
}
// Rate Limiting Middleware
public class RateLimitingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RateLimitingMiddleware> _logger;
private readonly IMemoryCache _cache;
private readonly RateLimitOptions _options;
public RateLimitingMiddleware(
RequestDelegate next,
ILogger<RateLimitingMiddleware> logger,
IMemoryCache cache,
IOptions<RateLimitOptions> options)
{
_next = next;
_logger = logger;
_cache = cache;
_options = options.Value;
}
public async Task InvokeAsync(HttpContext context)
{
var clientId = GetClientId(context);
var cacheKey = $"rate_limit_{clientId}";
var requestCount = _cache.GetOrCreate(cacheKey, entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);
return 0;
});
if (requestCount >= _options.RequestLimit)
{
_logger.LogWarning("Rate limit exceeded for client {ClientId}", clientId);
context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
await context.Response.WriteAsync("Rate limit exceeded");
return;
}
_cache.Set(cacheKey, requestCount + 1);
await _next(context);
}
private string GetClientId(HttpContext context)
{
// Try to get user ID from claims
var userIdClaim = context.User.FindFirst(ClaimTypes.NameIdentifier);
if (userIdClaim != null)
{
return $"user_{userIdClaim.Value}";
}
// Fallback to IP address
return $"ip_{context.Connection.RemoteIpAddress}";
}
}
public class RateLimitOptions
{
public int RequestLimit { get; set; } = 100;
public int WindowSizeMinutes { get; set; } = 1;
}
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
private readonly ITokenService _tokenService;
private readonly ILogger<AuthController> _logger;
public AuthController(ITokenService tokenService, ILogger<AuthController> logger)
{
_tokenService = tokenService;
_logger = logger;
}
[HttpPost("login")]
public async Task<ActionResult<AuthenticationResult>> Login([FromBody] LoginRequest request)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var result = await _tokenService.AuthenticateAsync(request);
if (!result.Success)
{
return Unauthorized(new { message = result.Message });
}
return Ok(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "Login failed for user {Username}", request.Username);
return StatusCode(StatusCodes.Status500InternalServerError,
new { message = "An error occurred during login" });
}
}
[HttpPost("refresh")]
public async Task<ActionResult<AuthenticationResult>> RefreshToken([FromBody] RefreshTokenRequest request)
{
try
{
if (string.IsNullOrEmpty(request.RefreshToken))
{
return BadRequest(new { message = "Refresh token is required" });
}
var result = await _tokenService.RefreshTokenAsync(request.RefreshToken);
if (!result.Success)
{
return Unauthorized(new { message = result.Message });
}
return Ok(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "Token refresh failed");
return StatusCode(StatusCodes.Status500InternalServerError,
new { message = "An error occurred during token refresh" });
}
}
[HttpPost("logout")]
[Authorize]
public async Task<IActionResult> Logout([FromBody] LogoutRequest request)
{
try
{
if (!string.IsNullOrEmpty(request.RefreshToken))
{
await _tokenService.RevokeTokenAsync(request.RefreshToken);
}
return Ok(new { message = "Logged out successfully" });
}
catch (Exception ex)
{
_logger.LogError(ex, "Logout failed");
return StatusCode(StatusCodes.Status500InternalServerError,
new { message = "An error occurred during logout" });
}
}
}
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class UsersController : ControllerBase
{
private readonly IUserService _userService;
private readonly ILogger<UsersController> _logger;
public UsersController(IUserService userService, ILogger<UsersController> logger)
{
_userService = userService;
_logger = logger;
}
[HttpGet]
[Authorize(Policy = "CanManageUsers")]
public async Task<ActionResult<IEnumerable<UserDto>>> GetUsers()
{
try
{
var users = await _userService.GetAllUsersAsync();
return Ok(users);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error retrieving users");
return StatusCode(StatusCodes.Status500InternalServerError,
new { message = "An error occurred while retrieving users" });
}
}
[HttpGet("{id}")]
[Authorize(Policy = "SameUser")]
public async Task<ActionResult<UserDto>> GetUser(int id)
{
try
{
var user = await _userService.GetUserByIdAsync(id);
if (user == null)
{
return NotFound();
}
return Ok(user);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error retrieving user {UserId}", id);
return StatusCode(StatusCodes.Status500InternalServerError,
new { message = "An error occurred while retrieving the user" });
}
}
[HttpPut("{id}")]
[Authorize(Policy = "SameUser")]
public async Task<ActionResult<UserDto>> UpdateUser(int id, [FromBody] UpdateUserRequest request)
{
try
{
var user = await _userService.UpdateUserAsync(id, request);
return Ok(user);
}
catch (NotFoundException ex)
{
return NotFound(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error updating user {UserId}", id);
return StatusCode(StatusCodes.Status500InternalServerError,
new { message = "An error occurred while updating the user" });
}
}
[HttpDelete("{id}")]
[Authorize(Policy = "CanManageUsers")]
public async Task<IActionResult> DeleteUser(int id)
{
try
{
await _userService.DeleteUserAsync(id);
return NoContent();
}
catch (NotFoundException ex)
{
return NotFound(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error deleting user {UserId}", id);
return StatusCode(StatusCodes.Status500InternalServerError,
new { message = "An error occurred while deleting the user" });
}
}
}
{
"Jwt": {
"Issuer": "YourApp",
"Audience": "YourAppUsers",
"Key": "ThisIsASecretKeyForJWTTokenGeneration123456789!@#$%^&*()",
"ExpirationMinutes": 60,
"RefreshTokenExpirationDays": 7
},
"RateLimit": {
"RequestLimit": 100,
"WindowSizeMinutes": 1
},
"Security": {
"RequireHttps": true,
"EnableCORS": true,
"AllowedOrigins": ["http://localhost:3000", "https://yourapp.com"],
"AllowedMethods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
"AllowedHeaders": ["*"]
}
}
{
"Jwt": {
"Issuer": "YourApp-Dev",
"Audience": "YourAppUsers-Dev",
"Key": "DevelopmentSecretKeyForJWT123456789",
"ExpirationMinutes": 1440
},
"Security": {
"RequireHttps": false,
"EnableCORS": true,
"AllowedOrigins": ["http://localhost:3000", "http://localhost:5000", "http://localhost:5001"]
}
}
public class PasswordService
{
public string HashPassword(string password)
{
return BCrypt.Net.BCrypt.HashPassword(password, 12);
}
public bool VerifyPassword(string password, string hash)
{
return BCrypt.Net.BCrypt.Verify(password, hash);
}
}
public class LoginRequest
{
[Required(ErrorMessage = "Username is required")]
[StringLength(50, MinimumLength = 3, ErrorMessage = "Username must be between 3 and 50 characters")]
[RegularExpression(@"^[a-zA-Z0-9_]+$", ErrorMessage = "Username can only contain letters, numbers, and underscores")]
public string Username { get; set; }
[Required(ErrorMessage = "Password is required")]
[StringLength(100, MinimumLength = 8, ErrorMessage = "Password must be between 8 and 100 characters")]
[RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]",
ErrorMessage = "Password must contain at least one uppercase letter, one lowercase letter, one digit, and one special character")]
public string Password { get; set; }
}
// In Program.cs
builder.Services.AddAntiforgery(options =>
{
options.Cookie.Name = "X-CSRF-TOKEN";
options.Cookie.HttpOnly = true;
options.HeaderName = "X-CSRF-TOKEN";
});
// In controller
[ValidateAntiForgeryToken]
[HttpPost]
public IActionResult SecureAction()
{
return Ok();
}
public class TokenServiceTests
{
private readonly Mock<IJwtService> _jwtServiceMock;
private readonly Mock<IUserRepository> _userRepositoryMock;
private readonly Mock<IRefreshTokenRepository> _refreshTokenRepositoryMock;
private readonly TokenService _tokenService;
public TokenServiceTests()
{
_jwtServiceMock = new Mock<IJwtService>();
_userRepositoryMock = new Mock<IUserRepository>();
_refreshTokenRepositoryMock = new Mock<IRefreshTokenRepository>();
_tokenService = new TokenService(
_jwtServiceMock.Object,
_userRepositoryMock.Object,
_refreshTokenRepositoryMock.Object,
Mock.Of<ILogger<TokenService>>());
}
[Fact]
public async Task AuthenticateAsync_ValidCredentials_ReturnsSuccess()
{
// Arrange
var request = new LoginRequest { Username = "testuser", Password = "password123" };
var user = new User
{
Id = 1,
Username = "testuser",
PasswordHash = BCrypt.Net.BCrypt.HashPassword("password123"),
IsActive = true,
Role = "User"
};
_userRepositoryMock.Setup(r => r.GetByUsernameAsync(request.Username))
.ReturnsAsync(user);
_jwtServiceMock.Setup(j => j.GenerateToken(user))
.Returns("jwt-token");
_jwtServiceMock.Setup(j => j.GenerateRefreshToken())
.Returns("refresh-token");
// Act
var result = await _tokenService.AuthenticateAsync(request);
// Assert
Assert.True(result.Success);
Assert.Equal("jwt-token", result.Token);
Assert.Equal("refresh-token", result.RefreshToken);
}
[Fact]
public async Task AuthenticateAsync_InvalidCredentials_ReturnsFailure()
{
// Arrange
var request = new LoginRequest { Username = "testuser", Password = "wrongpassword" };
_userRepositoryMock.Setup(r => r.GetByUsernameAsync(request.Username))
.ReturnsAsync((User)null);
// Act
var result = await _tokenService.AuthenticateAsync(request);
// Assert
Assert.False(result.Success);
Assert.Equal("Invalid username or password", result.Message);
}
}
public class AuthenticationIntegrationTests : IClassFixture<TestWebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public AuthenticationIntegrationTests(TestWebApplicationFactory<Program> factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task Login_ValidCredentials_ReturnsToken()
{
// Arrange
var loginRequest = new
{
Username = "testuser",
Password = "password123"
};
var json = JsonSerializer.Serialize(loginRequest);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// Act
var response = await _client.PostAsync("/api/auth/login", content);
// Assert
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<AuthenticationResult>();
Assert.NotNull(result);
Assert.True(result.Success);
Assert.NotNull(result.Token);
Assert.NotNull(result.RefreshToken);
}
[Fact]
public async Task ProtectedEndpoint_WithoutToken_ReturnsUnauthorized()
{
// Act
var response = await _client.GetAsync("/api/users");
// Assert
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
}
[Fact]
public async Task ProtectedEndpoint_WithToken_ReturnsSuccess()
{
// Arrange
var loginResponse = await LoginAsync("testuser", "password123");
var token = loginResponse.Token;
_client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
// Act
var response = await _client.GetAsync("/api/users");
// Assert
response.EnsureSuccessStatusCode();
}
private async Task<AuthenticationResult> LoginAsync(string username, string password)
{
var loginRequest = new { Username = username, Password = password };
var json = JsonSerializer.Serialize(loginRequest);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _client.PostAsync("/api/auth/login", content);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<AuthenticationResult>();
}
}
This skill provides comprehensive guidance for implementing secure JWT authentication and authorization in .NET applications. Use it to create robust, secure, and maintainable authentication systems.
development
This skill enables visual inspection of websites running locally or remotely to identify and fix design issues. Triggers on requests like "review website design", "check the UI", "fix the layout", "find design problems". Detects issues with responsive design, accessibility, visual consistency, and layout breakage, then performs fixes at the source code level.
testing
Comprehensive unit testing with xUnit, mocking, test patterns, and best practices for .NET applications
data-ai
Universal SQL performance optimization assistant for comprehensive query tuning, indexing strategies, and database performance analysis across all SQL databases (MySQL, PostgreSQL, SQL Server, Oracle). Provides execution plan analysis, pagination optimization, batch operations, and performance monitoring guidance.
development
Universal SQL code review assistant that performs comprehensive security, maintainability, and code quality analysis across all SQL databases (MySQL, PostgreSQL, SQL Server, Oracle). Focuses on SQL injection prevention, access control, code standards, and anti-pattern detection. Complements SQL optimization prompt for complete development coverage.