ASP.NET Core  

Securing APIs with JWT and OAuth2 in ASP.NET Core

In modern applications, APIs are the backbone of communication between frontend clients, mobile apps, and backend services. With this increasing dependency, securing APIs has become more critical than ever. Two widely adopted approaches for securing APIs are JSON Web Tokens (JWT) and OAuth2. ASP.NET Core provides first-class support for implementing these security models.

In this article, we’ll explore how to secure APIs in ASP.NET Core using JWT Authentication and OAuth2 Authorization, along with best practices and sample code.

Why Secure APIs?

APIs often expose sensitive data and operations. Without proper security, attackers could:

  • Steal personal information

  • Modify or delete data

  • Impersonate users

  • Launch replay or injection attacks

That’s why securing APIs with industry standards like JWT and OAuth2 is essential.

JSON Web Tokens (JWT) Overview

A JWT is a compact, URL-safe token that securely represents claims between two parties. It has three parts:

  1. Header: contains the signing algorithm (e.g., HS256).

  2. Payload: contains user claims (e.g., userId, role).

  3. Signature: ensures token integrity.

Example JWT

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Benefits of JWT

  • Stateless (no need for server-side sessions)

  • Lightweight and easy to transmit

  • Widely supported across platforms

OAuth2 Overview

OAuth2 is an authorization framework that allows third-party applications to access APIs on behalf of a user. Instead of sharing credentials, OAuth2 uses access tokens (often JWTs).

Common OAuth2 flows:

  • Authorization Code (for web apps)

  • Client Credentials (for service-to-service)

  • Password Grant (legacy, avoid if possible)

Setting Up JWT Authentication in ASP.NET Core

Step 1. Install NuGet Packages

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

Step 2. Configure JWT in Program.cs

var builder = WebApplication.CreateBuilder(args);

// Add authentication
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = "JwtBearer";
    options.DefaultChallengeScheme = "JwtBearer";
})
.AddJwtBearer("JwtBearer", options =>
{
    options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = builder.Configuration["Jwt:Issuer"],
        ValidAudience = builder.Configuration["Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(
            System.Text.Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
    };
});

builder.Services.AddAuthorization();

var app = builder.Build();

// Use authentication and authorization
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

Step 3. Generate JWT Token

[HttpPost("login")]
public IActionResult Login([FromBody] LoginModel model)
{
    if (model.Username == "admin" && model.Password == "password")
    {
        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, model.Username),
            new Claim("role", "Administrator")
        };

        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.Now.AddMinutes(30),
            signingCredentials: creds);

        return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
    }
    return Unauthorized();
}

Step 4. Secure API Endpoints

[Authorize]
[HttpGet("secure-data")]
public IActionResult GetSecureData()
{
    return Ok(new { Message = "This is a protected API response." });
}

Implementing OAuth2 with IdentityServer or Azure AD

For enterprise-level solutions, OAuth2 is best implemented with an Authorization Server like:

  • IdentityServer (self-hosted)

  • Azure AD / OpenID Connect (cloud)

  • Auth0 / Okta (third-party)

Example: Using Azure AD

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = $"https://login.microsoftonline.com/{tenantId}/v2.0";
        options.Audience = clientId;
    });

This offloads authentication to Azure AD and validates incoming JWT access tokens automatically.

Best Practices for Securing APIs

  1. Always use HTTPS: Prevents token interception.

  2. Set token expiration: Short-lived tokens reduce risk.

  3. Use refresh tokens: For renewing expired access tokens.

  4. Validate issuer and audience: Ensures tokens are from trusted sources.

  5. Limit scopes/roles: Apply least-privilege access.

  6. Revoke compromised tokens: Use blacklists or store token identifiers.

Conclusion

Securing APIs is a non-negotiable requirement in modern applications.

  • JWT provides a simple, stateless way to secure APIs.

  • OAuth2 offers a complete authorization framework for more complex scenarios.

By combining these techniques in ASP.NET Core, you can build secure, scalable, and reliable APIs.