API Gateway Authentication Patterns: JWT, OAuth2, and API Keys Complete Guide 2025

Nov 15, 2025
api-gatewayauthenticationjwtoauth2
0

API gateway authentication is critical for securing microservices architectures. By centralizing authentication at the gateway, you can protect backend services, simplify client implementations, and enforce consistent security policies. Different authentication patterns serve different use cases, from user-facing applications to service-to-service communication.

This comprehensive guide covers authentication patterns for API gateways, including JWT validation, OAuth2 flows, API key management, and service-to-service authentication. You'll learn how to implement these patterns in Ocelot, YARP, and Kong, handle token validation, manage refresh tokens, and deploy production-ready authentication configurations.

Authentication Patterns Overview

Why Authenticate at the Gateway?

Centralizing authentication at the API gateway provides:

  • Single Point of Control: Consistent security policies
  • Backend Protection: Services don't need auth logic
  • Simplified Clients: Standard authentication flow
  • Performance: Token validation at edge
  • Observability: Centralized auth logging

Authentication Methods

1. JWT (JSON Web Tokens)

  • Stateless token validation
  • Self-contained claims
  • Widely supported
  • Good for microservices

2. OAuth2

  • Industry-standard authorization
  • Supports multiple grant types
  • User consent flows
  • Token refresh support

3. API Keys

  • Simple authentication
  • Service-to-service
  • Easy to implement
  • Less secure than tokens

4. mTLS (Mutual TLS)

  • Certificate-based
  • Strong security
  • Service-to-service
  • Complex setup

JWT Authentication

JWT Structure

JWTs consist of three parts:

  • Header: Algorithm and token type
  • Payload: Claims (user info, permissions)
  • Signature: Verification signature

JWT Validation in Ocelot

Configure JWT authentication in Ocelot:

{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/{everything}",
      "UpstreamPathTemplate": "/api/{everything}",
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "Bearer",
        "AllowedScopes": ["api.read", "api.write"]
      }
    }
  ],
  "GlobalConfiguration": {
    "AuthenticationProviderKey": "Bearer"
  }
}
builder.Services.AddAuthentication()
    .AddJwtBearer("Bearer", options =>
    {
        options.Authority = "https://auth.example.com";
        options.Audience = "api.example.com";
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ClockSkew = TimeSpan.Zero
        };
    });

builder.Services.AddOcelot();

JWT Validation in YARP

Implement JWT validation middleware:

builder.Services.AddAuthentication()
    .AddJwtBearer("Bearer", options =>
    {
        options.Authority = "https://auth.example.com";
        options.Audience = "api.example.com";
    });

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapReverseProxy(proxyPipeline =>
{
    proxyPipeline.Use(async (context, next) =>
    {
        if (!context.User.Identity.IsAuthenticated)
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("Unauthorized");
            return;
        }

        await next();
    });
});

Custom JWT Validation

Implement custom validation logic:

public class CustomJwtValidator : ISecurityTokenValidator
{
    private readonly JwtSecurityTokenHandler _handler;
    private readonly ILogger<CustomJwtValidator> _logger;

    public CustomJwtValidator(ILogger<CustomJwtValidator> logger)
    {
        _handler = new JwtSecurityTokenHandler();
        _logger = logger;
    }

    public bool CanValidateToken => true;
    public int MaximumTokenSizeInBytes { get; set; } = TokenValidationParameters.DefaultMaximumTokenSizeInBytes;

    public bool CanReadToken(string securityToken)
    {
        return _handler.CanReadToken(securityToken);
    }

    public ClaimsPrincipal ValidateToken(
        string securityToken,
        TokenValidationParameters validationParameters,
        out SecurityToken validatedToken)
    {
        try
        {
            var principal = _handler.ValidateToken(
                securityToken, validationParameters, out validatedToken);

            // Custom validation logic
            var userId = principal.FindFirst("sub")?.Value;
            if (string.IsNullOrEmpty(userId))
            {
                throw new SecurityTokenValidationException("Missing user ID claim");
            }

            // Check custom claims
            var role = principal.FindFirst("role")?.Value;
            if (role != "admin" && role != "user")
            {
                throw new SecurityTokenValidationException("Invalid role");
            }

            return principal;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "JWT validation failed");
            throw;
        }
    }
}

