-
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 processLogin:
- 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 approachCreate two files as mentioned below.
- Models/IdentityUserModel.cs
- Models/DatabaseContext.cs
Step 6: Configure IdentityUserModel as belowusing Microsoft.AspNetCore.Identity; using System.Collections.Generic; public class IdentityUserModel: IdentityUser { public ICollection<ApplicationUserRole> UserRoles { get; set; } } public class ApplicationUserRole : IdentityUserRole<string> { 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.csusing 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 ScriptRun 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 APIRun 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...