Comprehensive Guide to Testing JWT Authentication in ASP.NET Core
This guide covers various approaches to test your JWT authentication implementation in ASP.NET Core applications.
Table of Contents
- Prerequisites
- Testing with Swagger UI
- Testing with Curl
- Testing with Postman
- Writing Unit Tests
- Integration Testing
- Troubleshooting
Prerequisites
Ensure your application has:
- JWT authentication properly configured in
Program.cs - An authentication controller to generate tokens
- Protected endpoints requiring authentication
- Proper configuration in
appsettings.json
Example configuration in appsettings.json:
{
"Jwt": {
"Key": "YourSecureSecretKeyWithAtLeast32Characters",
"Issuer": "your-issuer",
"Audience": "your-audience"
}
}Testing with Swagger UI
Swagger UI provides a convenient interface for testing your API authentication.
Run your application
bashdotnet runAccess Swagger UI
- Navigate to
https://localhost:{port}/swaggerin your browser
- Navigate to
Obtain a JWT token
- Find the
/api/Auth/tokenendpoint - Click on "Try it out"
- Enter credentials in the request body:json
{ "username": "admin", "password": "password" } - Click "Execute" and copy the token from the response
- Find the
Authenticate in Swagger
- Click the "Authorize" button at the top of the page
- In the popup, enter your token with the format:
Bearer {your-token} - Click "Authorize"
Test protected endpoints
- You can now access protected endpoints through Swagger UI
- Try accessing:
/api/Questions(requires authentication)/api/Admin(requires Admin role)/api/Admin/super(requires SuperAdmin role)/api/Public(accessible without authentication)
Testing with Curl
Curl commands provide a way to test your API from the command line.
Get a JWT Token
curl -X POST "https://localhost:{port}/api/Auth/token" \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"password"}'Test Protected Endpoint
curl -X GET "https://localhost:{port}/api/Questions" \
-H "Authorization: Bearer {your-token-here}"Test Role-Protected Endpoint
curl -X GET "https://localhost:{port}/api/Admin" \
-H "Authorization: Bearer {your-token-here}"Test Public Endpoint
curl -X GET "https://localhost:{port}/api/Public"Test Endpoint with Specific Role
For example, testing an endpoint that requires SuperAdmin role:
# First get a token for a SuperAdmin user
curl -X POST "https://localhost:{port}/api/Auth/token" \
-H "Content-Type: application/json" \
-d '{"username":"superadmin","password":"password"}'
# Then use that token
curl -X GET "https://localhost:{port}/api/Admin/super" \
-H "Authorization: Bearer {superadmin-token-here}"Testing with Postman
Postman offers a user-friendly interface for API testing.
Create a collection
- Open Postman and create a new collection for your API tests
Set up token request
- Create a POST request to
/api/Auth/token - Set the body to raw JSON:json
{ "username": "admin", "password": "password" } - Send the request and copy the token from the response
- Create a POST request to
Create environment variables (optional)
- Create a new environment
- Add variable
tokenwith the token value - Add variable
baseUrlwith your API's base URL
Test protected endpoints
- Create a new request to a protected endpoint (e.g.,
/api/Questions) - In the Authorization tab:
- Select Type: "Bearer Token"
- Token: paste your JWT or use
if you set up an environment variable
- Send the request and verify the response
- Create a new request to a protected endpoint (e.g.,
Automate token retrieval (advanced)
- In the "Tests" tab of your token request, add:javascript
var jsonData = pm.response.json(); pm.environment.set("token", jsonData.token); - This automatically updates your token variable after each request
- In the "Tests" tab of your token request, add:
Writing Unit Tests
For proper unit testing, you'll want to test controllers without actual HTTP requests.
Example Unit Test for a Controller
[Fact]
public void AdminOnly_WithAdminRole_ReturnsOkResult()
{
// Arrange
var claims = new List<Claim> {
new Claim(ClaimTypes.Role, "Admin")
};
var identity = new ClaimsIdentity(claims, "TestAuth");
var claimsPrincipal = new ClaimsPrincipal(identity);
var controllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext { User = claimsPrincipal }
};
var controller = new AdminController();
controller.ControllerContext = controllerContext;
// Act
var result = controller.AdminOnly();
// Assert
Assert.IsType<OkObjectResult>(result);
}Integration Testing
Integration tests provide end-to-end validation of your authentication flow.
Setting Up Integration Tests
Create a test project
bashdotnet new xunit -n YourProject.TestsAdd required packages
bashdotnet add package Microsoft.AspNetCore.Mvc.TestingCreate a test WebApplicationFactory
public class TestWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Override service registrations as needed for testing
});
}
}- Write integration tests
public class AuthenticationIntegrationTests : IClassFixture<TestWebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public AuthenticationIntegrationTests(TestWebApplicationFactory<Program> factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task PublicEndpoint_ReturnsSuccessfully()
{
// Act
var response = await _client.GetAsync("/api/Public");
// Assert
response.EnsureSuccessStatusCode();
}
[Fact]
public async Task ProtectedEndpoint_WithoutToken_ReturnsUnauthorized()
{
// Act
var response = await _client.GetAsync("/api/Questions");
// Assert
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
}
[Fact]
public async Task ProtectedEndpoint_WithValidToken_ReturnsSuccess()
{
// Arrange - get token
var tokenResponse = await _client.PostAsync("/api/Auth/token",
new StringContent(
JsonSerializer.Serialize(new { Username = "admin", Password = "password" }),
Encoding.UTF8,
"application/json"));
tokenResponse.EnsureSuccessStatusCode();
var tokenContent = await tokenResponse.Content.ReadAsStringAsync();
var token = JsonSerializer.Deserialize<TokenResponse>(tokenContent,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true })?.Token;
// Add token to client
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
// Act - call protected endpoint
var response = await _client.GetAsync("/api/Questions");
// Assert
response.EnsureSuccessStatusCode();
}
}
public class TokenResponse
{
public string Token { get; set; }
}Troubleshooting
Common issues and solutions when testing JWT authentication:
Invalid Token Format
Issue: Response status 401 Unauthorized when the token is provided.
Solution: Make sure you're including the word "Bearer" before the token.
Token Validation Errors
Issue: Token is rejected with 401 Unauthorized.
Solution: Check that:
- The token hasn't expired
- The issuer and audience match the configuration
- The signing key is the same used for token generation
- The clock on your server is accurate
Role Claims Not Working
Issue: Role-based endpoints return 403 Forbidden despite having a valid token.
Solution:
- Ensure that role claims are added correctly during token generation
- Verify that the claim type is
ClaimTypes.Role(or the expected type) - Check if roles are case-sensitive in your configuration
CORS Issues
Issue: Browser-based clients receive CORS errors with authenticated requests.
Solution: Ensure your CORS policy includes all necessary headers:
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowSpecificOrigins",
builder => builder
.WithOrigins("http://example.com")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});And apply the policy in your app:
app.UseCors("AllowSpecificOrigins");JWT Debug Tools
For troubleshooting JWT issues, you can decode tokens to inspect their contents at: