ASP.NET Hosting

ASP.NET Core Identity using JWT Authentication – Detailed Practical Manual

Authentication is one of the most important features in any application. Users need to register, login, and access resources securely.

ASP.NET Core Identity provides a ready-made solution for:

  • User Management
  • Password Hashing
  • Role Management
  • Claims Management
  • Authorization
  • Security Features

Project Structure

A simple project structure:

DotNetIdentity
│
├── Controllers
│   └── UserController.cs
│
├── Data
│   ├── TestDbContext.cs
│   └── Entity
│       └── ApplicationUser.cs
│
├── Model
│   ├── JWTSettings.cs
│   └── DTO
│       └── UserDto.cs
│
├── Program.cs
│
└── appsettings.json

Required NuGet Packages

Install the following packages.

Identity Package

Install-Package Microsoft.AspNetCore.Identity.EntityFrameworkCore

Purpose:

  • IdentityUser
  • IdentityRole
  • UserManager
  • RoleManager
  • IdentityDbContext

Entity Framework Core SQL Server

Install-Package Microsoft.EntityFrameworkCore.SqlServer

Purpose:

  • SQL Server connectivity

EF Core Migration Tools

Install-Package Microsoft.EntityFrameworkCore.Tools

Purpose:

  • Add-Migration
  • Update-Database

JWT Authentication Package

Install-Package Microsoft.AspNetCore.Authentication.JwtBearer

Purpose:

  • JWT Validation
  • Bearer Authentication

Swagger Package

Install-Package Swashbuckle.AspNetCore

Purpose:

  • API Documentation
  • JWT Testing

Step 1: Create JWT Settings Model

File Location

Model/JWTSettings.cs

Code

namespace DotNetIdentity.Model
{
    public class JWTSettings
    {
        public string Issuer { get; set; } = string.Empty;
        public string Audience { get; set; } = string.Empty;
        public string SecretKey { get; set; } = string.Empty;
        public int ExpireInMinutes { get; set; }
    }
}

Purpose

This class stores JWT configuration values from appsettings.json.

Step 2: Configure appsettings.json

File Location

appsettings.json

Code

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=YourMSSqlServerName;Initial Catalog=TestDbIdentity;Integrated Security=true;TrustServerCertificate=true;"
  },

  "JWTSettings": {
    "Issuer": "DemoApp",
    "Audience": "UserApp",
    "SecretKey": "#@493JDSSWOP()Y*&^&%^R$%R^&*@@#!@",
    "ExpireInMinutes": 20
  }
}

Purpose

Stores:

  • Database Connection String
  • JWT Secret Key
  • JWT Issuer
  • JWT Audience
  • Token Expiry Time

Step 3: Create Custom User

File Location

Data/Entity/ApplicationUser.cs

Code

using Microsoft.AspNetCore.Identity;

public class ApplicationUser : IdentityUser
{
    public string FirstName { get; set; } = string.Empty;

    public string LastName { get; set; } = string.Empty;

    public string Address { get; set; } = string.Empty;
}

Why Create ApplicationUser?

IdentityUser already contains:

  • Id
  • Email
  • UserName
  • PasswordHash
  • PhoneNumber

ApplicationUser allows us to add extra fields like:

  • FirstName
  • LastName
  • Address

Step 4: Configure Identity in Program.cs

File Location

Program.cs

Identity Registration

builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
    options.Password.RequireLowercase = false;
    options.Password.RequireUppercase = false;
    options.Password.RequireDigit = false;
})
.AddEntityFrameworkStores<TestDbContext>();

Purpose

Registers:

  • UserManager
  • RoleManager
  • Identity Services

Configures password rules.

Step 5: Configure Database

Program.cs

builder.Services.AddDbContext<TestDbContext>(options =>
{
    options.UseSqlServer(
        builder.Configuration["ConnectionStrings:DefaultConnection"]);
});

Purpose

Connects Identity to the SQL Server database.

Step 6: Configure JWT Authentication

Program.cs

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme =
        JwtBearerDefaults.AuthenticationScheme;

    options.DefaultChallengeScheme =
        JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.TokenValidationParameters =
        new TokenValidationParameters()
        {
            ValidateIssuer = true,
            ValidIssuer = settings.Issuer,

            ValidateAudience = true,
            ValidAudience = settings.Audience,

            ValidateIssuerSigningKey = true,

            IssuerSigningKey =
                new SymmetricSecurityKey(
                    Encoding.UTF8.GetBytes(
                        settings.SecretKey))
        };
});

