Introduction
Series : Token based authentication using ASP.NET Web API in AngularJS
- Part 1 : Token based authentication in ASP.NET Web API
- Part 2 : Token Based Authentication Using ASP.NET Web API in AngularJS
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?
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".
Step - 1: Add a New Project.
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.
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.
<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.
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>© 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.
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.