OAuth2 Authentication

Authorization Code Flow

Implement OAuth2 authorization code flow:

public class OAuth2AuthenticationHandler
{
    private readonly HttpClient _httpClient;
    private readonly OAuth2Config _config;

    public string GetAuthorizationUrl(string state, string redirectUri)
    {
        var parameters = new Dictionary<string, string>
        {
            ["client_id"] = _config.ClientId,
            ["response_type"] = "code",
            ["redirect_uri"] = redirectUri,
            ["scope"] = string.Join(" ", _config.Scopes),
            ["state"] = state
        };

        var queryString = string.Join("&",
            parameters.Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}"));

        return $"{_config.AuthorizationEndpoint}?{queryString}";
    }

    public async Task<TokenResponse> ExchangeCodeForTokenAsync(
        string authorizationCode,
        string redirectUri)
    {
        var request = new HttpRequestMessage(HttpMethod.Post, _config.TokenEndpoint)
        {
            Content = new FormUrlEncodedContent(new Dictionary<string, string>
            {
                ["grant_type"] = "authorization_code",
                ["code"] = authorizationCode,
                ["redirect_uri"] = redirectUri,
                ["client_id"] = _config.ClientId,
                ["client_secret"] = _config.ClientSecret
            })
        };

        var response = await _httpClient.SendAsync(request);
        response.EnsureSuccessStatusCode();

        var content = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<TokenResponse>(content);
    }
}

Client Credentials Flow

Service-to-service authentication:

public class ClientCredentialsFlow
{
    private readonly HttpClient _httpClient;
    private readonly OAuth2Config _config;

    public async Task<TokenResponse> GetAccessTokenAsync()
    {
        var request = new HttpRequestMessage(HttpMethod.Post, _config.TokenEndpoint)
        {
            Content = new FormUrlEncodedContent(new Dictionary<string, string>
            {
                ["grant_type"] = "client_credentials",
                ["client_id"] = _config.ClientId,
                ["client_secret"] = _config.ClientSecret,
                ["scope"] = string.Join(" ", _config.Scopes)
            })
        };

        var response = await _httpClient.SendAsync(request);
        response.EnsureSuccessStatusCode();

        var content = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<TokenResponse>(content);
    }
}

Token Refresh

Implement token refresh logic:

public class TokenRefreshService
{
    private readonly ITokenStore _tokenStore;
    private readonly OAuth2AuthenticationHandler _oauthHandler;
    private readonly ILogger<TokenRefreshService> _logger;

    public async Task<string> GetValidAccessTokenAsync(string userId)
    {
        var token = await _tokenStore.GetTokenAsync(userId);
        
        if (token == null)
        {
            throw new InvalidOperationException("No token found");
        }

        // Check if token needs refresh (5 minutes before expiry)
        if (DateTime.UtcNow >= token.ExpiresAt.AddMinutes(-5))
        {
            _logger.LogInformation("Refreshing token for user {UserId}", userId);
            
            var newToken = await _oauthHandler.RefreshTokenAsync(token.RefreshToken);
            await _tokenStore.SaveTokenAsync(userId, newToken);
            
            return newToken.AccessToken;
        }

        return token.AccessToken;
    }
}

API Key Authentication

API Key Validation

Implement API key validation:

