Security  

Top .NET 8 Security Techniques for Web APIs You Must Know

Introduction

Securing Web APIs is a crucial part of modern application development since every API can be a potential target. With .NET 8, Microsoft provides built-in tools that make it easier to protect APIs from unauthorized access. Features like JWT authentication, role-based authorization, and CORS help enforce strong security within applications.

These protections can be strengthened further with HTTPS, rate limiting, and token validation to guard against common threats. Combined, they create a solid security strategy that balances safety with performance. In this article, we’ll look at practical ways to secure a Web API in .NET 8 with code examples.

1. Identity ( ASP.NET Core Identity)

What: Manages users, passwords, roles and supports sign-in, lockout, password hashing.
Why: Central user store and authentication foundation.

Code (Startup-style / minimal)

  
    // Program.cs (ASP.NET Core)
var builder = WebApplication.CreateBuilder(args);

// Add EF Core + Identity (example using in-memory for demo)
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseInMemoryDatabase("AuthDb"));
builder.Services.AddIdentity<IdentityUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

builder.Services.AddAuthentication();
builder.Services.AddAuthorization();

var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();

app.MapPost("/register", async (UserManager<IdentityUser> um, RegisterDto dto) =>
{
    var user = new IdentityUser { UserName = dto.Email, Email = dto.Email };
    var result = await um.CreateAsync(user, dto.Password);
    return result.Succeeded ? Results.Ok() : Results.BadRequest(result.Errors);
});

app.Run();

record RegisterDto(string Email, string Password);
public class ApplicationDbContext : IdentityDbContext<IdentityUser> {
  public ApplicationDbContext(DbContextOptions options): base(options) {}
}
  

2. JWT (JSON Web Token)

What: Self-contained token format containing claims, signed by server.
Why: Stateless auth across distributed services.

Code (validate JWT in API)

  
    var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddJwtBearer(options =>
  {
      options.TokenValidationParameters = new TokenValidationParameters
      {
          ValidateIssuer = true,
          ValidateAudience = true,
          ValidateLifetime = true,
          ValidateIssuerSigningKey = true,
          ValidIssuer = "https://my.authserver",
          ValidAudience = "myapi",
          IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("super-secret-key-12345"))
      };
  });

builder.Services.AddAuthorization();

var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();

app.MapGet("/secure", [Authorize] () => "You are authenticated");

app.Run();
  

3. OAuth2 (example: Client Credentials / Authorization)

What: Authorization framework to obtain access tokens for APIs (many flows).
Why: Standard way for third-party apps and services to gain scoped access.

Code (illustration: validate token - server trusts an identity provider)

  
    // On resource server: accept tokens issued by an OAuth2 auth server (Authority)
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddJwtBearer(options =>
  {
      options.Authority = "https://auth.example.com"; // OAuth2 Authorization Server
      options.Audience = "myapi";
      options.RequireHttpsMetadata = true;
  });

builder.Services.AddAuthorization();
  

4. OpenID Connect (OIDC)

What: Identity layer on top of OAuth2 for authentication (sign-in, user info).

Why: Standard single-sign-on and user identity claims.

Code (OIDC login for web app)

  
    // For MVC/web app that wants interactive login
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
    options.Authority = "https://accounts.example.com";
    options.ClientId = "mvc_client";
    options.ClientSecret = "secret";
    options.ResponseType = "code";
    options.SaveTokens = true;
    options.Scope.Add("profile");
    options.Scope.Add("email");
});
  

5. API Key Authentication

What: A simple shared key that clients include in requests to authenticate.

Why: Simple service-to-service or third-party access when fine-grained user identity isn’t needed.

Code (custom middleware)

  
    // Simple API key middleware
app.Use(async (context, next) =>
{
    if (!context.Request.Headers.TryGetValue("X-API-KEY", out var extractedKey))
    {
        context.Response.StatusCode = 401;
        await context.Response.WriteAsync("API Key required");
        return;
    }
    var configuredKey = builder.Configuration["ApiKey"] ?? "dev-key-123";
    if (!configuredKey.Equals(extractedKey))
    {
        context.Response.StatusCode = 403;
        await context.Response.WriteAsync("Invalid API Key");
        return;
    }
    await next();
});
  

6. Mutual TLS (mTLS)

What: Both client and server present certificates for authentication.

Why: Strong machine-level authentication for B2B/high-security APIs.

Code (Kestrel server requiring client cert)

  
    // Program.cs - Kestrel configuration
builder.WebHost.ConfigureKestrel(options =>
{
    options.ConfigureHttpsDefaults(httpsOptions =>
    {
        httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
        httpsOptions.AllowAnyClientCertificate(); // for demo; validate in app
    });
});

