Part 2 : Token Based Authentication Using ASP.NET Web API in AngularJS



Introduction

Series : Token based authentication using ASP.NET Web API in AngularJS

In part 1 of this series "Token-based authentication in ASP.NET Web API" I have shown you, how to Secure ASP.NET Web API using Token Based Authentication, where we have done all the code on the server side web API application and tested our application with POSTMAN chrome extension.

Now in this article (Part 2) I am going to show you how to implement token based  secured web API service in AngularJS application.

We know that how token based authentication actually works.
When we will access secured resources from token based secured server using our client application, we have to pass signed token in the authorization header. Then this token will be validated by the server and if the token is valid then we will get a valid response from server otherwise, we will get 401 (Unauthorized)/403 (Forbidden)  response.


So what we will do in our client application? 
First of all, we will build a simple single page application (SPA) using AngularJS. In this client application, we will add following pages (route)

See How to implement AngularJS Routing in ASP.NET MVC application

1. Home - In this page, we will fetch data from our web API service action (URL : api/data/forall) which is allowed for anonymous users.

2. Authenticated - Here we will fetch data from web APIs service action  (URL : api/data/authenticate) which is allowed for authenticated users.

3. Authorized - Here we will fetch data from web API service action (URL : api/data/authorized) which is allowed for "admin" role authenticated users.

4. Login - When our token is missing or not valid, we will get 401 (Unauthorized) response from the server. Then we will redirect the user to login page, where we will get a token from the server after providing a valid credential.

5. Unauthorize - When our token is valid but the request is not authorized to perform the requested action then we will get 403 (Forbidden). Then we will redirect the user to Unauthorize page.

Follow the following steps in order to implement "Part 2 : Token-Based Authentication Using ASP.NET Web API in AngularJS".

Here In this article, I have used Visual Studio 2013

Step - 1: Add a New Project.

In part 1, we have already created a project where we have configured our API (Web API). Now we will start with adding 1 another project in the same solution for our client application.

Go to solution explorer > Right click on your solution > Add > New Project > Select "ASP.NET Empty Web Application" under web > Enter your project name & selete the project location > Click on ok button.

    It will bring up a new dialog window for select template > here I will select empty template > and then click on Ok button. 

Step-2: Add a javascript file, where we will write AngularJS code. 

Before adding the javascript file into our project I will add a "Scripts" folder then we will add the javascript file.

Go to solution Explorer > Right-click on your project name > Add > New Folder > Rename your added folder.

and then right click on the created folder > add > new item... > Select "Javascript File" > enter your file name > add.

For better understanding, I have added all the required AngularJS components (module, controller, factory,Http interceptor etc.) in this single file. Later we will see how to do Modular AngularJS App Design as we know An ideal AngularJS app structure should be modularized into very specific functions.
var myApp = angular.module('myApp', ['ngRoute']);
//config routing
myApp.config(['$routeProvider', function ($routeProvider) {
    $routeProvider
    .when('/', {
        redirectTo : '/home'
    })
    .when('/home', {
        templateUrl: '/template/home.html',
        controller: 'homeController'
    })
    .when('/authenticated', {
        templateUrl: '/template/authenticate.html',
        controller: 'authenticateController'
    })
    .when('/authorized', {
        templateUrl: '/template/authorize.html',
        controller: 'authorizeController'
    })
    .when('/login', {
        templateUrl: '/template/login.html',
        controller: 'loginController'
    })
    .when('/unauthorized', {
        templateUrl: '/template/unauthorize.html',
        controller: 'unauthorizeController'
    })
}])
//global veriable for store service base path
myApp.constant('serviceBasePath', 'http://localhost:25419');
//controllers
myApp.controller('homeController', ['$scope', 'dataService', function ($scope, dataService) {
    //FETCH DATA FROM SERVICES
    $scope.data = "";
    dataService.GetAnonymousData().then(function (data) {
        $scope.data = data;
    })
}])
myApp.controller('authenticateController', ['$scope', 'dataService', function ($scope, dataService) {
    //FETCH DATA FROM SERVICES
    $scope.data = "";
    dataService.GetAuthenticateData().then(function (data) {
        $scope.data = data;
    })
}])
myApp.controller('authorizeController', ['$scope', 'dataService', function ($scope, dataService) {
    //FETCH DATA FROM SERVICES
    $scope.data = "";
    dataService.GetAuthorizeData().then(function (data) {
        $scope.data = data;
    })
}])
myApp.controller('loginController', ['$scope', 'accountService','$location', function ($scope, accountService, $location) {
    //FETCH DATA FROM SERVICES
    $scope.account = {
        username: '',
        password: ''
    }
    $scope.message = "";
    $scope.login = function () {
        accountService.login($scope.account).then(function (data) {
            $location.path('/home');
        }, function (error) {
            $scope.message = error.error_description;
        })
    }
}])
myApp.controller('unauthorizeController', ['$scope', function ($scope) {
    //FETCH DATA FROM SERVICES
    $scope.data = "Sorry you are not authorize to access this page";
}])
//services
myApp.factory('dataService', ['$http', 'serviceBasePath', function ($http, serviceBasePath) {
    var fac = {};
    fac.GetAnonymousData = function () {
        return $http.get(serviceBasePath + '/api/data/forall').then(function (response) {
            return response.data;
        })
    }

    fac.GetAuthenticateData = function () {
        return $http.get(serviceBasePath + '/api/data/authenticate').then(function (response) {
            return response.data;
        })
    }

    fac.GetAuthorizeData = function () {
        return $http.get(serviceBasePath + '/api/data/authorize').then(function (response) {
            return response.data;
        })
    }
    return fac;
}])
myApp.factory('userService', function () {
    var fac = {};
    fac.CurrentUser = null;
    fac.SetCurrentUser = function (user) {
        fac.CurrentUser = user;
        sessionStorage.user = angular.toJson(user);
    }
    fac.GetCurrentUser = function () {
        fac.CurrentUser = angular.fromJson(sessionStorage.user);
        return fac.CurrentUser;
    }
    return fac;
})
myApp.factory('accountService', ['$http', '$q', 'serviceBasePath', 'userService', function ($http, $q, serviceBasePath, userService) {
    var fac = {};
    fac.login = function (user) {
        var obj = { 'username': user.username, 'password': user.password, 'grant_type': 'password' };
        Object.toparams = function ObjectsToParams(obj) {
            var p = [];
            for (var key in obj) {
                p.push(key + '=' + encodeURIComponent(obj[key]));
            }
            return p.join('&');
        }

        var defer = $q.defer();
        $http({
            method: 'post',
            url: serviceBasePath + "/token",
            data: Object.toparams(obj),
            headers : {'Content-Type' : 'application/x-www-form-urlencoded'}
        }).then(function (response) {
            userService.SetCurrentUser(response.data);
            defer.resolve(response.data);
        }, function (error) {
            defer.reject(error.data);
        })
        return defer.promise;
    }
    fac.logout = function () {
        userService.CurrentUser = null;
        userService.SetCurrentUser(userService.CurrentUser);
    }
    return fac;
}])
//http interceptor
myApp.config(['$httpProvider', function ($httpProvider) {
    var interceptor = function(userService, $q, $location)
    {
        return {
            request: function (config) {
                var currentUser = userService.GetCurrentUser();
                if (currentUser != null) {
                    config.headers['Authorization'] = 'Bearer ' + currentUser.access_token;
                }
                return config;
            },
            responseError : function(rejection)
            {
                if (rejection.status === 401) {
                    $location.path('/login');
                    return $q.reject(rejection);
                }
                if (rejection.status === 403) {
                    $location.path('/unauthorized');
                    return $q.reject(rejection);
                }
                return $q.reject(rejection);
            }

        }
    }
    var params = ['userService', '$q', '$location'];
    interceptor.$inject = params;
    $httpProvider.interceptors.push(interceptor);
}]);

