Introduction
I was trying to implement JWT Auth in the Web API in my Angular 2 client-side application. But while searching on the internet, I could not find a simple solution. Finally, I learned and implemented the process successfully. Here, I am sharing the steps involved in solving this problem. This will help you and will definitely save your time too.
Source code.
Prerequisites
- Web API in ASP.NET Core with JWT Authentication Project solution .
- Angular2/4 for a client-side application.
See the project stucture below.
Step 1 - Create ASP.NET Core Web API Project
- Open Visual Studio 2017 and go to File >> New >> Project
- Select the project template.
- Right click the Solution Explorer and select Add -> New Project->Class Librabry.
Fitness.JWT.API Project
I would like to explain the highlighted part of the project source code for enabling JWT Authentication.
Using the code
Blocks of code should look like this.
startup.cs
Configuring secret key, allowing cross-origin, and applying User policy authentication.
-
- public IConfigurationRoot Configuration { get; }
-
- private const string SecretKey = "ABCneedtogetthisfromenvironmentXYZ";
- private readonly SymmetricSecurityKey _signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey));
-
- public void ConfigureServices(IServiceCollection services)
- {
-
-
-
-
- services.AddCors(options =>
- {
- options.AddPolicy("CorsPolicy",
- builder => builder.AllowAnyOrigin()
- .AllowAnyMethod()
- .AllowAnyHeader()
- .AllowCredentials());
- });
-
- services.AddOptions();
-
-
-
- services.AddMvc(config =>
- {
- var policy = new AuthorizationPolicyBuilder()
- .RequireAuthenticatedUser()
- .Build();
- config.Filters.Add(new AuthorizeFilter(policy));
- });
-
-
- services.AddAuthorization(options =>
- {
- options.AddPolicy("FitnessJWT",
- policy => policy.RequireClaim("FitnessJWT", "FitnessUser"));
- });
-
-
- var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));
-
-
- services.Configure<JwtIssuerOptions>(options =>
- {
- options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
- options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
- options.SigningCredentials = new SigningCredentials(_signingKey, SecurityAlgorithms.HmacSha256);
- });
- }
-
JwtIssuerOptions.cs
This is the class file which is responsible to create Auth unique ticket on the server.
-
- public class JwtIssuerOptions
- {
-
-
-
-
-
-
-
- public string Issuer { get; set; }
-
-
-
-
-
-
-
-
-
-
-
- public string Subject { get; set; }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public string Audience { get; set; }
-
-
-
-
-
-
-
-
-
-
-
- public DateTime NotBefore => DateTime.UtcNow;
-
-
-
-
-
-
-
-
- public DateTime IssuedAt => DateTime.UtcNow;
-
-
-
-
- public TimeSpan ValidFor { get; set; } = TimeSpan.FromMinutes(1);
-
-
-
-
-
-
-
-
-
-
-
- public DateTime Expiration => IssuedAt.Add(ValidFor);
-
-
-
-
-
-
-
-
-
-
-
-
- public Func<Task<string>> JtiGenerator =>
- () => Task.FromResult(Guid.NewGuid().ToString());
-
-
-
-
- public SigningCredentials SigningCredentials { get; set; }
- }
-
JwtController.cs
it is a controller where the anonymous users will login and which creates the JWT security token, encodes it, and sends back to the client as a response with policy.
identity.FindFirst("FitnessJWT")
Look into the below code.
- [HttpPost]
- [AllowAnonymous]
- public async Task<IActionResult> Get([FromBody] ApplicationUser applicationUser)
- {
- var identity = await GetClaimsIdentity(applicationUser);
- if (identity == null)
- {
- _logger.LogInformation($"Invalid username ({applicationUser.UserName}) or password ({applicationUser.Password})");
- return BadRequest("Invalid credentials");
- }
-
- var claims = new[]
- {
- new Claim(JwtRegisteredClaimNames.Sub, applicationUser.UserName),
- new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()),
- new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64),
- identity.FindFirst("FitnessJWT")
- };
-
-
- var jwt = new JwtSecurityToken(
- issuer: _jwtOptions.Issuer,
- audience: _jwtOptions.Audience,
- claims: claims,
- notBefore: _jwtOptions.NotBefore,
- expires: _jwtOptions.Expiration,
- signingCredentials: _jwtOptions.SigningCredentials);
-
- var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
-
-
- var response = new
- {
- access_token = encodedJwt,
- expires_in = (int)_jwtOptions.ValidFor.TotalSeconds,
- State=1,
- expire_datetime= _jwtOptions.IssuedAt
- };
-
- var json = JsonConvert.SerializeObject(response, _serializerSettings);
- return new OkObjectResult(json);
- }
JwtAuthTestController.cs
This is the controller where I have defined the policy [Authorize(Policy = "FitnessJWT")]. So, when a user requsets to the Controller, then it matches the policy and secret key. Then only the response is returned to the client.
- [HttpGet("[action]")]
- [Authorize(Policy = "FitnessJWT")]
- public IActionResult WeatherForecasts()
- {
- var rng = new Random();
-
- List<WeatherForecast> lstWeatherForeCast = new List<WeatherForecast>();
- for (int i = 0; i < 5; i++)
- {
- WeatherForecast obj = new WeatherForecast();
- obj.DateFormatted = DateTime.Now.AddDays(i).ToString("d");
- obj.TemperatureC = rng.Next(-20, 55);
- obj.Summary = Summaries[rng.Next(Summaries.Length)];
- lstWeatherForeCast.Add(obj);
- }
-
- var response = new
- {
- access_token = lstWeatherForeCast,
- State = 1
- };
-
- var json = JsonConvert.SerializeObject(response, _serializerSettings);
- return new OkObjectResult(json);
-
- }
Step 2 - Angular2/4 for Client side application.
I would like to add one note that I have not focused on the UI part too much but I have tried to implement JWT Auth from Angular 2/4 Application.
Fitness.App.UI Solution
login.component.ts
This is the login module with TypeScript where we authenticate a user by passing Username and password.
- import { Component } from '@angular/core';
- import { Router } from '@angular/router';
- import { AuthService } from "../../../app/services/auth.service";
- import { LoginModel } from "../../model/login.model";
- @Component({
- selector: 'Fitness-Login',
- templateUrl: './login.component.html',
- styleUrls: ['./login.component.css'],
- providers: [AuthService]
- })
- export class LoginComponent {
- loginModel = new LoginModel();
- constructor(private router: Router, private authService: AuthService) {
- }
- login() {
- this.authService.login(this.loginModel.userName, this.loginModel.password)
- .then(result => {
- if (result.State == 1) {
- this.router.navigate(["/nav-menu"]);
- }
- else {
- alert(result.access_token);
- }
- });
- }
- }
auth.service.ts
Authentication service validates the credentials and redirects to the homepage.
- login(userName: string, password: string): Promise<ResponseResult> {
- let data = {
- "userName": userName,
- "password": password
- }
- let headers = new Headers({ 'Content-Type': 'application/json' });
- let applicationUser = JSON.stringify(data);
- let options = new RequestOptions({ headers: headers });
-
- if (this.checkLogin()) {
- return this.authPost(this.localUrl + '/api/Jwt', applicationUser, options);
- }
- else {
- return this.http.post(this.localUrl + '/api/Jwt', applicationUser, options).toPromise()
- .then(
- response => {
- let result = response.json() as ResponseResult;
- if (result.State == 1) {
- let json = result.access_token as any;
- localStorage.setItem(this.tokeyKey, json);
- localStorage.setItem(this.tokeyExpKey, result.expire_datetime);
- this.sg['isUserExist'] = true;
- }
- return result;
- }
- )
- .catch(this.handleError);
- }
- }
app.module.client.ts
{ provide: 'ORIGIN_URL', useValue: 'http://localhost:57323' }, path on the JWT WEB API.
You need to change the localhost API based on your machine URL.
- @NgModule({
- bootstrap: sharedConfig.bootstrap,
- declarations: sharedConfig.declarations,
- imports: [
- BrowserModule,
- FormsModule,
- HttpModule,
- ...sharedConfig.imports
- ],
- providers: [
-
- { provide: 'ORIGIN_URL', useValue: 'http://localhost:57323' },
- AuthService, AuthGuard, SimpleGlobal
- ]
- })
- export class AppModule {
- }
To run the application, you need to set he project as below.
Run the solution with multiple startup projects. Then, in the browser, both - the client app and the Web API Service will start in two tabs.
The output of the application is given below.
User Name: Test
Password - Test
Then, it will redirect you to the nav menu page as below.