public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
{
    private readonly IApiKeyValidator _apiKeyValidator;

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        if (!Request.Headers.TryGetValue("X-API-Key", out var apiKeyHeader))
        {
            return AuthenticateResult.Fail("Missing API key");
        }

        var apiKey = apiKeyHeader.ToString();
        var client = await _apiKeyValidator.ValidateApiKeyAsync(apiKey);

        if (client == null)
        {
            return AuthenticateResult.Fail("Invalid API key");
        }

        var claims = new[]
        {
            new Claim(ClaimTypes.NameIdentifier, client.ClientId),
            new Claim(ClaimTypes.Name, client.ClientName),
            new Claim("ApiKeyId", client.ApiKeyId)
        };

        var identity = new ClaimsIdentity(claims, Scheme.Name);
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, Scheme.Name);

        return AuthenticateResult.Success(ticket);
    }
}

API Key Store

Store and validate API keys:

public interface IApiKeyStore
{
    Task<ApiKeyClient> GetClientByApiKeyAsync(string apiKey);
    Task<bool> IsApiKeyValidAsync(string apiKey);
    Task RevokeApiKeyAsync(string apiKey);
}

public class DatabaseApiKeyStore : IApiKeyStore
{
    private readonly IDbContext _dbContext;

    public async Task<ApiKeyClient> GetClientByApiKeyAsync(string apiKey)
    {
        var hash = HashApiKey(apiKey);
        var client = await _dbContext.ApiKeys
            .Where(k => k.KeyHash == hash && k.IsActive)
            .Select(k => k.Client)
            .FirstOrDefaultAsync();

        return client;
    }

    private string HashApiKey(string apiKey)
    {
        using var sha256 = SHA256.Create();
        var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(apiKey));
        return Convert.ToBase64String(hash);
    }
}

Service-to-Service Authentication

Mutual TLS (mTLS)

Implement mTLS for service authentication:

public class MutualTlsHandler : DelegatingHandler
{
    private readonly X509Certificate2 _clientCertificate;

    public MutualTlsHandler(X509Certificate2 clientCertificate)
    {
        _clientCertificate = clientCertificate;
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        var handler = new HttpClientHandler
        {
            ClientCertificates = { _clientCertificate },
            ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
            {
                // Validate server certificate
                return ValidateServerCertificate(cert, chain, errors);
            }
        };

        using var httpClient = new HttpClient(handler);
        return await httpClient.SendAsync(request, cancellationToken);
    }
}

Service Account Tokens

Use service account tokens:

public class ServiceAccountAuthentication
{
    private readonly ITokenService _tokenService;
    private readonly IConfiguration _configuration;

    public async Task<string> GetServiceTokenAsync()
    {
        var serviceAccount = _configuration["ServiceAccount:Id"];
        var secret = _configuration["ServiceAccount:Secret"];

        return await _tokenService.GetServiceTokenAsync(serviceAccount, secret);
    }
}

Forwarding Authentication

Forward User Claims

Forward authentication to backend services:

public class ForwardAuthMiddleware
{
    private readonly RequestDelegate _next;

    public async Task InvokeAsync(HttpContext context)
    {
        if (context.User.Identity.IsAuthenticated)
        {
            // Forward user ID
            context.Request.Headers.Add("X-User-Id",
                context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value);

            // Forward user claims
            var claims = context.User.Claims
                .Select(c => $"{c.Type}={c.Value}");
            context.Request.Headers.Add("X-User-Claims",
                string.Join(",", claims));

            // Forward roles
            var roles = context.User.FindAll(ClaimTypes.Role)
                .Select(c => c.Value);
            context.Request.Headers.Add("X-User-Roles",
                string.Join(",", roles));
        }

        await _next(context);
    }
}

Best Practices

1. Validate Tokens Properly

  • Validate issuer, audience, and expiration
  • Check signature
  • Verify claims
  • Handle clock skew

2. Secure Token Storage

  • Never store tokens in localStorage for web apps
  • Use httpOnly cookies when possible
  • Encrypt tokens at rest
  • Implement token rotation

3. Handle Token Refresh

  • Refresh before expiration
  • Handle refresh failures
  • Implement retry logic
  • Log refresh events

