Table of Contents


How to Implement JWT Authentication using Asp.net Core and Angular 8 – PART 1

Hire our expert Anuglar developer

How to Integrate Identity Server to Angular Application

From the first part1 we are creating asp.net core application which contain all the setting of the JWT authentication at API level and in part 2 we are going to check how to use JWT authentication API in Angular project.

Here we consider that you know the swagger and Asp.net Identity.

Step 1: Create Asp.net Core application using visual studio

Step 2: After Create new application install nuget package of following dlls if not exists

MIcrosoft.AspNetCore.All

Microsoft.EntityFrameWorkCore

Microsoft.EntityFrameworkCore.SqlServer

Microsoft.EntityFrameworkCore.Tools

Step 3: Flow chart explanation of how the JWT authentication work

Step 4: Flow chart process

Login :

Login with user name and password

Authenticate the user name and password.

If login successful return with authentication token if not throw error.

Step 5: Create models for database as here we use code first approach

Create two files as mentioned below.

Models/IdentityUserModel.cs

Models/DatabaseContext.cs

Step 6: Configure IdentityUserModel as below


using Microsoft.AspNetCore.Identity;
using System.Collections.Generic;
public class IdentityUserModel: IdentityUser
{
	public ICollection<ApplicationUserRole> UserRoles { get; set; }
}

public class ApplicationUserRole : IdentityUserRole
{
	public virtual IdentityUserModel User { get; set; }
	public virtual ApplicationRole Role { get; set; }
}

public class ApplicationRole : IdentityRole
{
	public ICollection<ApplicationUserRole> UserRoles { get; set; }
}						
						

If you do not want to use role then remove role related code.

Step 7: Configure DatabaseContext.cs class file.


using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Http;
public class DatabaseContext: IdentityDbContext<IdentityUserModel, ApplicationRole, string, IdentityUserClaim<string>,
ApplicationUserRole, IdentityUserLogin<string>,
IdentityRoleClaim<string>, IdentityUserToken<string>>
{
    private IHttpContextAccessor accessor;
    private IConfiguration configuration;
        
    public DatabaseContext(DbContextOptions options, IHttpContextAccessor _accessor, IConfiguration _configuration)
    : base(options)
    {
        accessor = _accessor;
        configuration = _configuration;
    }
     
    public DbSet<UserModel> UsersInfo { get; set; }
        
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var host = accessor.HttpContext?.Request.Host;
        if (host!=null && host.ToString().IndexOf(configuration["APIUATUrl"]) >= 0)
        {
            optionsBuilder.UseSqlServer(configuration.GetConnectionString("UATConnectionString"));
        }
        else if (host != null && host.ToString().IndexOf(configuration["APIProductionUrl"]) >= 0)
        {
            optionsBuilder.UseSqlServer(configuration.GetConnectionString("ProductionConnectionString"));
        }
    }
 
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<ApplicationUserRole>(userRole =>
        {
            userRole.HasKey(ur => new {ur.UserId, ur.RoleId});

            userRole.HasOne(ur => ur.Role)
                    .WithMany(r => r.UserRoles)
                    .HasForeignKey(ur => ur.RoleId)
                    .IsRequired();
 
            userRole.HasOne(ur => ur.User)
                    .WithMany(r => r.UserRoles)
                    .HasForeignKey(ur => ur.UserId)
                    .IsRequired();
        });
    }
}						
						

Step 8: Configuring Asp.Net Identity in ConfigureServices method of Startup.cs


using Microsoft.EntityFrameworkCore;
using [projectName].Models;						
						

[projectName] - Replace with project namespace


string connectionString = Configuration.GetConnectionString("DatabaseConnectionString ");
services.AddDbContext<DatabaseContext>(opts => opts.UseSqlServer(connectionString));
services.AddIdentity<IdentityUserModel, ApplicationRole>()
.AddEntityFrameworkStores<DatabaseContext>()
.AddDefaultTokenProviders();						
						

Configuring JWT in ConfigureServices method of Startup.cs


using System;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); 
services.AddAuthentication(options =>
		{
			options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
			options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
			options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
			options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
		})
		.AddJwtBearer(cfg =>
		{
			cfg.RequireHttpsMetadata = false;
			cfg.SaveToken = true;
			cfg.TokenValidationParameters = new TokenValidationParameters
			{
				ValidateIssuer = true,
				ValidateAudience = true,
				ValidateLifetime = true,
				ValidateIssuerSigningKey = true, 
				ValidIssuer = Configuration["JwtIssuer"],
				ValidAudience = Configuration["JwtAudience"],
				IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtKey"])),
				ClockSkew = TimeSpan.Zero, 
				SaveSigninToken = true
			};
		});						
						

Connectionstring, Configuration["JwtIssuer"], Configuration["JwtAudience"], Configuration["JwtKey"] are configured in appsettings.json as below


"ConnectionStrings": {
    "DatabaseConnectionString": "Server=[Database_Server];Database=[Database_Name];User ID=[User_Name];Password=[Password]”
}
"APIUrl": "api-url",
"JwtIssuer": "[Issuer_Url]",
"JwtAudience": "[Audience_Url]",
"JwtKey": "secreatekeyComehere",
"JwtExpireHours": 12						
						

Set the values for these

Database_Server

Database_Name

User_Name

Password

Issuer_Url

Audience_Url

JWT key has to be changed to maintain uniqueness

Add below line in configure method of Startup.cs


app.UseAuthentication();						
						

Configure User Model as below in ConfigureService method of Startup.cs


using Microsoft.AspNetCore.Identity;
var builder = services.AddIdentityCore<IdentityUserModel>(o =>
					{
						o.Password.RequireDigit = true;
						o.Password.RequireLowercase = false;
						o.Password.RequireUppercase = true;
						o.Password.RequireNonAlphanumeric = true;
						o.Password.RequiredLength = 6;
						o.User.RequireUniqueEmail = true;
					});
builder = new IdentityBuilder(builder.UserType, typeof(IdentityRole), builder.Services);
builder.AddEntityFrameworkStores<DatabaseContext>().AddDefaultTokenProviders();						
						

Note: Update the settings for password complexity in the above code based on the requirement.

Step 9: Process of JWT Authentication and Token creation.

To configure Login endpoints create below files

ViewModels/LoginViewModel.cs

Helper/Response.cs

Controllers/AuthController.cs

Add LoginViewModel.cs class file


public class LoginViewModel
{
	public string Email { get; set; }
	public string Password { get; set; }
}	
						

Add Response.cs class file


public class Response
{
	public bool success { get; set; }
	public string message { get; set; }
}						
						

Create controller as AuthController.cs with login method as below


using Microsoft.AspNetCore.Identity;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using System.Text;
using [projectName].Models;
using [projectName].ViewModels						
						

[projectName] - Replace with project namespace


[Produces("application/json")]
[Route("api/auth")]
public class AuthController : Controller
{
	private readonly SignInManager<IdentityUserModel> signInManager;
	private readonly UserManager<IdentityUserModel> userManager;
	private readonly IConfiguration configuration;
	public AuthController(UserManager<IdentityUserModel> _userManager, 
	SignInManager<IdentityUserModel> _signInManager,
	IConfiguration _configuration)
	{
		userManager = _userManager;
		signInManager = _signInManager; 
		configuration = _configuration; 
	} 

	[HttpPost]
	[Route("login")]
	public async Task<object> Login([FromBody] LoginViewModel model)
	{
		bool enableLockout = false;
		var user = await userManager.FindByEmailAsync(model.Email);
		if (user != null)
		{
			var result = await signInManager.PasswordSignInAsync(user, model.Password, false, enableLockout);
			if (result.Succeeded)
			{ 
				return await generateJwtToken(model.Email, user);
			}
		} 
		var response = new
		{
			success = false,
			errorMessage = "Login failed"
		};
		return Ok(response);
	} 

	[NonAction]
	private async Task<object> generateJwtToken(string email, IdentityUserModel user)
	{
		var roles = await userManager.GetRolesAsync(user);
		var claims = new List<Claim>
		{
			new Claim(JwtRegisteredClaimNames.Sub, email),
			new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
			new Claim(ClaimTypes.NameIdentifier, user.Id),
			new Claim(ClaimTypes.Role, roles[0])
		}; 
		var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JwtKey"]));
		var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
		var expires = DateTime.UtcNow.AddHours(Convert.ToDouble(configuration["JwtExpireHours"])); 
		var token = new JwtSecurityToken(configuration["JwtIssuer"],
										configuration["JwtAudience"],
										claims,
										expires: expires,
										signingCredentials: creds
										);
		var response = new { auth_token = new JwtSecurityTokenHandler().WriteToken(token),
							 success = true
						   };
		return Ok(response);
	}
}						
						

Step 10: Generate Asp.net Identity table using Migration Script

Run migration commands in Package Manager Console — command to enable migration PM> enable-migrations — command to add migration PM> Add-Migration [Migration_name] — command to update database PM> update-database

— If any changes or mistake in migration, then use the below command to remove migration before updating the database i.e before executing update-database command. PM> remove-migration

Above commands results in creation of Identity tables in database

Note :

No extra configurations or settings are required to create Asp.net Identity tables. Those are taken care by .Net Framework itself.

Add migration will create a file with the all the changes has to be done on the database. Below screen shot is one of the examples of that.

With the migration name a record is created in the __EFMigrationsHistory table

Before testing the login api, User and roles has to be created. Create roles required in the database. Table name AspNetRoles.


INSERT INTO AspNetRoles (Id,[Name],NormalizedName)
Values('6B108034-FAE8-46BD-B1E3-F5A957EDE43E','User','User')						
						

Step 11: Testing login API

Run the application, login with valid user name and password which returns JWT token if not throws error. Click on Try it out button

Enter valid user name and password

Response body is returned with authentication token

Add authorize annotation above the controller method for which authorization requires.


[HttpGet]
[Route("testAuthorize")]
[Authorize]
public async Task<IActionResult> testAuthorize()
{ 
  return Ok(new { success = true, message = 'Success' });
}						
						

From part 2 we can see how to use those api setting in Angular 8

Click here to continue with Part 2...

Ready to Build Something Amazing?

Get in touch with Prishusoft – your trusted partner for custom software development. Whether you need a powerful web application or a sleek mobile app, our expert team is here to turn your ideas into reality.

image