v3.0
$ cd ../blog

.Net core web API Login and Registration using Identity

July 29, 2023 sajad shafi
.NetC#JWTMSSQL

Introduction

Creating a login and registration system in a .NET Core 7 web API involves several steps. In this article, we will cover the necessary steps to create a basic login and registration system using Identity, the built-in authentication and authorization framework for .NET Core.

Basic setup

Create a new .NET Core web API project. You can do this by opening a command prompt and running the command dotnet new webapi -n [project name] or you can create new webapi project in Visual studio by clicking on the file menu then click on new and select project. Then you can find template ASP.NET core web API:

ASP NET Core web API!

select ASP.NET Core Web API and click next.

Now in the next window type the name of your application and click next: Type name of your application!

Then select .NET core version and click Create: Select version of the application!

Now the application is ready it can be tested with running it from the visual studio.

Installing dependencies

Next, we need to add some nuget packages that are listed as:

the packages can be added by running the commands dotnet add package [package_name] or if you are using visual studio you can add using Nuget package manager. If an error occurs during installation of the packages check the nuget gallery for the right version that has to be installed.

Database connectivity

Next, create a database in the MsSQL with the following query:

Code
create database test_app;

Now define a section for ConnectionStrings in appsettings.json file:

Code
"ConnectionStrings": {
    "DefaultConnection": "Data Source=your_server_name;Initial Catalog=your_database_name;Encrypt=False;Persist Security Info=True;User ID=your_server_user_name;Password=your_sqlserver_password;"
  },

change the your_server_name, your_database_name, your_server_user_name, and your_sqlserver_password to the credentials of your own.

Defining models

After the nuget packages are installed successfully, we can now define models for Users and UserRoles. This step is only needed if you want customized user details, but if you want to use only the properties that are already defined in the Identity then no need to write these models: In this case we want to use both the properties of our own and the properties that are inbuild in identity, for that we need to inherit from the IdentityUser, SystemRole, and SystemUserRole: e.g: Create a class for the User and UserRoles:

Code
public class SystemUser : IdentityUser {
   public string Name { get; set; }
   public string Phone { get; set; }
   public string Address { get ; set }
}

Define DbContext

Create a class for the Database context as:

Code
public class DbContext : IdentityDbContext<SystemUser, IdentityRole, string,
   IdentityUserClaim<string>, IdentityUserLogin<string>,
   IdentityUserToken<string>> {

    public DbContext(DbContextOptions<DbContext> dbOptions):base(dbOptions) {}

    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        base.OnModelCreating(modelBuilder);
    }
}

Instead of passing IdentityUser I have passed our own model SystemUser and the other models come from Identity like, IdentityUserClaim, IdentityRoleClaim, etc.

Configure database connectivity in Services

To configure database with DbContext that we just created. In the Program.cs add the database to builder.Services:

Code
builder.Services.AddDbContext<DbContext>(
    options =>
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));

Now lets create Login and Register functionality by creating 2 folders for Interfaces and their corresponding classes. I name them as IServices and Services but name them as you want. Inside the IService, create an file IAuthService.cs for IAuthService interface as:

Code
public interface IAuthService {
    Task<Response<IdentityResult>> RegisterSystemUser(SystemRegisterUserDTO user);
    Task<Response<LoginResponseDTO>> LoginSystemUser(SystemSignInUserDTO credentials);
}

Now create an AuthService.cs file for AuthService class that will implement IAuthService interface.

Code
public class AuthService : IAuthService {
    private readonly SignInManager<SystemUser> _signInManager;
    private readonly UserManager<SystemUser> _userManager;

    public AuthService(SignInManager<SystemUser> signInManager,
        UserManager<SystemUser> userManager) {
        _signInManager = signInManager;
        _userManager = userManager;
    }
    public async Task<Response<LoginResponseDTO>> LoginSystemUser(SystemSignInUserDTO credentials) {
        var user = await _userManager.FindByEmailAsync(credentials.Email);
        if (user == null) {
            return new() {
                Success = false,
                Data = new LoginResponseDTO() { },
                Message = "Email or password is incorrect",
            };
        }
        var result = await _signInManager.PasswordSignInAsync(user.UserName, credentials.Password, false, true);
        if(!result.Succeeded) {
            return new() {
                Success = false,
                Data = new LoginResponseDTO() { },
                Message = "Email or password is incorrect",
            };
        }
        return new Response<LoginResponseDTO>() {
            Message = "Login Successfull!",
            Data = new() {
                Id=user.Id,
                Username = user.UserName,
                Name = user.Name,
                Email = user.Email,
                Phone = user.Phone,
                Address = user.Address,
            },
        };
    }

