Implementing Authentication in .NET Core API Using JWT and IdentityServer

Implementing Authentication in .NET Core API Using JWT and IdentityServer

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.

  1. Create a new project using Visual Studio or the .NET CLI.
  2. Open the terminal and run the following command to create a new Web API project:
    dotnet new webapi -n SecureApi
  3. Navigate to the project folder:
    cd SecureApi
  4. 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:

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 IEnumerable GetApiScopes()
    {
        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.

  1. Open Postman and make a request to https://localhost:5001/api/myapi.
  2. You’ll need to get a JWT token from IdentityServer. You can do this by sending a request to IdentityServer's token endpoint:
  3. 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
                    
  4. Use the token from the response and add it to the Authorization header in Postman as follows:
  5. Authorization: Bearer {your-jwt-token}
                    
  6. Make the GET request to /api/myapi. If the token is valid, you should get a successful response.

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!

© 2025 Sandeep Mhaske. All rights reserved.

Follow me on LinkedIn | GitHub

Sandip Mhaske

I’m a software developer exploring the depths of .NET, AWS, Angular, React, and digital entrepreneurship. Here, I decode complex problems, share insightful solutions, and navigate the evolving landscape of tech and finance.

Post a Comment

Previous Post Next Post