This project includes comprehensive integration tests that validate the complete HTTP Basic authentication pipeline in a realistic ASP.NET Core context.
The tests spin up a minimal ASP.NET Core host for isolation and validation:
- LocalTestServiceManager - Hosts ASP.NET Core application on dynamic port (26000 + PID)
- TestAssemblySetup - Manages host lifecycle (startup/shutdown)
- BasicAuthenticationTestController - Provides test endpoints
- BasicAuthenticationIntegrationTests - Validates HTTP behavior
Integration tests validate the complete authentication pipeline including:
- ✅ Header parsing and validation
- ✅ Base64 encoding/decoding
- ✅ Credentials extraction and comparison
- ✅ WWW-Authenticate challenge generation
- ✅ HTTP status codes and response headers
This catches issues that unit tests cannot: middleware ordering, header case sensitivity, realm encoding, etc.
ProtectedEndpoint_WithoutCredentials_ReturnsOk
GET /basic/public
// No Authorization header
Expected: HTTP 200 OK
Response: "public-ok"Validates unrestricted endpoint access without authentication.
ProtectedEndpoint_WithoutCredentials_ReturnsUnauthorized
GET /basic/protected
// No Authorization header
Expected: HTTP 401 Unauthorized
Headers: WWW-Authenticate: Basic realm="Test Realm"Validates proper challenge response when credentials are missing.
ProtectedEndpoint_WithValidCredentials_ReturnsOk
GET /basic/protected
Authorization: Basic dGVzdHVzZXI6dGVzdHBhc3M=
// base64("testuser:testpass")
Expected: HTTP 200 OK
Response: "protected-ok-testuser"Validates successful authentication with correct credentials.
ProtectedEndpoint_WithInvalidUsername_ReturnsUnauthorized
GET /basic/protected
Authorization: Basic d3Jvbmd1c2VyOnRlc3RwYXNz
// base64("wronguser:testpass")
Expected: HTTP 401 UnauthorizedValidates rejection of incorrect username.
ProtectedEndpoint_WithInvalidPassword_ReturnsUnauthorized
GET /basic/protected
Authorization: Basic dGVzdHVzZXI6d3JvbmdwYXNz
// base64("testuser:wrongpass")
Expected: HTTP 401 UnauthorizedValidates rejection of incorrect password.
ProtectedEndpoint_WithMalformedAuthorizationHeader_ReturnsUnauthorized
GET /basic/protected
Authorization: Basic invalid-base64-data!!!
Expected: HTTP 401 UnauthorizedValidates error handling for non-Base64 data in Authorization header.
ProtectedEndpoint_WithEmptyPassword_ReturnsUnauthorized
GET /basic/protected
Authorization: Basic dGVzdHVzZXI6
// base64("testuser:")
Expected: HTTP 401 UnauthorizedValidates rejection of empty passwords.
| Username | Password |
|---|---|
testuser |
testpass |
| Setting | Value |
|---|---|
| Service Port | Dynamic (26000 + Process ID) |
| Realm | "Test Realm" |
| Encoding | UTF-8 (RFC 7617) |
| Validator | TestBasicAuthenticationValidator (hardcoded) |
The test uses a simple inline validator:
class TestBasicAuthenticationValidator : IBasicAuthenticationValidator
{
public Task<bool> ValidateAsync(string username, string password,
CancellationToken cancellationToken = default)
{
var isValid = username == "testuser" && password == "testpass";
return Task.FromResult(isValid);
}
}cd qckdev.AspNetCore.Authentication.Basic
dotnet test qckdev.AspNetCore.Authentication.Basic.sln -v minimaldotnet test --filter "FullyQualifiedName~BasicAuthenticationIntegrationTests.ProtectedEndpoint_WithValidCredentials"dotnet test --logger "console;verbosity=detailed" --no-build[AssemblyInitialize]
↓
LocalTestServiceManager.StartIfNeeded()
├─ Create IHostBuilder
├─ Configure services (AddControllers, AddAuthentication)
├─ Configure middleware (UseRouting, UseAuthentication, UseEndpoints)
├─ Start host on dynamic port
└─ Wait for service ready (polling, 10s timeout)
↓
[Test Methods Execute]
├─ Create HttpClient with base address from service
├─ Build request with headers (Authorization, etc.)
├─ Send request to live endpoint
└─ Assert response status, headers, content
↓
[AssemblyCleanup]
↓
LocalTestServiceManager.Stop()
└─ Dispose host
Tests execute against all supported frameworks:
- .NET Core 3.1 (EOL December 2022)
- .NET 5.0 (EOL May 2022)
- .NET 6.0 (LTS until November 2024)
- .NET 8.0 (LTS until November 2026)
- .NET 10.0 (Current, supported until November 2027)
Each framework runs 7 integration tests independently.
✅ Header parsing - Case-insensitive scheme matching
✅ Encoding - Base64 decoding with UTF-8 charset
✅ Credentials extraction - Splits on first : only
✅ Case sensitivity - Username/password comparison is ordinal (case-sensitive)
✅ Error handling - Base64 decode failures trigger 401
✅ Challenge response - WWW-Authenticate header present and correctly formatted
✅ Identity mapping - Authenticated username available via User.Identity.Name
Symptom: Test hangs for 10 seconds then fails
Cause: Service not responding on expected port
Solution:
- Verify port is not in use:
netstat -ano | findstr :26xxx - Check firewall settings
- Ensure ASP.NET Core runtime installed
Symptom: "Address already in use" error
Cause: Another process using the dynamic port
Solution:
- Tests use different ports based on Process ID
- If running multiple test suites simultaneously, ensure different processes
- Manually free port:
netstat -ano | findstr :26xxx→taskkill /PID xxxx
Symptom: Valid credentials rejected with "Invalid username or password"
Cause: Credentials don't match test validator exactly
Solution:
- Verify credentials in test match
TestBasicAuthenticationValidatorlogic - Check Base64 encoding is correct
- Ensure no trailing whitespace in username/password
- Always use Base64 encoding - Never send credentials in plain text
- Use HTTPS in production - Basic auth transmits credentials in header only
- Test custom validators - Create integration tests for your own validator implementations
- Parallel execution - Tests are thread-safe and work with parallel execution
- Check for timeouts - Custom validators should implement timeouts to prevent hanging
- Test Coverage Guide - Coverage across all projects
- Framework Compatibility - Supported frameworks and versions
- Main README - Usage examples and quickstart
Last updated: March 1, 2026