4. Monitor Authentication

  • Track authentication failures
  • Monitor token usage
  • Alert on suspicious patterns
  • Log all auth events

5. Implement Rate Limiting

  • Limit authentication attempts
  • Prevent brute force attacks
  • Throttle token requests
  • Monitor for abuse

Advanced Authentication Patterns

Token Introspection

Validate tokens via introspection endpoint:

public class TokenIntrospectionService
{
    public async Task<TokenInfo> IntrospectTokenAsync(string token)
    {
        var request = new HttpRequestMessage(HttpMethod.Post, _introspectionEndpoint)
        {
            Content = new FormUrlEncodedContent(new Dictionary<string, string>
            {
                ["token"] = token,
                ["token_type_hint"] = "access_token"
            })
        };

        request.Headers.Authorization = new AuthenticationHeaderValue(
            "Basic", Convert.ToBase64String(
                Encoding.UTF8.GetBytes($"{_clientId}:{_clientSecret}")));

        var response = await _httpClient.SendAsync(request);
        var content = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<TokenInfo>(content);
    }
}

Multi-Tenant Authentication

Handle authentication for multiple tenants:

public class MultiTenantAuthHandler
{
    public async Task<ClaimsPrincipal> AuthenticateAsync(HttpContext context)
    {
        var tenantId = ExtractTenantId(context);
        var authConfig = await GetAuthConfigForTenantAsync(tenantId);
        
        return await AuthenticateWithConfigAsync(context, authConfig);
    }
}

Real-World Scenarios

Scenario 1: Service Mesh Integration

Integrate with service mesh authentication:

public class ServiceMeshAuthHandler
{
    public async Task ForwardAuthToMeshAsync(HttpContext context)
    {
        // Extract mTLS certificate
        var certificate = context.Connection.ClientCertificate;
        
        // Forward to service mesh
        context.Request.Headers.Add("X-Client-Cert", 
            Convert.ToBase64String(certificate.RawData));
    }
}

Extended FAQ

Q: How do I handle token expiration at the gateway?

A: Implement automatic refresh:

public async Task<T> CallWithAutoRefreshAsync<T>(Func<string, Task<T>> apiCall)
{
    var token = await GetValidTokenAsync();
    
    try
    {
        return await apiCall(token);
    }
    catch (UnauthorizedException)
    {
        token = await RefreshTokenAsync();
        return await apiCall(token);
    }
}

Conclusion

Implementing proper authentication at the API gateway is essential for securing microservices. By understanding JWT validation, OAuth2 flows, API keys, and service-to-service authentication, you can build secure, production-ready API gateways that protect your backend services.

Key Takeaways:

  1. Use JWT for stateless auth - Good for microservices
  2. Implement OAuth2 properly - Handle all grant types
  3. Secure API keys - Hash and validate properly
  4. Forward authentication - Pass user context to backends
  5. Monitor authentication - Track failures and usage
  6. Handle token refresh - Implement proper refresh logic
  7. Token introspection - Validate tokens remotely
  8. Multi-tenant support - Handle multiple tenants
  9. Service mesh integration - mTLS support
  10. Automatic refresh - Seamless token management

Next Steps:

  1. Choose authentication method
  2. Implement token validation
  3. Set up token refresh
  4. Configure forwarding
  5. Set up monitoring
  6. Implement introspection
  7. Add multi-tenant support

Authentication Implementation Guide

Step-by-Step Implementation

  1. Choose Authentication Method

    • JWT for stateless auth
    • OAuth 2.0 for delegated access
    • API keys for service-to-service
    • mTLS for high security
  2. Implement Token Validation

    • Validate token signature
    • Check token expiration
    • Verify token issuer
    • Validate token audience
  3. Set Up Token Refresh

    • Handle refresh tokens
    • Implement automatic refresh
    • Cache refreshed tokens
    • Handle refresh failures
  4. Configure Forwarding

    • Forward user context
    • Add custom headers
    • Preserve authentication info
    • Handle token propagation
  5. Set Up Monitoring

    • Track authentication failures
    • Monitor token usage
    • Alert on anomalies
    • Generate audit logs

