Azure AD Auth with MSAL in Angular and ASP.NET Core API
Learn how to implement Azure AD authentication in your Angular app using MSAL and connect it securely with ASP.NET Core Web API. Step-by-step setup included.
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.
MIcrosoft.AspNetCore.All
Microsoft.EntityFrameWorkCore
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Login with user name and password
Authenticate the user name and password.
If login successful return with authentication token if not throw error.
Create two files as mentioned below.
Models/IdentityUserModel.cs
Models/DatabaseContext.cs
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.
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();
});
}
}
using Microsoft.EntityFrameworkCore;
using [projectName].Models;
string connectionString = Configuration.GetConnectionString("DatabaseConnectionString ");
services.AddDbContext<DatabaseContext>(opts => opts.UseSqlServer(connectionString));
services.AddIdentity<IdentityUserModel, ApplicationRole>()
.AddEntityFrameworkStores<DatabaseContext>()
.AddDefaultTokenProviders();
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
};
});
"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
app.UseAuthentication();
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.
To configure Login endpoints create below files
ViewModels/LoginViewModel.cs
Helper/Response.cs
Controllers/AuthController.cs
public class LoginViewModel
{
public string Email { get; set; }
public string Password { get; set; }
}
public class Response
{
public bool success { get; set; }
public string message { get; set; }
}
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
[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);
}
}
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
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')
Run the application, login with valid user name and password which returns JWT token if not throws error. Click on Try it out button
[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
Learn how to implement Azure AD authentication in your Angular app using MSAL and connect it securely with ASP.NET Core Web API. Step-by-step setup included.
Learn how to integrate Okta authentication in Angular using OAuth 2.0 and OIDC. Step-by-step guide for secure login, token handling, and route protection.angular
Step-by-step guide to implement Google OAuth2 authentication in ASP.NET Core. Secure your app with Google sign-in, token validation, and user claims.
Learn how to implement Facebook OAuth2 login in ASP.NET Core. Step-by-step guide to configure app ID, secret, and secure authentication with user claims.
Continue building secure apps with JWT in Angular 8 and ASP.NET Core. Learn about token refresh, role-based access, and protecting Angular routes in Part 2.
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.