    public async Task<Response<IdentityResult>> RegisterSystemUser(SystemRegisterUserDTO user) {
        SystemUser _user = new() {
            UserName = user.Username,
            Email = user.Email,
            Name= user.Name,
        };
        var result = await _userManager.CreateAsync(_user, user.Password);
        return new Response<IdentityResult>() {
            Success = true,
            Data = result,
            Message = result.Succeeded ? "User Registration Successfull!" : "User Registration Failed!"
        };
    }
}

Jwt Authentication

The article jwt authentication with C# contains a complete description about Configuring JWT with .NET core 7 we can use that same concept here if we want to use JWT authentication with this login function. You will find the function in that mentioned article:

C#
public string GenerateJWTToken(SystemUser user) {
    var claims = new List<Claim> {
        new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
        new Claim(ClaimTypes.Name, user.Name),
    };
    var jwtToken = new JwtSecurityToken(
        claims: claims,
        notBefore: DateTime.UtcNow,
        expires: DateTime.UtcNow.AddDays(30),
        signingCredentials: new SigningCredentials(
            new SymmetricSecurityKey(
               Encoding.UTF8.GetBytes(configuration["ApplicationSettings:JWT_Secret"])
                ),
            SecurityAlgorithms.HmacSha256Signature)
        );
    return new JwtSecurityTokenHandler().WriteToken(jwtToken);
}

If you want to use JWT token then the Login function will look like this.

Code
public async Task<Response<LoginResponseDTO>> LoginSystemUser(SystemSignInUserDTO credentials) {
    var user = await _userManager.FindByEmailAsync(credentials.Email);
    if (user == null) {
        return new() {
            Success = false,
            Data = new LoginResponseDTO() { },
            Message = "Email or password is incorrect",
        };
    }
    var result = await _signInManager.PasswordSignInAsync(user.UserName, credentials.Password, false, true);
    if(!result.Succeeded) {
        return new() {
            Success = false,
            Data = new LoginResponseDTO() { },
            Message = "Email or password is incorrect",
        };
    }
    var token = _authUtils.GenerateJWTToken(user);
    return new Response<LoginResponseDTO>() {
        Message = "Login Successfull!",
        Data = new() {
            Id=user.Id,
            Username = user.UserName,
            Name = user.Name,
            Email = user.Email,
            Phone = user.Phone,
            Address = user.Address,
            Token = token
        },
    };
}

Configure Auth Dependency

Next add IAuthService and AuthService to service container in Program.cs

Code
builder.Services.AddTransient<IAuthService, AuthService>();

Now, inside the controllers folder create a new file AuthController.cs for Login and Register APIs: Create a Login API as below:

Code
[HttpPost, Route("system-login")]
public async Task<IActionResult> SignIn(SystemSignInUserDTO credentials) =>
   Ok(await _authService.LoginSystemUser(credentials));

Create a Register API as below:

Code
[HttpPost, Route("system-register")]
public async Task<IActionResult> SignUp(SystemRegisterUserDTO userDetails) =>
   Ok(await _authService.RegisterSystemUser(userDetails));

Here is the full AuthController:

Code
[Route("api/v1/auth")]
[ApiController]
public class AuthController : ControllerBase {
  private readonly IAuthenticationService _authService;
  public AuthController(IAuthenticationService authService) {
      _authService = authService;
  }

  [HttpPost, Route("system-login")]
  public async Task<IActionResult> SignIn(SystemSignInUserDTO credentials) =>
      Ok(await _authService.LoginSystemUser(credentials));

  [HttpPost, Route("system-register")]
  public async Task<IActionResult> SignUp(SystemRegisterUserDTO userDetails) =>
      Ok(await _authService.RegisterSystemUser(userDetails));
}

Finally, run the app and test the APIs using swagger.

Conclusion

This is a basic example of how to create a login and registration system using Identity in .NET Core 7 web API, read another article jwt authentication with C# that gives a complete description of how to configure JWT Authentication and you can configure this login and the JWT auth in that article together as described in - jwt-authentication section.

NORMAL blog/dotnet-webapi-user-registration-login.svelte main