Authentication Patterns

JWT Validation

public class JwtValidator
{
    public async Task<ClaimsPrincipal> ValidateJwtAsync(string token)
    {
        var handler = new JwtSecurityTokenHandler();
        var validationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = await GetSigningKeyAsync()
        };

        return handler.ValidateToken(token, validationParameters, out _);
    }
}

OAuth 2.0 Token Refresh

public class OAuthTokenRefresher
{
    public async Task<TokenResponse> RefreshTokenAsync(string refreshToken)
    {
        var request = new HttpRequestMessage(HttpMethod.Post, _tokenEndpoint)
        {
            Content = new FormUrlEncodedContent(new Dictionary<string, string>
            {
                ["grant_type"] = "refresh_token",
                ["refresh_token"] = refreshToken,
                ["client_id"] = _clientId,
                ["client_secret"] = _clientSecret
            })
        };

        var response = await _httpClient.SendAsync(request);
        return await response.Content.ReadFromJsonAsync<TokenResponse>();
    }
}

Security Best Practices

Token Storage

  • Never store tokens in localStorage
  • Use httpOnly cookies for web apps
  • Encrypt tokens at rest
  • Implement token rotation
  • Monitor token usage

Token Validation

  • Always validate token signature
  • Check token expiration
  • Verify token issuer
  • Validate token audience
  • Check token revocation

Authentication Configuration Examples

Ocelot JWT Configuration

Configure JWT in Ocelot:

{
  "AuthenticationOptions": {
    "AuthenticationProviderKey": "Bearer",
    "AllowedScopes": []
  },
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/{everything}",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "api.example.com",
          "Port": 443
        }
      ],
      "UpstreamPathTemplate": "/api/{everything}",
      "UpstreamHttpMethod": [ "GET", "POST" ],
      "AuthenticationOptions": {
        "AuthenticationProviderKey": "Bearer",
        "AllowedScopes": []
      }
    }
  ]
}

YARP Authentication

Configure authentication in YARP:

builder.Services.AddAuthentication("Bearer")
    .AddJwtBearer("Bearer", options =>
    {
        options.Authority = "https://identity.example.com";
        options.Audience = "api.example.com";
    });

app.UseAuthentication();
app.UseAuthorization();

app.MapReverseProxy()
    .RequireAuthorization();

Kong OAuth Configuration

Configure OAuth in Kong:

plugins:
- name: oauth2
  config:
    scopes:
    - read
    - write
    mandatory_scope: true
    token_expiration: 3600

Token Management

Token Caching

Cache tokens for performance:

public class TokenCache
{
    private readonly IMemoryCache _cache;

    public async Task<string> GetTokenAsync(string clientId)
    {
        var cacheKey = $"token:{clientId}";
        
        if (_cache.TryGetValue(cacheKey, out string cached))
        {
            return cached;
        }

        var token = await GetNewTokenAsync(clientId);
        _cache.Set(cacheKey, token, TimeSpan.FromMinutes(55));
        
        return token;
    }
}

Authentication Best Practices

Token Validation Pipeline

Implement comprehensive token validation:

public class TokenValidationPipeline
{
    public async Task<ValidationResult> ValidateTokenAsync(string token)
    {
        // Step 1: Parse token
        var parsed = ParseToken(token);
        if (parsed == null)
        {
            return ValidationResult.Invalid("Invalid token format");
        }

        // Step 2: Validate signature
        if (!await ValidateSignatureAsync(parsed))
        {
            return ValidationResult.Invalid("Invalid token signature");
        }

        // Step 3: Check expiration
        if (IsExpired(parsed))
        {
            return ValidationResult.Expired("Token expired");
        }

        // Step 4: Check revocation
        if (await IsRevokedAsync(parsed))
        {
            return ValidationResult.Revoked("Token revoked");
        }

        // Step 5: Validate claims
        if (!ValidateClaims(parsed))
        {
            return ValidationResult.Invalid("Invalid token claims");
        }

        return ValidationResult.Valid(parsed);
    }
}