var app = builder.Build();

app.Use(async (ctx, next) =>
{
    var clientCert = ctx.Connection.ClientCertificate;
    if (clientCert == null)
    {
        ctx.Response.StatusCode = 401;
        await ctx.Response.WriteAsync("Client certificate required");
        return;
    }
    // Validate cert: thumbprint, issuer, etc.
    await next();
});
  

(In production, validate certificates properly and supply server certificate via Kestrel config.)

7. Role-Based Access Control (RBAC)

What: Grant rights based on user roles (e.g., Admin , User ).

Why: Easy mapping of permissions to roles.

Code (use roles with ASP.NET Core Identity / JWT claims)

  
    // Configure auth (JWT as above), then:
app.MapGet("/admin", [Authorize(Roles = "Admin")] () => "Admins only");

// Or policy-based role example at startup:
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("RequireManager", policy => policy.RequireRole("Manager"));
});
app.MapGet("/manager", [Authorize(Policy = "RequireManager")] () => "Managers only");
  

8. Policy-Based Authorization (claims/custom requirements)

What: Fine-grained rules based on claims, attributes, or custom handlers.

Why: Implement business rules like “canEdit = true” or “must belong to department X”.

Code (policy based on a claim)

  
    builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("Over18", policy =>
        policy.RequireAssertion(ctx =>
            ctx.User.HasClaim(c => c.Type == "age" && int.Parse(c.Value) >= 18)));
});

app.MapGet("/adults", [Authorize(Policy = "Over18")] () => "18+ resource");
  

Custom requirement example

  
    public class SameCompanyRequirement : IAuthorizationRequirement { }
public class SameCompanyHandler : AuthorizationHandler<SameCompanyRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SameCompanyRequirement requirement)
    {
        var company = context.User.FindFirst("company")?.Value;
        // validate company matches resource claim or route
        if (!string.IsNullOrEmpty(company)) context.Succeed(requirement);
        return Task.CompletedTask;
    }
}
builder.Services.AddSingleton<IAuthorizationHandler, SameCompanyHandler>();
builder.Services.AddAuthorization(opts => opts.AddPolicy("SameCompany", p => p.Requirements.Add(new SameCompanyRequirement())));
  

9. CORS (Cross-Origin Resource Sharing)

What: Controls which origins may call your API from a browser.

Why: Prevents unauthorized websites from using a user’s browser to call your API.

Code (configure CORS)

  
    builder.Services.AddCors(options =>
{
    options.AddPolicy("TrustedFrontend", policy =>
    {
        policy.WithOrigins("https://app.example.com")
              .AllowAnyMethod()
              .AllowAnyHeader()
              .AllowCredentials();
    });
});

var app = builder.Build();
app.UseCors("TrustedFrontend");
  

10. Rate Limiting & Throttling

What: Controls request rates per client/IP to prevent abuse and DDoS.

Why: Protects backend resources and ensures fairness.

Code (using built-in ASP.NET Core rate limiting - .NET 7+)

  
    using System.Threading.RateLimiting;

builder.Services.AddRateLimiter(options =>
{
    options.AddPolicy("FixedWindow", context =>
        RateLimitPartition.GetIpPolicy(context.Connection.RemoteIpAddress, ip => new FixedWindowRateLimiterOptions
        {
            PermitLimit = 100,
            Window = TimeSpan.FromMinutes(1),
            QueueLimit = 0,
            AutoReplenishment = true
        }));
});

var app = builder.Build();
app.UseRateLimiter();
app.MapGet("/", () => "Hello")
   .RequireRateLimiting("FixedWindow");

app.Run();
  

(You can also use API gateways or reverse proxies to apply broader rate-limiting rules.)

Quick Addendum — Other Important Practices

  • Transport security (HTTPS/TLS) — Always serve APIs over HTTPS. Configure HSTS.

  • Secrets management — Use Azure Key Vault / AWS Secrets Manager or environment variables, never hard-code secrets.

  • Input validation & sanitization — Prevent SQL injection, XSS, etc. Use parameterized queries and validators.

  • Logging & monitoring — Instrument auth failures, token errors, and suspicious traffic (App Insights, ELK).

  • Short-lived tokens + refresh tokens — Use refresh tokens securely to renew access tokens.

Conclusion & Suggested Structure for Your Article

You can present the article like this:

  1. Intro: Why layered security matters.

  2. Short summary table of the 10 techniques.

  3. One section per technique (what, why, pros/cons, code snippet).

  4. Best practices & checklist.

  5. Example architecture diagram (Identity server + API gateway + microservices).

  6. Conclusion + further reading links (IdentityServer, OAuth2 spec, JWT RFC).