November 22, 2019

JSON Web Token with ASP.NET Web API and Angular 8

JSON Web Token with ASP.NET Web API and Angular 8

What is Json Web Token (JWT)

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object.

In simple terms, we use these tokens, to authenticate and authorize requests which are made over the internet and also to share information in forms of object typically known as Claims.
You can know more in detail about Jwt on there official website.

In this article we are going to implement a simple login page in Angular, which will call an Web API endpoint to authenticate a user. If the provided credentials are authenticated, the server return a JWT token, which the client (i.e. Angular app in our case) uses for further requests involving secure transmission.

Firstly, we are going to implement the back end service which will generate the token for the first request and validate it for any further requests.

Backend with Asp.Net Web API

Step 1: Create an empty Asp.Net Web API Project and add the following Nuget Packages:

  • Microsoft.AspNet.Cors
  • System.IdentityModel.Token.Jwt
  • Microsoft.Owin.Security.OAuth
  • Swashbuckle
Step 2: Create a new class named “Jwt_Authentication”, which will act as the
middle ware to generate the JWT token.Also Read: Create a news app with Angular 8 and Bootstrap

using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;

namespace AngularJwt_Api.Business
{
    public class Jwt_Authentication
    {
        private static string Secret = "ERMN05OPLoDvbTTa/QkqLNMI7cPLguaRyHzyg7n5qNBVjQmtBhz4SzYh4NBVCXi3KJHlSXKP+oi2+bXr6CUYTR==";
        public static string GenerateToken(string username)
        {

            DateTime issuedAt = DateTime.UtcNow;
            DateTime expires = DateTime.UtcNow.AddDays(7);


            var tokenHandler = new JwtSecurityTokenHandler();

            ClaimsIdentity claimsIdentity = new ClaimsIdentity(new[]
            {
                 new Claim(ClaimTypes.Name, "UserName"),
                new Claim(ClaimTypes.Email,"user@yopmail.com"),
                new Claim("DisplayName", "User"),
                new Claim(ClaimTypes.Role, "admin")
            });

            var now = DateTime.UtcNow;
            var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(Secret));
            var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey,
                Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature);


            var token =
                (JwtSecurityToken)
                 tokenHandler.CreateJwtSecurityToken(issuer: "http://localhost:51888", audience: "http://localhost:400",
                    subject: claimsIdentity, notBefore: issuedAt, expires: expires, signingCredentials: signingCredentials);
            var tokenString = tokenHandler.WriteToken(token);

            return tokenString;
        }

    }
}
This method will be called, once the credentials have been verified and the token needs to generated in order to send back to the client.
In the real world scenario, Claims will have the details of the logged in user, and the Issuer and Audience Url’s will be of the server and client application.

(Note, these will be different for you based on the port on which you API and Angular project are hosted)

Step 3: Create a simple model class to store the incoming request from the client. I have created a class named LoginModel.cs having two string properties Username and Password.

Step 4: Now we will be create the Controller which will have the logic to implement the authentication and call the GenerateToken method we created above.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using AngularJwt_Api.Models;
using AngularJwt_Api.Business;
using System.Net.Http;
using System.Net;
using System.Text;
using System.Web.Http.Cors;

namespace AngularJwt_Api.Controllers
{
 
    [EnableCors("*","*","*")]
    public class LoginController : ApiController
    {
        [HttpPost]
        [AllowAnonymous]
        public IHttpActionResult Authenticate([FromBody]LoginModel login)
        {
            bool isUsernamePasswordValid = false;

            if (login != null)
                isUsernamePasswordValid = login.Password == "admin" ? true : false;

            if (isUsernamePasswordValid)
            {
                string token = Jwt_Authentication.GenerateToken(login.Username);
             
                return Ok<string>(token);
            }
            else
            {
                return BadRequest("Login failed, invalid Username or Password.");
            }
        }

        [AuthorizeJwt]
        [HttpGet]
        public HttpResponseMessage GetSecureValues()
        {
            return this.Request.CreateResponse(HttpStatusCode.OK,
                        new { content = "Secure Content Returned" });
        }
    }
}
Here, we have two API endpoint, one which authenticates the user and the other which we will discuss later. The Authenticate method receives the LoginModel from the client and first the credentials are verified. For now we are simply matching the password but practically here we will make a call to the DB and get the credentials verified.

If the user is authenticated, the GenerateToken class is called with Username as parameter which is used in claims. You should pass on the actual User object to generate the claims.

Do remember to EnableCors in your project otherwise your angular application will not be able to communicate with the Api application.

We are all set to test are token generation logic through swagger. If you have not used Swagger before, it is a useful tool which generates documentation for your endpoints and gives you an interface to test them.

It can be easily added to the project using Swashbuckle and then be accessed using /Swagger in the url of your application

Swagger to Generate Json Web Token in Asp.Net Web API