Authentication Middleware

Implement authentication middleware:

public class AuthenticationMiddleware
{
    public async Task InvokeAsync(HttpContext context)
    {
        var token = ExtractToken(context);
        
        if (string.IsNullOrEmpty(token))
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("Unauthorized");
            return;
        }

        var validationResult = await _tokenValidator.ValidateTokenAsync(token);
        
        if (!validationResult.IsValid)
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync(validationResult.Error);
            return;
        }

        context.User = validationResult.Principal;
        await _next(context);
    }
}

OAuth 2.0 Implementation

Authorization Code Flow

Implement authorization code flow:

public class OAuth2AuthorizationCodeFlow
{
    public async Task<string> GetAuthorizationUrlAsync(string clientId, string redirectUri)
    {
        var parameters = new Dictionary<string, string>
        {
            ["response_type"] = "code",
            ["client_id"] = clientId,
            ["redirect_uri"] = redirectUri,
            ["scope"] = "read write",
            ["state"] = GenerateState()
        };

        var queryString = string.Join("&", 
            parameters.Select(p => $"{p.Key}={Uri.EscapeDataString(p.Value)}"));

        return $"{_authorizationEndpoint}?{queryString}";
    }

    public async Task<TokenResponse> ExchangeCodeForTokenAsync(
        string code, 
        string redirectUri)
    {
        var request = new HttpRequestMessage(HttpMethod.Post, _tokenEndpoint)
        {
            Content = new FormUrlEncodedContent(new Dictionary<string, string>
            {
                ["grant_type"] = "authorization_code",
                ["code"] = code,
                ["redirect_uri"] = redirectUri,
                ["client_id"] = _clientId,
                ["client_secret"] = _clientSecret
            })
        };

        var response = await _httpClient.SendAsync(request);
        return await response.Content.ReadFromJsonAsync<TokenResponse>();
    }
}

Client Credentials Flow

Implement client credentials flow:

public class OAuth2ClientCredentialsFlow
{
    public async Task<TokenResponse> GetTokenAsync()
    {
        var request = new HttpRequestMessage(HttpMethod.Post, _tokenEndpoint)
        {
            Content = new FormUrlEncodedContent(new Dictionary<string, string>
            {
                ["grant_type"] = "client_credentials",
                ["client_id"] = _clientId,
                ["client_secret"] = _clientSecret,
                ["scope"] = "api.read api.write"
            })
        };

        var response = await _httpClient.SendAsync(request);
        return await response.Content.ReadFromJsonAsync<TokenResponse>();
    }
}

Token Introspection

Token Introspection Implementation

Implement token introspection:

public class TokenIntrospection
{
    public async Task<IntrospectionResult> IntrospectTokenAsync(string token)
    {
        var request = new HttpRequestMessage(HttpMethod.Post, _introspectionEndpoint)
        {
            Content = new FormUrlEncodedContent(new Dictionary<string, string>
            {
                ["token"] = token,
                ["token_type_hint"] = "access_token",
                ["client_id"] = _clientId,
                ["client_secret"] = _clientSecret
            })
        };

        var response = await _httpClient.SendAsync(request);
        var result = await response.Content.ReadFromJsonAsync<IntrospectionResult>();

        return result;
    }
}

Authentication Best Practices Summary

Implementation Checklist

  • Choose authentication method (JWT, OAuth 2.0, API keys)
  • Implement token validation
  • Set up token refresh
  • Configure token caching
  • Enable audit logging
  • Set up monitoring
  • Test authentication flows
  • Document authentication procedures
  • Review security settings
  • Plan for token rotation

Production Deployment

