A step-by-step guide to implementing secure authentication in your .NET Core API with JWT and IdentityServer, featuring role-based access control.
Introduction
Authentication is a fundamental aspect of securing APIs. In this tutorial, we will walk through the implementation of authentication in a .NET Core API using JSON Web Tokens (JWT) and IdentityServer. This will allow us to secure our API endpoints, manage users, and implement role-based authorization.
By the end of this tutorial, you'll have a working API that uses JWTs for authentication, IdentityServer for issuing tokens, and will implement role-based access control to ensure that only authorized users can access specific resources.
Setting Up the Project
Let's start by setting up a new .NET Core Web API project. If you don’t have .NET Core installed, download it from the official site.
- Create a new project using Visual Studio or the .NET CLI.
- Open the terminal and run the following command to create a new Web API project:
dotnet new webapi -n SecureApi
- Navigate to the project folder:
cd SecureApi
- Open the project in Visual Studio or your preferred editor.
Now, we will install the necessary packages for JWT and IdentityServer.
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer dotnet add package IdentityServer4
The following diagram illustrates secure authentication with JWT and Identityserver:

Configuring IdentityServer
IdentityServer is an open-source framework for implementing OpenID Connect and OAuth 2.0 in your .NET Core applications. We’ll configure IdentityServer to issue JWT tokens for our API.
In your project, create a new folder named Config and add a class called IdentityServerConfig.cs to configure the IdentityServer services.
using IdentityServer4.Models; using IdentityServer4.Test; using System.Collections.Generic; public class IdentityServerConfig { public static IEnumerableGetApiScopes() { return new List { new ApiScope("api1", "My API") }; } public static IEnumerable GetApiResources() { return new List { new ApiResource("api1", "My API") }; } public static IEnumerable GetClients() { return new List { new Client { ClientId = "client", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("secret".Sha256()) }, AllowedScopes = { "api1" } } }; } }
This class configures a single API resource, API scope, and a client to access it using client credentials.
In your Startup.cs, configure IdentityServer services as follows:
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddIdentityServer() .AddInMemoryApiScopes(IdentityServerConfig.GetApiScopes()) .AddInMemoryApiResources(IdentityServerConfig.GetApiResources()) .AddInMemoryClients(IdentityServerConfig.GetClients()) .AddTestUsers(new List()) .AddDeveloperSigningCredential(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseIdentityServer(); } }
IdentityServer is now set up to issue JWT tokens. We will use this to secure our API endpoints.
Configuring JWT Authentication in API
Now, let’s configure JWT authentication in our API. In Startup.cs, add the JWT Bearer authentication middleware.
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = "https://localhost:5001"; // IdentityServer URL options.Audience = "api1"; options.RequireHttpsMetadata = false; }); services.AddControllers(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } }
This configuration will enable the API to authenticate incoming requests using JWTs issued by IdentityServer.
Creating Protected API Endpoints
Let’s create a simple API controller with a protected endpoint that requires authentication. In the Controllers folder, add a new controller called MyApiController.cs:
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; [Route("api/[controller]")] [ApiController] public class MyApiController : ControllerBase { [HttpGet] [Authorize] public IActionResult Get() { return Ok(new { message = "This is a protected API endpoint!" }); } }
This endpoint requires an authenticated JWT to access. Any request without a valid JWT will return a 401 Unauthorized error.
Testing the API with Postman
Now that we have our protected endpoint, let’s test it with Postman.
- Open Postman and make a request to
https://localhost:5001/api/myapi
. - You’ll need to get a JWT token from IdentityServer. You can do this by sending a request to IdentityServer's token endpoint:
- Use the token from the response and add it to the Authorization header in Postman as follows:
- Make the GET request to
/api/myapi
. If the token is valid, you should get a successful response.
POST https://localhost:5001/connect/token Content-Type: application/x-www-form-urlencoded Body: grant_type=client_credentials client_id=client client_secret=secret scope=api1
Authorization: Bearer {your-jwt-token}
Implementing Role-Based Authorization
To implement role-based authorization, you can add roles to the JWT claims and configure policies in your API.
[HttpGet] [Authorize(Roles = "Admin")] public IActionResult AdminEndpoint() { return Ok(new { message = "Admin access granted!" }); }
This endpoint will only be accessible to users with the "Admin" role in their JWT claims.
Conclusion
In this tutorial, we’ve learned how to implement JWT-based authentication in a .NET Core API using IdentityServer. We covered setting up IdentityServer, configuring JWT authentication in the API, creating protected endpoints, and implementing role-based authorization. By using JWTs, your API becomes more secure, and you gain fine-grained control over who can access your endpoints.
Keep exploring IdentityServer and JWTs to create secure, scalable APIs for your applications!