Purpose

Validates incoming JWT tokens.

Checks:

  • Issuer
  • Audience
  • Secret Key

Step 7: Configure Swagger Authentication

Program.cs

options.AddSecurityDefinition("Bearer",
new OpenApiSecurityScheme
{
    Name = "Authorization",
    Type = SecuritySchemeType.Http,
    Scheme = "bearer",
    BearerFormat = "JWT"
});

Purpose

Adds the Authorize button inside Swagger UI.

Step 8: Create User API

File Location

Controllers/UserController.cs

Create User Endpoint

[HttpPost("create-user")]
public async Task<IActionResult> CreateUser(UserDto userData)
{
    var appUser = new ApplicationUser()
    {
        FirstName = userData.FirstName,
        LastName = userData.LastName,
        Email = userData.Email,
        PhoneNumber = userData.Phone,
        UserName = userData.Email
    };

    var result =
        await _userManager.CreateAsync(
            appUser,
            userData.Password);

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

    return Ok("User Created");
}

What Happens Here?

  • User sends data.
  • ApplicationUser object is created.
  • Identity hashes the password.
  • User gets stored in the AspNetUsers table.

Step 9: Login API

Login Endpoint

[HttpPost("login-user")]
public async Task<IActionResult>
LoginUser(string email, string password)
{
    var user =
        await _userManager.FindByEmailAsync(email);

    if (user == null)
        return BadRequest();

    var result =
        await _userManager
        .CheckPasswordAsync(user, password);

    if (!result)
        return BadRequest();

    // Generate JWT
}

What Happens?

  • Email searched in the database.
  • Password checked against PasswordHash.
  • If valid, a JWT token is generated.

Step 10: Generate JWT Token

Login Method

var claims = new List<Claim>()
{
    new Claim(ClaimTypes.Email,email),
    new Claim(ClaimTypes.Role,"admin") //hardcoded 'admin' role here
};

Create Token

var tokenObj = new JwtSecurityToken(
    claims: claims,
    issuer: jwtsettings.Issuer,
    audience: jwtsettings.Audience,
    expires: DateTime.UtcNow.AddMinutes(
        jwtsettings.ExpireInMinutes),

    signingCredentials:
        new SigningCredentials(
            new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(
                    jwtsettings.SecretKey)),
            SecurityAlgorithms.HmacSha256)
);

// Note: The token currently includes a hardcoded role ("admin"), so it will only authorize users as admin.
// In real-world applications, roles should not be hardcoded. Instead, they should be retrieved from the user's
// assigned roles in the database using Identity (e.g., _userManager.GetRolesAsync(user)).

Convert to String

var token =
    new JwtSecurityTokenHandler()
    .WriteToken(tokenObj);

Return Token

return Ok(new
{
    token
});

Step 11: Protect APIs

Admin API

[Authorize(Roles = "admin")]
[HttpPost("test-auth-admin")]
public IActionResult TestAuthAdmin()
{
    return Content(
        "Authentication-admin Api Called");
}

Only users with the Admin role can access.

Manager API

[Authorize(Roles = "manager")]
[HttpPost("test-auth-manager")]
public IActionResult TestAuthManager()
{
    return Content(
        "Authentication-manager Api Called");
}

Only users with the Manager role are allowed.

Step 12: Middleware Pipeline

Program.cs

app.UseAuthentication();

app.UseAuthorization();

Order is important.

Authentication must come before Authorization.

Step 13: Create Database

Run Migration

Add-Migration InitialIdentity

Apply Migration

Update-Database

Identity automatically creates:

  • AspNetUsers
  • AspNetRoles
  • AspNetUserRoles
  • AspNetUserClaims
  • AspNetRoleClaims
  • AspNetUserTokens
  • AspNetUserLogins

Testing Flow

Register User

POST

/api/User/create-user

Creates a new user.

Login User

POST

/api/User/login-user

Returns:

{
  "token": "eyJhbGc..."
}

Swagger Authorization

Click:

Authorize

Enter:

token here...

Now protected APIs can be called.

ASP.NET Core 10.0 Hosting Recommendation

HostForLIFE.eu
HostForLIFE.eu is a popular recommendation that offers various hosting choices. Starting from shared hosting to dedicated servers, you will find options fit for beginners and popular websites. It offers various hosting choices if you want to scale up. Also, you get flexible billing plans where you can choose to purchase a subscription even for one or six months.