Before deploying authentication:

  1. Test all authentication flows
  2. Verify token validation works
  3. Test token refresh
  4. Validate error handling
  5. Set up monitoring
  6. Load test authentication
  7. Document procedures
  8. Review security settings

Authentication Troubleshooting

Common Issues

Troubleshoot authentication issues:

public class AuthenticationTroubleshooter
{
    public async Task<DiagnosticsResult> DiagnoseAsync(string token)
    {
        var diagnostics = new DiagnosticsResult();

        // Check token format
        diagnostics.TokenFormatValid = ValidateTokenFormat(token);

        // Check token signature
        diagnostics.SignatureValid = await ValidateSignatureAsync(token);

        // Check token expiration
        diagnostics.NotExpired = CheckExpiration(token);

        // Check token revocation
        diagnostics.NotRevoked = await CheckRevocationAsync(token);

        return diagnostics;
    }
}

Authentication Implementation Examples

JWT Validation Service

Complete JWT validation:

public class JwtValidationService
{
    private readonly IConfiguration _configuration;
    private readonly ILogger<JwtValidationService> _logger;

    public async Task<ClaimsPrincipal> ValidateTokenAsync(string token)
    {
        var handler = new JwtSecurityTokenHandler();
        var validationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = _configuration["Jwt:Issuer"],
            ValidateAudience = true,
            ValidAudience = _configuration["Jwt:Audience"],
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = await GetSigningKeyAsync(),
            ClockSkew = TimeSpan.Zero
        };

        try
        {
            var principal = handler.ValidateToken(token, validationParameters, out _);
            return principal;
        }
        catch (SecurityTokenExpiredException)
        {
            _logger.LogWarning("Token expired");
            throw;
        }
        catch (SecurityTokenInvalidSignatureException)
        {
            _logger.LogWarning("Invalid token signature");
            throw;
        }
    }
}

OAuth 2.0 Client

Complete OAuth 2.0 client:

public class OAuth2Client
{
    private readonly HttpClient _httpClient;
    private readonly OAuth2Config _config;

    public async Task<TokenResponse> GetTokenAsync(string code, string redirectUri)
    {
        var request = new HttpRequestMessage(HttpMethod.Post, _config.TokenEndpoint)
        {
            Content = new FormUrlEncodedContent(new Dictionary<string, string>
            {
                ["grant_type"] = "authorization_code",
                ["code"] = code,
                ["redirect_uri"] = redirectUri,
                ["client_id"] = _config.ClientId,
                ["client_secret"] = _config.ClientSecret
            })
        };

        var response = await _httpClient.SendAsync(request);
        response.EnsureSuccessStatusCode();

        return await response.Content.ReadFromJsonAsync<TokenResponse>();
    }
}

Authentication Token Management

Token Refresh Strategy

Implement token refresh:

public class TokenRefreshStrategy
{
    private readonly ITokenCache _tokenCache;
    private readonly ILogger<TokenRefreshStrategy> _logger;

    public async Task<string> GetValidTokenAsync(string userId)
    {
        var cachedToken = await _tokenCache.GetTokenAsync(userId);

        if (cachedToken != null && !IsTokenExpiringSoon(cachedToken))
        {
            return cachedToken.AccessToken;
        }

        // Refresh token
        var refreshedToken = await RefreshTokenAsync(cachedToken?.RefreshToken);
        await _tokenCache.SetTokenAsync(userId, refreshedToken);

        return refreshedToken.AccessToken;
    }

    private bool IsTokenExpiringSoon(TokenInfo token)
    {
        var expirationTime = token.ExpiresAt;
        var timeUntilExpiration = expirationTime - DateTime.UtcNow;
        return timeUntilExpiration < TimeSpan.FromMinutes(5);
    }
}

Token Revocation

Handle token revocation:

public class TokenRevocationService
{
    public async Task RevokeTokenAsync(string token)
    {
        // Add to revocation list
        await AddToRevocationListAsync(token);

        // Invalidate cache
        await InvalidateTokenCacheAsync(token);

        // Notify clients
        await NotifyClientsAsync(token);
    }

