Securing ASP.NET Core APIs with IdentityServer
This tutorial demonstrates how to secure your ASP.NET Core Web APIs using IdentityServer, a popular open-source framework for authentication and authorization in .NET.
1. Prerequisites
- Visual Studio 2022 or later
- .NET 6 SDK or later
- Basic understanding of ASP.NET Core Web APIs
- Familiarity with OAuth 2.0 and OpenID Connect concepts (recommended)
2. Setting up IdentityServer
Ensure you have followed the setup steps in the IdentityServer Setup tutorial.
3. Protecting API Endpoints
To protect an API endpoint, you need to configure your ASP.NET Core API project to require authentication. This is typically done using the [Authorize] attribute.
3.1. Applying the [Authorize] Attribute
Add the [Authorize] attribute to your controller or specific action methods.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace MyApi.Controllers
{
[ApiController]
[Route("[controller]")]
[Authorize] // This attribute protects the entire controller
public class DataController : ControllerBase
{
[HttpGet]
public IActionResult GetSecureData()
{
// Accessing claims of the authenticated user
var userId = User.FindFirst("sub")?.Value;
var userName = User.Identity?.Name;
return Ok(new { Message = $"Hello, {userName}! Your ID is {userId}. This is secure data." });
}
[HttpGet("public")]
[AllowAnonymous] // This endpoint is publicly accessible
public IActionResult GetPublicData()
{
return Ok(new { Message = "This is public data, no authentication required." });
}
}
}
4. Configuring API Scopes in IdentityServer
IdentityServer uses scopes to define permissions for your APIs. You need to configure these scopes in your IdentityServer project's Config.cs or equivalent.
using IdentityServer4.Models;
using System.Collections.Generic;
public static class Config
{
public static IEnumerable<IdentityServer4.Models.ApiScope> ApiScopes =>
new List<ApiScope>
{
new ApiScope("myapi.read", "Read access to My API"),
new ApiScope("myapi.write", "Write access to My API")
};
public static IEnumerable<IdentityServer4.Models.ApiResource> ApiResources =>
new List<ApiResource>
{
new ApiResource("myapi", "My Application API")
{
Scopes = { "myapi.read", "myapi.write" }
}
};
// ... other configurations (Clients, Identity Resources)
}
5. Configuring API Client in IdentityServer
Clients are applications that request tokens from IdentityServer. Your ASP.NET Core API itself can act as a client, especially if it needs to call other secured services.
using IdentityServer4.Models;
using System.Collections.Generic;
public static class Config
{
// ... ApiScopes and ApiResources
public static IEnumerable<Client> Clients =>
new List<Client>
{
new Client
{
ClientId = "api_client",
ClientName = "API Client",
AllowedGrantTypes = GrantTypes.ClientCredentials, // For machine-to-machine communication
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedScopes = { "myapi.read" }
}
// ... other clients
};
// ... Identity Resources
}
6. Configuring the ASP.NET Core API to Validate Tokens
Your API needs to be configured to trust IdentityServer and validate incoming JWT tokens.
6.1. Add NuGet Packages
Install the following NuGet packages in your API project:
Microsoft.AspNetCore.Authentication.JwtBearer
6.2. Configure Authentication in Program.cs (or Startup.cs)
// Program.cs (for .NET 6+)
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// IdentityServer Configuration
var identityServerUri = builder.Configuration["IdentityServer:Authority"]; // e.g., "https://localhost:5001"
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = identityServerUri; // URL of your IdentityServer
options.Audience = "myapi"; // Audience defined in ApiResource
options.RequireHttpsMetadata = false; // Set to true for production
});
// Authorization
builder.Services.AddAuthorization(); // Enables [Authorize] attribute
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
// Authentication Middleware
app.UseAuthentication();
// Authorization Middleware
app.UseAuthorization();
app.MapControllers();
app.Run();
Ensure your appsettings.json includes the IdentityServer authority:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"IdentityServer": {
"Authority": "https://localhost:5001" // Replace with your IdentityServer URL
}
}
7. Testing the Secured API
To test, you'll need a client application that can authenticate with IdentityServer and obtain a JWT token. This token should be sent in the Authorization header of your API requests:
Authorization: Bearer [Your_JWT_Token_Here]
options.RequireHttpsMetadata = true; and configure your SSL certificates properly.
8. Advanced Security Concepts
- Scope Validation: Beyond just authentication, you can validate that the token contains the required scopes (e.g.,
myapi.read) for specific operations. - Role-Based Authorization: Integrate ASP.NET Core's role-based authorization with claims issued by IdentityServer.
- Token Validation Parameters: Fine-tune token validation, including issuer validation, lifetime checks, and custom validation logic.
By following these steps, you can effectively secure your ASP.NET Core Web APIs with IdentityServer, providing robust authentication and authorization for your applications.