In modern web and mobile applications, securing REST APIs is critical. Unauthorized access can lead to data leaks, account takeovers, or service abuse. Two of the most widely used approaches for API security are API keys and JWT (JSON Web Tokens).

This article explores how to implement both methods in ASP.NET Core, highlighting best practices, real-world implementation patterns, and strategies for production-ready security.

Table of Contents

  1. Introduction to API Security

  2. API Keys vs JWT Tokens

  3. Architectural Considerations

  4. Securing APIs with API Keys

  5. Securing APIs with JWT Tokens

  6. Token Expiration and Refresh

  7. Role-Based Access Control (RBAC) with JWT

  8. Best Practices for Production

  9. Logging and Monitoring

  10. Common Pitfalls

  11. Conclusion

1. Introduction to API Security

REST APIs are stateless interfaces, which makes them scalable but also vulnerable if not secured. Common security threats include:

API keys and JWT tokens help mitigate these risks by ensuring only authenticated and authorized clients can access the API.

2. API Keys vs JWT Tokens

FeatureAPI KeyJWT Token
TypeSimple static tokenSigned JSON object
Use CaseService-to-service authenticationUser authentication, authorization
ExpirationUsually staticConfigurable, often short-lived
RevocationManualCan use blacklists or short TTL
SecurityLess secure aloneMore secure, can include claims

3. Architectural Considerations

When securing APIs:

  1. Use HTTPS: Never transmit tokens or keys over HTTP.

  2. Separate Authentication and Authorization: Authentication verifies identity; authorization checks permissions.

  3. Use Middleware: ASP.NET Core allows adding middleware for token validation and claims extraction.

  4. Centralized Key Management: Avoid hardcoding keys in code.

4. Securing APIs with API Keys

4.1 Generating API Keys

API keys are usually random strings stored securely in a database. Example:

public string GenerateApiKey()
{
    return Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));
}

4.2 Storing API Keys

public class ApiKey
{
    public string KeyHash { get; set; }
    public string ClientName { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime? ExpiresAt { get; set; }
    public bool IsActive { get; set; }
}

4.3 Validating API Keys in ASP.NET Core

Create a middleware:

public class ApiKeyMiddleware
{
    private readonly RequestDelegate _next;
    private const string API_KEY_HEADER = "X-API-KEY";

    public ApiKeyMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context, IApiKeyService apiKeyService)
    {
        if (!context.Request.Headers.TryGetValue(API_KEY_HEADER, out var extractedKey))
        {
            context.Response.StatusCode = 401;
            await context.Response.WriteAsync("API Key is missing");
            return;
        }

        if (!await apiKeyService.IsValidKeyAsync(extractedKey))
        {
            context.Response.StatusCode = 403;
            await context.Response.WriteAsync("Invalid API Key");
            return;
        }

        await _next(context);
    }
}

Register middleware in Program.cs:

app.UseMiddleware<ApiKeyMiddleware>();

4.4 Best Practices for API Keys

5. Securing APIs with JWT Tokens

JWTs are signed tokens that carry claims about the user or client.

5.1 Installing Packages

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

5.2 Configuring JWT Authentication

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = builder.Configuration["Jwt:Issuer"],
        ValidAudience = builder.Configuration["Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(
            Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
    };
});

5.3 Generating JWT Tokens

public string GenerateJwtToken(User user)
{
    var claims = new[]
    {
        new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
        new Claim("role", user.Role)
    };

    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

    var token = new JwtSecurityToken(
        issuer: _config["Jwt:Issuer"],
        audience: _config["Jwt:Audience"],
        claims: claims,
        expires: DateTime.UtcNow.AddHours(1),
        signingCredentials: creds
    );

    return new JwtSecurityTokenHandler().WriteToken(token);
}

5.4 Validating JWT Tokens

Use the [Authorize] attribute on controllers:

[Authorize(Roles = "Admin")]
[HttpGet("admin-data")]
public IActionResult GetAdminData()
{
    return Ok("Secure admin data");
}

ASP.NET Core middleware handles validation automatically.

6. Token Expiration and Refresh

7. Role-Based Access Control (RBAC) with JWT

[Authorize(Policy = "CanEditOrders")]
public IActionResult EditOrder(int id)
{
    // Only users with "CanEditOrders" claim can access
    return Ok();
}

8. Best Practices for Production

  1. Always use HTTPS to protect tokens and API keys.

  2. Do not store sensitive data in JWT payloads — only claims necessary for authorization.

  3. Rotate keys and secrets regularly.

  4. Limit API access using IP whitelisting and rate limiting.

  5. Blacklist revoked tokens to prevent misuse.

  6. Use standard libraries for token validation instead of custom code.

9. Logging and Monitoring

10. Common Pitfalls

Conclusion

Securing REST APIs with API keys and JWT tokens is essential for modern applications.

By following best practices, implementing proper token validation, expiration, and monitoring, developers can build APIs that are secure, maintainable, and production-ready.

Key Takeaways for Senior Developers