Skip to content

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

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:

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.

  1. Run your application

    bash
    dotnet run
  2. Access Swagger UI

    • Navigate to https://localhost:{port}/swagger in your browser
  3. Obtain a JWT token

    • Find the /api/Auth/token endpoint
    • 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
  4. 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"
  5. 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

bash
curl -X POST "https://localhost:{port}/api/Auth/token" \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"password"}'

Test Protected Endpoint

bash
curl -X GET "https://localhost:{port}/api/Questions" \
  -H "Authorization: Bearer {your-token-here}"

Test Role-Protected Endpoint

bash
curl -X GET "https://localhost:{port}/api/Admin" \
  -H "Authorization: Bearer {your-token-here}"

Test Public Endpoint

bash
curl -X GET "https://localhost:{port}/api/Public"

Test Endpoint with Specific Role

For example, testing an endpoint that requires SuperAdmin role:

bash
# 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.

  1. Create a collection

    • Open Postman and create a new collection for your API tests
  2. 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
  3. Create environment variables (optional)

    • Create a new environment
    • Add variable token with the token value
    • Add variable baseUrl with your API's base URL
  4. 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
  5. 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

Writing Unit Tests

For proper unit testing, you'll want to test controllers without actual HTTP requests.

Example Unit Test for a Controller

csharp
[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

  1. Create a test project

    bash
    dotnet new xunit -n YourProject.Tests
  2. Add required packages

    bash
    dotnet add package Microsoft.AspNetCore.Mvc.Testing
  3. Create a test WebApplicationFactory

csharp
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
        });
    }
}
  1. Write integration tests
csharp
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:

csharp
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowSpecificOrigins",
        builder => builder
            .WithOrigins("http://example.com")
            .AllowAnyMethod()
            .AllowAnyHeader()
            .AllowCredentials());
});

And apply the policy in your app:

csharp
app.UseCors("AllowSpecificOrigins");

JWT Debug Tools

For troubleshooting JWT issues, you can decode tokens to inspect their contents at: