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.
![JSON Web Token]()
Step 1 - Create ASP.NET Core Web API Project
- Open Visual Studio 2017 and go to File >> New >> Project
- Select the project template.
![JSON Web Token]()
- Right click the Solution Explorer and select Add -> New Project->Class Librabry.
![JSON Web Token]()
Fitness.JWT.API Project
I would like to explain the highlighted part of the project source code for enabling JWT Authentication.
![JSON Web Token]()
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.
![JSON Web Token]()
The output of the application is given below.
![JSON Web Token]()
![JSON Web Token]()
User Name: Test
Password - Test
Then, it will redirect you to the nav menu page as below.
![JSON Web Token]()