In the second image, we can see that our API return the JWT token in the Response Body. We will now move on to implementing the validation logic for incoming requests with these token.

Step 5: Create a new class names “AuthorizeJwtAttribute.cs”. We will use this class to implement a custom Authorize Attribute to monitor, fetch, decode and validate the token.

using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;

namespace AngularJwt_Api.Business
{
    public class AuthorizeJwtAttribute : AuthorizeAttribute
    {
        private static string Secret = "ERMN05OPLoDvbTTa/QkqLNMI7cPLguaRyHzyg7n5qNBVjQmtBhz4SzYh4NBVCXi3KJHlSXKP+oi2+bXr6CUYTR==";
        public override void OnAuthorization(HttpActionContext actionContext)
        {
            string token;
            if (!TryRetrieveToken(actionContext.Request, out token))
            {
                actionContext.Response =
                                     new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
            }

            try
            {
                var now = DateTime.UtcNow;
                var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(Secret));


                SecurityToken securityToken;
                JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
                TokenValidationParameters validationParameters = new TokenValidationParameters()
                {
                    ValidAudience = "http://localhost:50191",
                    ValidIssuer = "http://localhost:50191",
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    LifetimeValidator = this.LifetimeValidator,
                    IssuerSigningKey = securityKey
                };
                //extract and assign the user of the jwt
                Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken);
                HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken);
                base.OnAuthorization(actionContext);

            }
            catch (SecurityTokenValidationException e)
            {
                actionContext.Response =
                                     new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
            }
            catch (Exception ex)
            {
                actionContext.Response =
                                      new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError);
            }


        }


        private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
        {
            token = null;
            IEnumerable<string> authzHeaders;
            if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1)
            {
                return false;
            }
            var bearerToken = authzHeaders.ElementAt(0);
            token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
            return true;
        }


        public bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
        {
            if (expires != null)
            {
                if (DateTime.UtcNow < expires)
                {
                    return true;
                }
            }
            return false;
        }
    }
}

Here in this class, we first retrive the token using the common Secret which we used while creating the token as well. Then we provide the parameters and use the inbuilt functions to validate the token.

LifetimeValidator function is implemented to check the expiry of our token. We have implemented it to be for 7 days but in real time it is somewhere around 20 mins to 30 mins in most of the applications.

We will now utilize the GetSecureValues function to use the custom attribute just created to limit the endpoint to bearer of our Jwt token.

Json Web Token in Asp.Net Web API implemented

Test the endpoint for our token through Postman, move over to the Auth tab and select the Bearer Token from the Type dropdown. Enter the token and click on Send button.

So, our API is working as expected. We have now generated and validated a Json Web Token in our Web Api project.

Client Application with Angular

Step 6: We will now create a client application using Angular 8 in Visual Studio Code and consume our API.

Follow the image below and create two components login-app and nav-bar, a model named login-model and a service to communicate to our Api named login-service.

Note: You can ignore nav-bar component and all other html code and create a simple form as long as it can take a username and password as input from the user.

Angular project structure to implement Json Web Tokens

login-model.ts

export class LoginModel {
  public  Username:string = "";
 public   Password:string = "";
}

login-app.component.ts

Here we will create a reactive form in angular to take in Username and Password from the users and on click of Submit button call the service class to communicate with the API. We have also implemented basic validation messages in the form.

The console.log method will help us showcase the values we entered and response we get back from the service once we complete the implementation.

Finally we will store the token returned in either browser local storage or session storage. One stored it will be available in the memory from where it can be fetched and added in headers for all further requests.

import { LoginServiceService } from './../login-service.service';
import { Component, OnInit } from '@angular/core';
import {FormBuilder, FormGroup, Validators, FormControl} from '@angular/forms';
import {MatButtonModule} from '@angular/material/button';
import { LoginModel } from '../login-model';


@Component({
  selector: 'app-login-app',
  templateUrl: './login-app.component.html',
  styleUrls: ['./login-app.component.css']
})
export class LoginAppComponent implements OnInit {

  loginForm : FormGroup;
  loginmodel : LoginModel = new LoginModel();
  
  constructor(
    private formBuilder : FormBuilder,
    private loginService : LoginServiceService
  
  ) { }

  ngOnInit() {
    this.loginForm = this.formBuilder.group(
      {
        username : ['',Validators.required],
        password : ['',Validators.required]
      }
    );
  }

  get(key:string) : string
  {
    return this.loginForm.controls[key].value;
  }

  onSubmit() : void{
    console.log(this.loginForm.value);
    console.log(this.get('username'));
    console.log(this.get('password'));

   
    this.loginmodel.Username = this.get('username');
    this.loginmodel.Password = this.get('password');
    
    this.loginService.AuthenticateUser(this.loginmodel)
        .subscribe((response) => {
            console.log(response);
            localStorage.setItem('token', response.toString());
            sessionStorage.setItem('token', response.toString());
        })

  }

}