I have described all the AngularJS code added (in myApp.JS file) here in this video

Step-3: Add required templates (HTML files) we have defined in AngularJS route configuration.

Here in our project we will add a "template" folder first and then inside this folder we will add following templates (HTML files) 
home.html
<h2>Home Page</h2>
<p>{{data}}</p>

authenticate.html
<h2>Authenticate Page</h2>
<p>{{data}}</p>

authorize.html
<h2>Authorize</h2>
<p>{{data}}</p>

login.html
<h2>Login</h2>
<form ng-submit="login()">
    <div>
        <label>Username : </label>
        <input type="text" ng-model="account.username" />
    </div>
    <div>
        <label>Password : </label>
        <input type="password" ng-model="account.password" />
    </div>
    <input type="submit" />
    <div style="color:red">
        {{message}}
    </div>
</form>

See how to create login page in AngularJS application

unauthorized.html
<h2>Unauthorized Page</h2>
<p>{{data}}</p>

Step-4: Now we will add a HTML file (index.html) in our client project. 

Go to Solution Explorer > Right click on Project Name > Add > New Item... > Select "HTML Page" under web > Enter Name > add.

Index.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Angular JS app</title>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" type="text/css" rel="stylesheet" />

</head>
<body ng-app="myApp">
    <div class="navbar navbar-inverse navbar-fixed-top">
        <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="/">Application name</a>
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a href="#/home">Home</a></li>
                    <li><a href="#/authenticated">Authenticated</a></li>
                    <li><a href="#/authorized">Authorized</a></li>
                </ul>
            </div>
        </div>
    </div>
    <div class="container body-content" style="margin-top:40px;">
        <data-ng-view></data-ng-view>
        <hr />
        <footer>
            <p>&copy; Dotnet awesome</p>
        </footer>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-route.js"></script>
    <script src="/Scripts/myApp.js"></script>
</body>
</html>

Step-5: Run Application.

Before running, we have to do a little configuration for make both Project (WEB API & Angular Client Project) running at the same time because  we will fetch data from Web API services in our  AngularJS client application.

Go to Solution Explorer > Right click on the solution > Properties > Select "Multiple startup projects" under Startup Project > Change action to Start of both projects > Click on Apply > Click on Ok Button. and then we will run our projects.



Hello ! My name is Sourav Mondal. I am a software developer working in Microsoft .NET technologies since 2010.

I like to share my working experience, research and knowledge through my site.

I love developing applications in Microsoft Technologies including Asp.Net webforms, mvc, winforms, c#.net, sql server, entity framework, Ajax, Jquery, web api, web service and more.