    public async Task<bool> IsTokenRevokedAsync(string token)
    {
        return await IsInRevocationListAsync(token);
    }
}

Authentication Security Hardening

Token Validation Hardening

Harden token validation:

public class HardenedTokenValidator
{
    public async Task<ValidationResult> ValidateTokenHardenedAsync(string token)
    {
        // Check revocation
        if (await IsTokenRevokedAsync(token))
        {
            return new ValidationResult { IsValid = false, Reason = "Token revoked" };
        }

        // Check expiration
        if (IsTokenExpired(token))
        {
            return new ValidationResult { IsValid = false, Reason = "Token expired" };
        }

        // Check signature
        if (!await ValidateTokenSignatureAsync(token))
        {
            return new ValidationResult { IsValid = false, Reason = "Invalid signature" };
        }

        // Check issuer
        if (!ValidateTokenIssuer(token))
        {
            return new ValidationResult { IsValid = false, Reason = "Invalid issuer" };
        }

        // Check audience
        if (!ValidateTokenAudience(token))
        {
            return new ValidationResult { IsValid = false, Reason = "Invalid audience" };
        }

        return new ValidationResult { IsValid = true };
    }
}

Authentication Rate Limiting

Limit authentication attempts:

public class AuthenticationRateLimiter
{
    private readonly IRateLimiter _rateLimiter;

    public async Task<bool> CheckAuthenticationRateLimitAsync(string identifier)
    {
        var key = $"auth:ratelimit:{identifier}";
        return await _rateLimiter.CheckLimitAsync(key, 5, TimeSpan.FromMinutes(15));
    }
}

Authentication Best Practices Summary

Key Takeaways

  1. Use JWT for Stateless Authentication: Ideal for microservices and distributed systems
  2. Implement OAuth 2.0 for Third-Party Integration: Standard protocol for secure authorization
  3. Always Validate Tokens: Check signature, expiration, issuer, and audience
  4. Implement Token Refresh: Seamless user experience with automatic token renewal
  5. Monitor Authentication: Track authentication failures, token usage, and security events

Security Considerations

  • Always use HTTPS for token transmission
  • Implement proper token storage (secure cookies or memory)
  • Validate tokens on every request
  • Implement token revocation for security incidents
  • Use short-lived access tokens with longer refresh tokens
  • Monitor for suspicious authentication patterns

Common Pitfalls to Avoid

  • Storing tokens in localStorage (XSS vulnerability)
  • Not validating token signatures
  • Using long-lived tokens without refresh mechanism
  • Not implementing token revocation
  • Failing to monitor authentication events

Authentication Implementation Checklist

Pre-Implementation

  • Choose authentication method (JWT, OAuth 2.0, API keys)
  • Design token structure and claims
  • Plan token expiration and refresh strategy
  • Select token storage mechanism
  • Design error handling and security measures

Implementation

  • Implement token generation/validation
  • Add authentication middleware
  • Implement token refresh mechanism
  • Add token revocation support
  • Configure security headers

Post-Implementation

  • Monitor authentication metrics
  • Review security logs regularly
  • Test token expiration and refresh
  • Update documentation
  • Train team on authentication patterns

Authentication Troubleshooting Guide

Common Issues

Issue 1: Token validation failures

  • Symptom: Valid tokens being rejected
  • Solution: Check token signature, expiration, issuer, audience
  • Prevention: Implement comprehensive token validation

Issue 2: Token refresh not working

  • Symptom: Users getting logged out unexpectedly
  • Solution: Check refresh token validity and expiration
  • Prevention: Implement proper refresh token handling

Issue 3: Security vulnerabilities

  • Symptom: Tokens being compromised
  • Solution: Implement token revocation, use HTTPS, secure storage
  • Prevention: Follow security best practices from the start

For more API gateway guidance, explore our API Gateway Comparison or Rate Limiting Strategies.

Related posts