login-app.component.html

Basic Angular form created using Bootstrap. We have also used a simple Angular Material button for the Submit, but in case you have not worked with them before it is easier and faster to work with default bootstrap buttons for the purpose of this article.

<div class="card col-md-4 login-div offset-md-4">
      
    <h5 class="card-header">Login into the App!</h5>
   <div class="form-div card-body">
    <form [formGroup] = "loginForm" (ngSubmit) = "onSubmit($event)">
        <div class="form-group">
            <input type="text" formControlName= "username" class = "form-control" placeholder="UserName">
            <div class="error has-error alert-danger errorDiv" *ngIf = "loginForm.controls['username'].errors
            && loginForm.controls['username'].touched">
                               
                <div>Username is required</div>
            </div>
         </div>
         <div class="form-group">
            <input type="text" formControlName= "password" class = form-control placeholder="Password">
            <div class="error has-error alert-danger errorDiv" *ngIf = "loginForm.controls['password'].errors
            && loginForm.controls['password'].touched">
                <div>Password is required</div>
            </div>
         </div>
         <div>
         <button mat-raised-button color = 'primary' type="submit">  
            Login
        </button>
            <span style="float: right; margin-top: 12px">Forgot Password</span>
        </div>
    </form>
   </div>
  
</div>

nav-bar.component.ts (Optional )

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-nav-bar',
  templateUrl: './nav-bar.component.html',
  styleUrls: ['./nav-bar.component.css']
})
export class NavBarComponent implements OnInit {

isLoggedIn: boolean

  constructor() { }

  ngOnInit() {
    this.isLoggedIn = false;
  }

}

nav-bar.component.html (Optional )

<nav class="navbar navbar-expand-lg navbar-light" style="background-color: #ffc048">
        <a class="navbar-brand" href="#">Angular Jwt App</a>
       
        <div class="collapse navbar-collapse navList" style="overflow: hidden;">
          <ul class="navbar-nav">
            <li class="nav-item" *ngIf = "!isLoggedIn">
              <a class="nav-link" href="#">Login</a>
            </li>
            <li class="nav-item" *ngIf = "isLoggedIn">
             <a class="nav-link" href="#">Hello</a>
            </li>

            <li class="nav-item mr-auto ml-2 mt-lg-0" style="margin-left: 2%">
              <a class="nav-link" href="#">Logout</a>
            </li>
        </ul>
        
        </div>
      </nav>

    

login-service.service.ts

Here we use the HttpClient module in Angular to call the method in our Api which returns the token if the user is authenticated. This method is subscribed in the login-app.component.ts

import { LoginModel } from './login-model';
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
//import {Response, RequestOptions, Headers } from '@angular/common/http';
import { HttpHeaders } from '@angular/common/http';  
import { Observable } from 'rxjs';  

@Injectable({
  providedIn: 'root'
})
export class LoginServiceService {

  base_url = "http://localhost:51888/api";
  loginModel : LoginModel;
  constructor(private http: HttpClient) { }

  AuthenticateUser(login : LoginModel){
   
    return this.http.post(this.base_url + "/Login",login)
  }

}

app.module.ts

import { LoginServiceService } from './custom-components/login-service.service';
import { LoginModel } from './custom-components/login-model';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
//import { HttpModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginAppComponent } from './custom-components/login-app/login-app.component';
import { NgForm, FormsModule, ReactiveFormsModule } from '@angular/forms';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {MatButtonModule} from '@angular/material/button';
import {MatIconModule} from '@angular/material/icon';
import { NavBarComponent } from './custom-components/nav-bar/nav-bar.component';
import { HttpClientModule } from '@angular/common/http';


@NgModule({
  declarations: [
    AppComponent,
    LoginAppComponent,
    NavBarComponent,
    
      ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule,
    ReactiveFormsModule,
    BrowserAnimationsModule,
    MatButtonModule,
    MatIconModule,
    HttpClientModule   
  ],
  providers: [LoginModel, LoginServiceService],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.html

<app-nav-bar></app-nav-bar>

<app-login-app></app-login-app>

No changes are required in the app.component.ts file.

Angular App to implement Json Web Token with Asp.Net Web Api

We can now use the dummy credentials we have hard coded in our Api to test if our client app work and if the token is saved in the cookie and local storage of the browser

Json Web Token returned from Asp.Net Web Api in Angular
Json Web Token stored in browser storage

Summary

In this post we have successfully implemented Json Web Token based authentication in Asp.Net Web Api using an Angular application as a client. It is the basic skeleton above which you can build any enterprise level application.

3 thoughts on “JSON Web Token with ASP.NET Web API and Angular 8

Leave a Reply

Your email address will not be published. Required fields are marked *