Angular JS Token Based Authentication using Asp.net Core Web API 2.0 and JSON Web Token

ASP.NET Core Identity is designed to enable us to easily use a number of different storage providers for our ASP.NET applications. We can use the supplied identity providers that are included with the .NET Framework, or we can implement our own providers.

In this tutorial, we will  build a Token-Based Authentication using ASP.NET Core Identity , ASP.NET Core Web API and Angular JS

With Token-Based Authentication, the client application is not dependent on a specific authentication mechanism. The token is generated by the server and the Web API have some APIs to understand, validate the token and perform the authentication. This approach provides Loose Coupling between client and the Web API.

this toturial is not for beginners, to follow it, you must understand Angular2 and Asp.NET REST Services

2

Securing our web application consists of two scenarios  :  Authentication and Authorization

1.   Authentication identifies the user. So the user must be registered first, using login and password or third party logins like Facebook, Twitter, etc…
     2.  Authorization  talks about  permission for authenticated users
– What is the user (authenticated) allowed to do ?
– What ressources can the user access ?

We have build our back end service using ASP.NET WEB API Core, web api provides an internal authorization server using OWIN MIDDLEWARE

The authorization server and the authentication filter are both called into an OWIN middleware component that handles  OAuth2
3

This article is the third part of a series of 4 articles

  1. Token Based Authentication using Asp.net Core Web Api
  2. Asp.Net Core Web Api Integration testing using EntityFrameworkCore LocalDb and XUnit2
  3. Angular Token Based Authentication using Asp.net Core Web API and JSON Web Token
  4. Angular JS Token based Authentication using Asp.net Core Web API and JSON Web Token

BUILDING WEB API RESSOURCE SERVER AND AUTHORIZATION SERVER

In the first part Token Based Authentication using Asp.net Core Web API,  I talked about how to configure an ASP.NET Web API  Core Token Based Authentication using JWT.  So in this tutorial I will talk about an Angular2 client that connect to the  Web Api Authorization server using a JWT Token

BUILDING ANGULAR2 WEB CLIENT

Create an ASP.NET Empty WebSite  and structure it as follow

Package.config

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="AngularJS.Core" version="1.6.5" targetFramework="net461" />
  <package id="AngularJS.Resource" version="1.6.5" targetFramework="net461" />
  <package id="AngularJS.Route" version="1.6.5" targetFramework="net461" />
  <package id="bootstrap" version="3.3.7" targetFramework="net461" />
  <package id="jQuery" version="1.9.1" targetFramework="net461" />
</packages>

Package.config defines some dependencies , so use nuget package manager  to restore them

nuget

index.html

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <link href="Content/bootstrap.css" rel="stylesheet" />
    <link href="Content/Site.css" rel="stylesheet" />
</head>

<body ng-app="appmodule"
      ng-controller="userCtrl as vm">
    <div>
        <nav class="navbar navbar-default ">
            <div class="container">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    <a class="navbar-brand" href="https://logcorner.com">LogCorner.com</a>
                </div>
                <div class="navbar-collapse collapse">
                    <ul class="nav navbar-nav navbar-right">
                        <li ng-if="vm.isLoggedIn()">
                            <a>Welcome {{ vm.userName }}</a>
                        </li>
                        <li ng-if="vm.isLoggedIn()">
                            <span>
                                <button type="submit"
                                        class="btn btn-default"
                                        ng-click="vm.logout()">
                                    Log Out
                                </button>
                            </span>
                        </li>
                    </ul>
                </div>
                <div>
                </div>
            </div>
        </nav>

        <div class="container">
            <div class="row">
                <div class="jumbotron">
                    <h1>AngularJS Token Authentication </h1>
                    <p>
                        AngularJS Token based Authentication using Asp.net Core Web API and JSON Web Token
                    </p>
                </div>
            </div>
            <div class="row">
                <div ng-if="vm.isLoggedIn() == true"
                     ng-include="'app/product/productListView.html'">
                </div>
                <div ng-if="vm.isLoggedIn() == false"
                     ng-include="'app/user/userLoginView.html'">
                </div>
            </div>
        </div>
    </div>

    <script src="Scripts/angular.js"></script>
    <script src="Scripts/angular-resource.js"></script>
    <script src="app/app.js"></script>
    <script src="app/Common/common.services.js"></script>
    <script src="app/user/userCtrl.js"></script>
    <script src="app/product/productListCtrl.js"></script>
    <script src="app/service/loginservice.js"></script>
    <script src="app/service/productService.js"></script>
    <script src="app/Model/userProfile.js"></script>
</body>
</html>

lets include  *.css files on header section

 <link href="Content/bootstrap.css" rel="stylesheet" />
 <link href="Content/Site.css" rel="stylesheet" />

lets include  *.js files at the end of body  section

<script src="Scripts/angular.js"></script>
<script src="Scripts/angular-resource.js"></script>
<script src="app/app.js"></script>
<script src="app/Common/common.services.js"></script>
<script src="app/user/userCtrl.js"></script>
<script src="app/product/productListCtrl.js"></script>
<script src="app/service/loginservice.js"></script>
<script src="app/service/productService.js"></script>
<script src="app/Model/userProfile.js"></script>

Index.html is the entry point of our SPA application, Use the directive ng-app to auto-bootstrap our AngularJS application.

We can specify an AngularJS module to be used as the root module for the application. This module will be loaded into the $injector when the application is bootstrapped. It should contain the application code needed or have dependencies on other modules that will contain the code.   

Our root angular module is appmodule,  we specify it in index.html by  <body ng-app=”appmodule”…….

app.js

(function () {
    "use strict";

    var app = angular.module("appmodule",
        ["common.services"]);
}());

In app.js file  we inject common.services as a dependency

common.service.js

Lets create a common.service.js file

(function () {
    "use strict";

    angular
        .module("common.services",
        ["ngResource"])
        .constant("appSettings",
        {
            serverPath: "http://localhost:58834"
        });
}());

