
Ravi Kumar
••8 min read
How to Create a Login and Registration API in ASP.NET Core 10 Using JWT Authentication
User authentication is one of the most essential features in any modern application. In this guide, we will walk through the complete process of creating a
Login & Registration API in ASP.NET Core 10, using Entity Framework Core, Password Hashing, and JWT Token Authentication.
- This tutorial covers:
- Creating a new ASP.NET Core 10 Web API project
- Setting up models and DTOs
- Creating authentication controllers
- Implementing JWT token generation
- Connecting to SQL Server
- Adding DbContext and migrations
- Using Service & IService layers for clean architecture
Let’s get started!
1. Create a New ASP.NET Core 10 Web API Project
Open the terminal and run:
dotnet new webapi -n MSIHR
This creates a new API project with Swagger enabled so you can easily test login and registration.
2. Create the Model
Create a new file User.cs inside the Models folder:
public class User
{
public string Username { get; set; }
public string PasswordHash { get; set; } // used to store hashed password
}
The password will not be stored directly. Instead, we will store a hashed version for security.
3. Create the DTO Model
Create a file UserDto.cs:
public class UserDto
{
public string Username { get; set; }
public string Password { get; set; }
}
DTOs are used to receive user input without exposing the database entity directly.
4. Create the Authentication Controller (Simple Version)
Before adding JWT, here is the basic login/register flow:
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private static User? user = new();
[HttpPost("register")]
public ActionResult<User> Register([FromBody] UserDto request)
{
user.Username = request.Username;
user.PasswordHash = new PasswordHasher<User>()
.HashPassword(user, request.Password);
return Ok(user);
}
[HttpPost("login")]
public ActionResult<User?> Login([FromBody] UserDto request)
{
if (user.Username != request.Username)
return BadRequest("User not found.");
if (new PasswordHasher<User>()
.VerifyHashedPassword(user, user.PasswordHash, request.Password)
== PasswordVerificationResult.Failed)
return BadRequest("Wrong password.");
return Ok(user);
}
}
This validates username and password using hashing.
5. Implement JWT Token Authentication
To secure the API, we must generate a JWT token after successful login.
Here is the updated controller using JWT:
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly IConfiguration configuration;
private static User? user = new();
public AuthController(IConfiguration configuration)
{
this.configuration = configuration;
}
[HttpPost("register")]
public ActionResult<User> Register([FromBody] UserDto request)
{
user.Username = request.Username;
user.PasswordHash = new PasswordHasher<User>()
.HashPassword(user, request.Password);
return Ok(user);
}
[HttpPost("login")]
public ActionResult<string> Login([FromBody] UserDto request)
{
if (user.Username != request.Username)
return BadRequest("User not found.");
if(new PasswordHasher<User>()
.VerifyHashedPassword(user, user.PasswordHash,request.Password)
== PasswordVerificationResult.Failed)
return BadRequest("Wrong password.");
string token = CreateToken(user);
return Ok(token);
}
private string CreateToken(User user)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.Username)
};
var key = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(configuration["AppSettings:Token"]!)
);
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512);
var tokenDescriptor = new JwtSecurityToken(
issuer: configuration["AppSettings:Issuer"],
audience: configuration["AppSettings:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddDays(1),
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);
}
}
6. Connect the API to SQL Server Database
DbContext File: MSIHRDbContext.cs
public class MSIHRDbContext : DbContext
{
public MSIHRDbContext(DbContextOptions<MSIHRDbContext> options) : base(options) {}
public DbSet<User> Users { get; set; }
}
Shape
Add Connection String in appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "Server=DESKTOP-7D4K4FQ\\SQLEXPRESS;Database=MSIHR;Integrated Security=True;TrustServerCertificate=True"
},
"AppSettings": {
"Token": "3L2JZYvABNJyLLJ-so0F5sF64kEsilc2hrX4oUNPgdJ5gLc_zMDm9LutF7uNI_Bn",
"Issuer": "Rk",
"Audience": "MyAuthAudience"
}
}
Shape
Program.cs – Configure Database
builder.Services.AddDbContext<MSIHRDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
7. Run Migrations
Open Package Manager Console:
add-migration InitialCreate
update-database
This creates the database and the Users table.
8. Test Using Swagger
Run the API:
dotnet run
Open Swagger and test:
POST → /api/auth/register
POST → /api/auth/login
You will receive a JWT token on successful login.
9. Using IService and Service Layer (Clean Architecture)
To keep the code maintainable, we move the logic into Service and IService layers.
Shape
IAuthService.cs
public interface IAuthService
{
Task<string?> Login(UserDto request);
Task<User> Register(UserDto request);
}
Shape
AuthService.cs
public class AuthService : IAuthService
{
private readonly IConfiguration configuration;
private readonly MSIHRDbContext _context;
public AuthService(IConfiguration configuration, MSIHRDbContext context)
{
this.configuration = configuration;
_context = context;
}
public async Task<User> Register(UserDto request)
{
if (await _context.Users.AnyAsync(u => u.Username == request.Username))
return null;
var user = new User
{
Username = request.Username,
PasswordHash = new PasswordHasher<User>()
.HashPassword(null, request.Password)
};
await _context.Users.AddAsync(user);
await _context.SaveChangesAsync();
return user;
}
public async Task<string?> Login(UserDto request)
{
User? user = await _context.Users.FirstOrDefaultAsync(u => u.Username == request.Username);
if (user == null)
return null;
if (new PasswordHasher<User>()
.VerifyHashedPassword(user, user.PasswordHash, request.Password)
== PasswordVerificationResult.Failed)
return null;
return CreateToken(user);
}
private string CreateToken(User user)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.Username)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["AppSettings:Token"]!));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512);
var tokenDescriptor = new JwtSecurityToken(
issuer: configuration["AppSettings:Issuer"],
audience: configuration["AppSettings:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddDays(1),
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);
}
}
Shape
AuthController Using Service Layer
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly IAuthService _service;
public AuthController(IAuthService service)
{
_service = service;
}
[HttpPost("register")]
public async Task<ActionResult<User?>> Register([FromBody] UserDto request)
{
var user = await _service.Register(request);
if (user == null)
return BadRequest("User already exists.");
return Ok(user);
}
[HttpPost("login")]
public async Task<ActionResult<string>> Login([FromBody] UserDto request)
{
var token = await _service.Login(request);
if (token == null)
return BadRequest("Invalid username or password.");
return Ok(token);
}
}
Final Result
At the end of this tutorial, you now have:
✔ A complete ASP.NET Core 10 Web API
✔ User registration with password hashing
✔ Login with JWT token authentication
✔ Database integration using Entity Framework
✔ Clean architecture using Services & Interfaces
✔ Full testing support through Swagger
This setup is perfect for building real-world applications such as admin panels, dashboards, mobile apps, and more.
R
Ravi Kumar
Technical writer and software development expert at Murmu Software Infotech, sharing insights on modern web development, software architecture, and best practices.

