JSON Web Tokens (JWT) are one of the most popular ways to secure APIs. When combined with ASP.NET Core Identity and Entity Framework Core (EF), they provide a powerful and flexible authentication system for modern web applications.

In this article, you’ll learn how to:

🧩 What is JWT (JSON Web Token)?

A JWT is a compact, URL-safe token used to securely transmit information between parties.
It consists of three parts:

  
    header.payload.signature
  

Example JWT

  
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiJ2aXBpbi5taXR0YWxAZ21haWwuY29tIiwiaWF0IjoxNjk2MDE0MzQ3LCJleHAiOjE2OTYwMTc5NDd9.
3nwbOScm9vPh3O4uH0MknhrK5hLbAwT7LM0I9_rM9w4
  

Each part represents:

📘 Reference:
JWT.io — Introduction to JWT

⚙️ Step 1. Create a New ASP.NET Core Web API Project

  
    dotnet new webapi -n JwtAuthApi
cd JwtAuthApi
  

🧱 Step 2. Add Required NuGet Packages

Run these commands:

  
    dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
  

🗃️ Step 3. Configure ApplicationDbContext

Create a new folder Data → ApplicationDbContext.cs :

  
    using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

namespace JwtAuthApi.Data
{
    public class ApplicationDbContext : IdentityDbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options) { }
    }
}
  

👤 Step 4. Register Identity and JWT in Program.cs

  
    using JwtAuthApi.Data;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// 1️⃣ Configure EF Core
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

// 2️⃣ Add Identity
builder.Services.AddIdentity<IdentityUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

// 3️⃣ Configure JWT Authentication
var jwtKey = builder.Configuration["Jwt:Key"];
var jwtIssuer = builder.Configuration["Jwt:Issuer"];

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = jwtIssuer,
        ValidAudience = jwtIssuer,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey))
    };
});

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();
  

🧾 Step 5. Add Configuration in appsettings.json

  
    {
  "ConnectionStrings": {
    "DefaultConnection": "Server=.;Database=JwtAuthDb;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Jwt": {
    "Key": "ThisIsASecretKeyForJwtToken12345",
    "Issuer": "https://localhost:5001"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    }
  },
  "AllowedHosts": "*"
}
  

🔑 Step 6. Create Authentication Controller

Create Controllers/AuthController.cs :

  
    using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace JwtAuthApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthController : ControllerBase
    {
        private readonly UserManager<IdentityUser> _userManager;
        private readonly IConfiguration _config;

        public AuthController(UserManager<IdentityUser> userManager, IConfiguration config)
        {
            _userManager = userManager;
            _config = config;
        }

        [HttpPost("register")]
        public async Task<IActionResult> Register([FromBody] RegisterModel model)
        {
            var user = new IdentityUser { UserName = model.Email, Email = model.Email };
            var result = await _userManager.CreateAsync(user, model.Password);

            if (!result.Succeeded)
                return BadRequest(result.Errors);

            return Ok("User registered successfully");
        }

        [HttpPost("login")]
        public async Task<IActionResult> Login([FromBody] LoginModel model)
        {
            var user = await _userManager.FindByEmailAsync(model.Email);
            if (user != null && await _userManager.CheckPasswordAsync(user, model.Password))
            {
                var authClaims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, user.UserName),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
                };

                var token = GetToken(authClaims);

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

        private JwtSecurityToken GetToken(List<Claim> authClaims)
        {
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            return new JwtSecurityToken(
                issuer: _config["Jwt:Issuer"],
                audience: _config["Jwt:Issuer"],
                expires: DateTime.Now.AddHours(3),
                claims: authClaims,
                signingCredentials: creds
            );
        }
    }

    public class RegisterModel
    {
        public string Email { get; set; } = "";
        public string Password { get; set; } = "";
    }

    public class LoginModel
    {
        public string Email { get; set; } = "";
        public string Password { get; set; } = "";
    }
}
  

🧠 Step 7. Protect an API Endpoint

Create a test controller Controllers/TestController.cs :

  
    using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace JwtAuthApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class TestController : ControllerBase
    {
        [Authorize]
        [HttpGet("secure")]
        public IActionResult SecureEndpoint()
        {
            return Ok("You have accessed a protected endpoint!");
        }

        [AllowAnonymous]
        [HttpGet("public")]
        public IActionResult PublicEndpoint()
        {
            return Ok("Anyone can access this endpoint.");
        }
    }
}
  

Now when you access:

🧪 Step 8. Test Using Swagger or Postman

  1. Register user
    POST → /api/auth/register

          
            { "email": "[email protected]", "password": "Test@123" }
          
        
  2. Login user
    POST → /api/auth/login
    → Copy the returned JWT token.

  3. Authorize

    • In Swagger, click Authorize → paste Bearer {token} .

    • Call /api/test/secure → ✅ Success!

🛡️ Bonus. Role-Based Authorization

You can also decorate your API actions like:

  
    [Authorize(Roles = "Admin")]
public IActionResult AdminOnly()
{
    return Ok("Only admins can access this.");
}
  

To assign roles:

  
    await _userManager.AddToRoleAsync(user, "Admin");
  

🧾 Conclusion

By integrating ASP.NET Core Identity , Entity Framework , and JWT , you can create a modern, secure, and scalable authentication system suitable for web and mobile APIs.

It combines database-backed identity management with stateless token-based access.