Documentation
adrs/010-jwt-bearer-authentication.md
ADR-010: JWT Bearer Authentication Standard
Status
Accepted
Context
The Dynaplex architecture initially supported dual authentication mechanisms: standard JWT Bearer tokens and a custom "acsistoken" header authentication. This dual approach added complexity to the authentication pipeline, increased maintenance burden, and created potential security inconsistencies.
Historical context:
- Legacy system used custom token headers for backwards compatibility
- Some older clients relied on the custom authentication mechanism
- JWT Bearer is the industry standard for API authentication
- Supporting multiple authentication methods complicates security audits
Decision
We will standardize on JWT Bearer authentication only for all Dynaplex services. The custom "acsistoken" header authentication will be removed completely.
Implementation approach:
- Use standard
Authorization: Bearer <token>headers - Remove
AcsisTokenAuthenticationHandlerand related code - Configure JWT Bearer authentication in all services
- Use Microsoft Identity platform for token validation
- Implement consistent token validation across all services
JWT Configuration
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = configuration["Identity:Authority"];
options.Audience = configuration["Identity:Audience"];
options.RequireHttpsMetadata = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.FromMinutes(5)
};
});
Consequences
Positive
- Standards Compliance: Following OAuth 2.0 and OpenID Connect standards
- Security: Single, well-tested authentication mechanism
- Tooling Support: Better support in API clients, Postman, etc.
- Simplicity: Reduced code complexity and maintenance
- Interoperability: Easier integration with third-party services
- Documentation: Standard authentication is well documented
Negative
- Breaking Change: Clients using custom token must be updated
- Migration Effort: Need to update all existing client applications
- No Fallback: Cannot support legacy clients during transition
Neutral
- Token Format: JWT tokens are larger than custom tokens
- Performance: JWT validation has cryptographic overhead
- Debugging: Standard tools can inspect JWT tokens
Implementation Notes
Service Configuration
Each service must configure JWT Bearer:
// In Program.cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer();
builder.Services.AddAuthorization();
// After building
app.UseAuthentication();
app.UseAuthorization();
Token Claims
Standard claims to include:
sub: User identifiername: User display nameemail: User email addressroles: User roles arraypermissions: Optional fine-grained permissionsiat: Issued at timestampexp: Expiration timestampjti: Unique token identifier
Migration Strategy
- Phase 1: Add JWT support alongside custom tokens
- Phase 2: Update all clients to use JWT
- Phase 3: Deprecate custom token endpoints
- Phase 4: Remove custom token authentication code
Client Updates
Update all API clients to use Bearer tokens:
// Old way (removed)
client.DefaultRequestHeaders.Add("acsistoken", token);
// New way
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
Token Generation
Identity service generates tokens:
public string GenerateJwtToken(User user)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_configuration["Jwt:Secret"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Username),
new Claim(ClaimTypes.Email, user.Email)
}),
Expires = DateTime.UtcNow.AddHours(8),
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
Security Best Practices
- Use HTTPS only for token transmission
- Implement token refresh mechanism
- Short token lifetime (15-60 minutes)
- Secure token storage on client side
- Implement token revocation list if needed
- Log authentication events for auditing
Related ADRs
- ADR-007: Migration to .NET Aspire Microservices (requires consistent auth)
- ADR-018: Removing MediatR and CQRS Patterns (simplification theme)
- ADR-021: Microsoft Kiota for API Client Generation (generates Bearer auth code)