Here, we implement common functionalities such as error handling, serverPath to call API, (http://localhost:58834/api is the API endpoint)

userCtrl.js

(function () {
    "use strict";
    angular
        .module("appmodule")
        .controller("userCtrl",
        ["$scope", "loginservice", "userProfile",
            userCtrl]);

    function userCtrl($scope, loginservice, userProfile) {
        var vm = this;
        vm.registerErrorData = "";
        vm.loginErrorData = "";
        vm.responseData = "";
        vm.userName = "";
        vm.userEmail = "";
        vm.userPassword = "";
        vm.newUserEmail = "";
        vm.newUserPassword = "";
        vm.confirmUserPassword = "";
        vm.accessToken = "";
        vm.refreshToken = "";

        vm.isLoggedIn = function () {
            var result = userProfile.getProfile().isLoggedIn;
            return result;
        };

        vm.registerUser = function () {
            vm.responseData = '';
            vm.registerErrorData = '';
            vm.loginErrorData = '';
            var userRegistrationInfo = {
                Email: vm.newUserEmail,
                Password: vm.newUserPassword,
                ConfirmPassword: vm.confirmUserPassword
            };

            var registerResult = loginservice.register(userRegistrationInfo);
            registerResult.then(function (data) {
                vm.responseData = "User Registration successful";
                vm.newUserPassword = "";
                vm.confirmUserPassword = "";
            }, function (response) {
                vm.registerErrorData = response.statusText + "\r\n";
                if (response.data) {
                    for (var key in response.data) {
                        vm.registerErrorData += response.data[key] + "\r\n";
                    }
                    if (response.data.exceptionMessage)
                        vm.registerErrorData += response.data.exceptionMessage;
                }
            });
        };

        vm.redirect = function (url) {
            window.location.href = url;
        };

        vm.login = function () {
            var userLogin = {
                grant_type: 'password',
                username: vm.userEmail,
                password: vm.userPassword
            };
            vm.responseData = '';
            vm.registerErrorData = '';
            vm.loginErrorData = '';
            var loginResult = loginservice.login(userLogin);
            loginResult.then(function (resp) {
                var result = resp.data.claims.filter(function (o) {
                    return o.type == 'sub';
                });
                vm.userName = result ? result[0].value : null;
                userProfile.setProfile(resp.data);
            }, function (response) {
                vm.loginErrorData = response.statusText + " : \r\n";
                if (response.data) {
                    for (var key in response.data) {
                        vm.loginErrorData += response.data[key] + "\r\n";
                    }
                }
            });
        };

        vm.logout = function () {
            userProfile.logout();
        };
    }
})();

In userCtrl.js file Ininject login.services and userProfile as a dependencies: userProfile to store user data and login.service to call api

I define some functions :

  • vm.isLoggedIn :  to verify if the user is logged in  :  sessionStorage.getItem(‘accessToken’) != null
  • vm.registerUser : register a new user. it uses loginservice.register
  • vm.login : log in user. it uses loginservice.login
  • vm.logout : logout user. it uses userProfile to clear  user data

in index.html view I reference  userCtrl as  ng-controller=”userCtrl as vm”

So if user is loggedIn I display  userLoginView

 <div ng-if="vm.isLoggedIn() == false"
      ng-include="'app/user/userLoginView.html'">
 </div>

else I display  productListView

 <div ng-if="vm.isLoggedIn() == false"
      ng-include="'app/user/userLoginView.html'">
 </div>

userLoginView.html

<div class="row">
    <div class="col-md-6">
        <div class="panel panel-success">
            <div class="panel-heading center-block">
                <span>REGISTER A NEW USER</span>
            </div>
            <div class="panel-body">
                <form ng-hide="vm.isLoggedIn()" class="form-horizontal" role="form">
                    <div class="form-group">
                        <input type="text"
                               class="form-control"
                               placeholder="username (email)"
                               ng-model="vm.newUserEmail">
                    </div>
                    <div class="form-group">
                        <input type="password"
                               class="form-control col-md-10"
                               placeholder="password"
                               ng-model="vm.newUserPassword">
                    </div>
                    <div class="form-group">
                        <input type="password"
                               class="form-control col-md-10"
                               placeholder="ConfirmPassword"
                               ng-model="vm.confirmUserPassword">
                    </div>
                    <button type="submit"
                            class="btn btn-default"
                            ng-click="vm.registerUser()">
                        Register
                    </button>
                </form>
            </div>
            <div class="panel-footer">
                <div class="alert-danger" ng-show="vm.registerErrorData">{{vm.registerErrorData}}</div>
                <div class="alert-success" ng-show="vm.responseData">{{vm.responseData}}</div>
            </div>
        </div>
    </div>
    <div class="col-md-6">
        <div class="panel panel-success">
            <div class="panel-heading center-block">
                <span>LOGIN IN</span>
            </div>
            <div class="panel-body">
                <form ng-hide="vm.isLoggedIn()" class="form-horizontal" role="form">
                    <div class="form-group">
                        <input type="text"
                               class="form-control col-md-10"
                               placeholder="username (email)"
                               ng-model="vm.userEmail">
                    </div>
                    <div class="form-group">
                        <input type="password"
                               class="form-control col-md-10"
                               placeholder="password"
                               ng-model="vm.userPassword">
                    </div>
                    <button type="submit"
                            class="btn btn-default"
                            ng-click="vm.login()">
                        Login
                    </button>
                </form>
            </div>
            <div class="panel-footer">
                <div class="alert-danger" ng-show="vm.loginErrorData">{{vm.loginErrorData}}</div>
            </div>
        </div>
    </div>
</div>

UserLoginView  defines the login form and the register form and uses userCtrl :

  • ng-click=”vm.registerUser()”  handles  registerUser function of userCtrl.
  • ng-click=”vm.login()”  handles  login function of userCtrl.

 

  <button type="submit" class="btn btn-default"  ng-click="vm.registerUser()">
         Register
  </button>

   ......................

 <button type="submit" class="btn btn-default" ng-click="vm.login()">
         Login
  </button>

loginService.js

(function () {
    "use strict";
    angular
        .module("common.services")
        .factory("loginservice", ["$http", "appSettings",
            loginservice])
    function loginservice($http, appSettings) {
        this.register = function (userInfo) {
            var resp = $http({
                url: appSettings.serverPath + "/api/auth/Register",
                method: "POST",
                data: userInfo,
            });
            return resp;
        };
        this.login = function (userlogin) {
            var contentHeaders = [{ 'Content-Type': 'application/json' },
            { 'Accept': 'application/json' },
            { 'Content-Type': 'application/x-www-form-urlencoded' }
            ]

            var credentials = {
                grant_type: 'password',
                email: userlogin.username,
                password: userlogin.password
            };

            var resp = $http({
                url: appSettings.serverPath + "/api/auth/token",
                method: "POST",
                data: credentials,
                headers: contentHeaders,
            });
            return resp;
        };
        return {
            register: this.register,
            login: this.login
        }
    }
})();
  • register function :  register a new to database
  • login function : log an existing user and returns a valid token

userProfile.js

(function () {
    "use strict";
    angular
        .module("common.services")
        .factory("userProfile",
        userProfile)

    function userProfile() {
        var setProfile = function (data) {
            sessionStorage.setItem('accessToken', data.token);
            sessionStorage.setItem('claims', data.claims);
            sessionStorage.setItem('expiration', data.expiration);
        };

        var getProfile = function () {
            var profile = {
                isLoggedIn: sessionStorage.getItem('accessToken') != null,
                token: sessionStorage.getItem('accessToken'),
                claims: sessionStorage.getItem('claims'),
                expiration: sessionStorage.getItem('expiration')
            };
            return profile;
        };

        var getToken = function () {
            return sessionStorage.getItem('accessToken');
        };

        var getAuthHeaders = function () {
            var accesstoken = sessionStorage.getItem('accessToken');
            var authHeaders = {};
            if (accesstoken) {
                authHeaders.Authorization = 'Bearer ' + accesstoken;
            }
            return authHeaders;
        };
        var logout = function () {
            sessionStorage.removeItem('accessToken');
        };

        return {
            setProfile: setProfile,
            getProfile: getProfile,
            getToken: getToken,
            getAuthHeaders: getAuthHeaders,
            logout: logout
        }
    }
})();

userProfile : after succesfull login , store user informations like userName, token and claims to sessionStorage. 

If the user logout, I clear  user informations  from session

ProductListCtrl.js

(function () {
    "use strict";
    angular
        .module("appmodule")
        .controller("ProductListCtrl",
        ["$scope", "productService",
            ProductListCtrl]);

    function ProductListCtrl($scope, productService) {
        var vm = this;
        vm.products = [];
        vm.Message = "";
        GetProducts();
        function GetProducts() {
            var groupResult = productService.get();
            groupResult.then(function (resp) {
                vm.products = resp.data;
                vm.Message = "Call Completed Successfully";
            }, function (err) {
                vm.Message = "Error!!! " + err.status
            });
        };
    }
}());

productService.js

(function () {
    "use strict";
    angular
        .module("common.services")
        .factory("productService", ["$http", "appSettings", "userProfile",
            productService])

    function productService($http, appSettings, userProfile) {
        this.get = function () {
            var authHeaders = userProfile.getAuthHeaders();
            var response = $http({
                url: appSettings.serverPath + "/api/product",
                method: "GET",
                headers: authHeaders
            });
            return response;
        };

        return {
            get: this.get
        }
    }
})();

To get products, user must be logged in first, so user must retrive a token, and send the token with the getProduct request

var authHeaders = userProfile.getAuthHeaders();

var response = $http({
url: appSettings.serverPath + “/api/product”,
method: “GET”,
headers: authHeaders
});

productListView.html

<div class="panel panel-success"
     ng-controller="ProductListCtrl as vm">
    <div class="panel-heading"
         style="font-size:large">
        Product List : you must be logged in to view this list
    </div>

    <div class="panel-body">
        <table class="table">

            <thead>
                <tr>
                    <td>Id</td>
                    <td>Name</td>
                    <td>Price</td>
                    <td>Description</td>
                </tr>
            </thead>
            <tbody>
                <tr ng-repeat="product in vm.products">
                    <td>{{ product.id}}</td>
                    <td>{{ product.name }}</td>
                    <td>{{ product.price  }}</td>
                    <td>{{ product.description }}</td>
                </tr>
            </tbody>
        </table>
    </div>
</div>

SEE IT IN ACTION

Open Package Manager Console and run update-database command to generate database

This is the generated database on localdb

Configure Startup multiple project : TokenAuthWebApiCore.Server and AngularJSClient

Run F5

 

Soure code is available here  https://github.com/logcorner/Angular-JS-Token-Based-Authentication-using-Asp.net-Core-Web-API-2.0-and-JSON-Web-Token

Gora LEYE

I'm a microsoft most valuable professional (MVP) .NET Architect and Technical Expert skills located in Paris (FRANCE). The purpose of this blog is mainly to post general .NET tips and tricks, www.masterconduite.com Gora LEYE

Support us

BMC logoBuy me a coffee