This is the second part of AngularJS Token Authentication using ASP.NET Web API 2 and Owin middleware, you can find the first part using the link below:
- Token Based Authentication using ASP.NET Web API 2, Owin middleware, and ASP.NET Identity – Part 1.
- Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin – Part 3.
- ASP.NET Web API 2 external logins with Facebook and Google in AngularJS app – Part 4.
- Decouple OWIN Authorization Server from Resource Server – Part 5.
You can check the demo application on (http://ngAuthenticationWeb.azurewebsites.net), play with the back-end API for learning purposes (http://ngauthenticationapi.azurewebsites.net), and check the source code on Github.
In this post we’ll build sample SPA using AngularJS, this application will allow the users to do the following:
- Register in our system by providing username and password.
- Secure certain views from viewing by authenticated users (Anonymous users).
- Allow registered users to log-in and keep them logged in for
24 hours30 minutes because we are using refresh tokens or until they log-out from the system, this should be done using tokens.
If you are new to AngularJS, you can check my other tutorial which provides step by step instructions on how to build SPA using AngularJS, it is important to understand the fundamentals aspects of AngularJS before start working with it, in this tutorial I’ll assume that reader have basic understanding of how AngularJS works.
Step 1: Download Third Party Libraries
To get started we need to download all libraries needed in our application:
- AngularJS: We’ll serve AngularJS from from CDN, the version is 1.2.16
- Loading Bar: We’ll use the loading bar as UI indication for every XHR request the application will made, to get this plugin we need to download it from here.
- UI Bootstrap theme: to style our application, we need to download a free bootstrap ready made theme from http://bootswatch.com/ I’ve used a theme named “Yeti”.
Step 2: Organize Project Structure
You can use your favorite IDE to build the web application, the app is completely decoupled from the back-end API, there is no dependency on any server side technology here, in my case I’m using Visual Studio 2013 so add new project named “AngularJSAuthentication.Web” to the solution we created in the previous post, the template for this project is “Empty” without any core dependencies checked.
After you add the project you can organize your project structure as the image below, I prefer to contain all the AngularJS application and resources files we’ll create in folder named “app”.
Step 3: Add the Shell Page (index.html)
Now we’ll add the “Single Page” which is a container for our application, it will contain the navigation menu and AngularJS directive for rendering different application views “pages”. After you add the “index.html” page to project root we need to reference the 3rd party JavaScript and CSS files needed as the below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
<!DOCTYPE html> <html data-ng-app="AngularAuthApp"> <head> <meta content="IE=edge, chrome=1" http-equiv="X-UA-Compatible" /> <title>AngularJS Authentication</title> <link href="content/css/bootstrap.min.css" rel="stylesheet" /> <link href="content/css/site.css" rel="stylesheet" /> <link href="content/css/loading-bar.css" rel="stylesheet" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> </head> <body> <div class="navbar navbar-inverse navbar-fixed-top" role="navigation" data-ng-controller="indexController"> <div class="container"> <div class="navbar-header"> <button class="btn btn-success navbar-toggle" data-ng-click="navbarExpanded = !navbarExpanded"> <span class="glyphicon glyphicon-chevron-down"></span> </button> <a class="navbar-brand" href="#/">Home</a> </div> <div class="collapse navbar-collapse" data-collapse="!navbarExpanded"> <ul class="nav navbar-nav navbar-right"> <li data-ng-hide="!authentication.isAuth"><a href="#">Welcome {{authentication.userName}}</a></li> <li data-ng-hide="!authentication.isAuth"><a href="#/orders">My Orders</a></li> <li data-ng-hide="!authentication.isAuth"><a href="" data-ng-click="logOut()">Logout</a></li> <li data-ng-hide="authentication.isAuth"> <a href="#/login">Login</a></li> <li data-ng-hide="authentication.isAuth"> <a href="#/signup">Sign Up</a></li> </ul> </div> </div> </div> <div class="jumbotron"> <div class="container"> <div class="page-header text-center"> <h1>AngularJS Authentication</h1> </div> <p>This single page application is built using AngularJS, it is using OAuth bearer token authentication, ASP.NET Web API 2, OWIN middleware, and ASP.NET Identity to generate tokens and register users.</p> </div> </div> <div class="container"> <div data-ng-view=""> </div> </div> <hr /> <div id="footer"> <div class="container"> <div class="row"> <div class="col-md-6"> <p class="text-muted">Created by Taiseer Joudeh. Twitter: <a target="_blank" href="http://twitter.com/tjoudeh">@tjoudeh</a></p> </div> <div class="col-md-6"> <p class="text-muted">Taiseer Joudeh Blog: <a target="_blank" href="https://bitoftech.net">bitoftech.net</a></p> </div> </div> </div> </div> <!-- 3rd party libraries --> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js"></script> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular-route.min.js"></script> <script src="scripts/angular-local-storage.min.js"></script> <script src="scripts/loading-bar.min.js"></script> <!-- Load app main script --> <script src="app/app.js"></script> <!-- Load services --> <script src="app/services/authInterceptorService.js"></script> <script src="app/services/authService.js"></script> <script src="app/services/ordersService.js"></script> <!-- Load controllers --> <script src="app/controllers/indexController.js"></script> <script src="app/controllers/homeController.js"></script> <script src="app/controllers/loginController.js"></script> <script src="app/controllers/signupController.js"></script> <script src="app/controllers/ordersController.js"></script> </body> </html> |
Step 4: “Booting up” our Application and Configure Routes
We’ll add file named “app.js” in the root of folder “app”, this file is responsible to create modules in applications, in our case we’ll have a single module called “AngularAuthApp”, we can consider the module as a collection of services, directives, filters which is used in the application. Each module has configuration block where it gets applied to the application during the bootstrap process.
As well we need to define and map the views with the controllers so open “app.js” file and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
var app = angular.module('AngularAuthApp', ['ngRoute', 'LocalStorageModule', 'angular-loading-bar']); app.config(function ($routeProvider) { $routeProvider.when("/home", { controller: "homeController", templateUrl: "/app/views/home.html" }); $routeProvider.when("/login", { controller: "loginController", templateUrl: "/app/views/login.html" }); $routeProvider.when("/signup", { controller: "signupController", templateUrl: "/app/views/signup.html" }); $routeProvider.when("/orders", { controller: "ordersController", templateUrl: "/app/views/orders.html" }); $routeProvider.otherwise({ redirectTo: "/home" }); }); app.run(['authService', function (authService) { authService.fillAuthData(); }]); |
So far we’ve defined and mapped 4 views to their corresponding controllers as the below:
- Home view which shows the home page and can be accessed by anonymous users on http://ngauthenticationweb.azurewebsites.net/#/home
- Signup view which shows signup form and can be accessed by anonymous users on http://ngauthenticationweb.azurewebsites.net/#/signup
- Log-in view which shows log-in form and can be accessed by anonymous users on http://ngauthenticationweb.azurewebsites.net/#/login
- Orders view which shows orders forms for authenticated users only as the image below, view can be accessed on http://ngauthenticationweb.azurewebsites.net/#/orders
Step 5: Add AngularJS Authentication Service (Factory)
This AngularJS service will be responsible for signing up new users, log-in/log-out registered users, and store the generated token in client local storage so this token can be sent with each request to access secure resources on the back-end API, the code for AuthService will be as the below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
'use strict'; app.factory('authService', ['$http', '$q', 'localStorageService', function ($http, $q, localStorageService) { var serviceBase = 'http://ngauthenticationapi.azurewebsites.net/'; var authServiceFactory = {}; var _authentication = { isAuth: false, userName : "" }; var _saveRegistration = function (registration) { _logOut(); return $http.post(serviceBase + 'api/account/register', registration).then(function (response) { return response; }); }; var _login = function (loginData) { var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password; var deferred = $q.defer(); $http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) { localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName }); _authentication.isAuth = true; _authentication.userName = loginData.userName; deferred.resolve(response); }).error(function (err, status) { _logOut(); deferred.reject(err); }); return deferred.promise; }; var _logOut = function () { localStorageService.remove('authorizationData'); _authentication.isAuth = false; _authentication.userName = ""; }; var _fillAuthData = function () { var authData = localStorageService.get('authorizationData'); if (authData) { _authentication.isAuth = true; _authentication.userName = authData.userName; } } authServiceFactory.saveRegistration = _saveRegistration; authServiceFactory.login = _login; authServiceFactory.logOut = _logOut; authServiceFactory.fillAuthData = _fillAuthData; authServiceFactory.authentication = _authentication; return authServiceFactory; }]); |
Now by looking on the method “_saveRegistration” you will notice that we are issuing HTTP Post to the end point “http://ngauthenticationapi.azurewebsites.net/api/account/register” defined in the previous post, this method returns a promise which will be resolved in the controller.
The function “_login” is responsible to send HTTP Post request to the endpoint “http://ngauthenticationapi.azurewebsites.net/token”, this endpoint will validate the credentials passed and if they are valid it will return an “access_token”. We have to store this token into persistence medium on the client so for any subsequent requests for secured resources we’ve to read this token value and send it in the “Authorization” header with the HTTP request.
Notice that we have configured the POST request for this endpoint to use “application/x-www-form-urlencoded” as its Content-Type and sent the data as string not JSON object.
The best way to store this token is to use AngularJS module named “angular-local-storage” which gives access to the browsers local storage with cookie fallback if you are using old browser, so I will depend on this module to store the token and the logged in username in key named “authorizationData”. We will use this key in different places in our app to read the token value from it.
As well we’ll add object named “authentication” which will store two values (isAuth, and username). This object will be used to change the layout for our index page.
Step 6: Add the Signup Controller and its View
The view for the signup is simple so open file named “signup.html” and add it under folders “views” open the file and paste the HTML below:
1 2 3 4 5 6 7 8 9 10 |
<form class="form-login" role="form"> <h2 class="form-login-heading">Sign up</h2> <input type="text" class="form-control" placeholder="Username" data-ng-model="registration.userName" required autofocus> <input type="password" class="form-control" placeholder="Password" data-ng-model="registration.password" required> <input type="password" class="form-control" placeholder="Confirm Password" data-ng-model="registration.confirmPassword" required> <button class="btn btn-lg btn-info btn-block" type="submit" data-ng-click="signUp()">Submit</button> <div data-ng-hide="message == ''" data-ng-class="(savedSuccessfully) ? 'alert alert-success' : 'alert alert-danger'"> {{message}} </div> </form> |
Now we need to add controller named “signupController.js” under folder “controllers”, this controller is simple and will contain the business logic needed to register new users and call the “saveRegistration” method we’ve created in “authService” service, so open the file and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
'use strict'; app.controller('signupController', ['$scope', '$location', '$timeout', 'authService', function ($scope, $location, $timeout, authService) { $scope.savedSuccessfully = false; $scope.message = ""; $scope.registration = { userName: "", password: "", confirmPassword: "" }; $scope.signUp = function () { authService.saveRegistration($scope.registration).then(function (response) { $scope.savedSuccessfully = true; $scope.message = "User has been registered successfully, you will be redicted to login page in 2 seconds."; startTimer(); }, function (response) { var errors = []; for (var key in response.data.modelState) { for (var i = 0; i < response.data.modelState[key].length; i++) { errors.push(response.data.modelState[key][i]); } } $scope.message = "Failed to register user due to:" + errors.join(' '); }); }; var startTimer = function () { var timer = $timeout(function () { $timeout.cancel(timer); $location.path('/login'); }, 2000); } }]); |
Step 6: Add the log-in Controller and its View
The view for the log-in is simple so open file named “login.html” and add it under folders “views” open the file and paste the HTML below:
1 2 3 4 5 6 7 8 9 |
<form class="form-login" role="form"> <h2 class="form-login-heading">Login</h2> <input type="text" class="form-control" placeholder="Username" data-ng-model="loginData.userName" required autofocus> <input type="password" class="form-control" placeholder="Password" data-ng-model="loginData.password" required> <button class="btn btn-lg btn-info btn-block" type="submit" data-ng-click="login()">Login</button> <div data-ng-hide="message == ''" class="alert alert-danger"> {{message}} </div> </form> |
Now we need to add controller named “loginController.js” under folder “controllers”, this controller will be responsible to redirect authenticated users only to the orders view, if you tried to request the orders view as anonymous user, you will be redirected to log-in view. We’ll see in the next steps how we’ll implement the redirection for anonymous users to the log-in view once users request a secure view.
Now open the “loginController.js” file and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
'use strict'; app.controller('loginController', ['$scope', '$location', 'authService', function ($scope, $location, authService) { $scope.loginData = { userName: "", password: "" }; $scope.message = ""; $scope.login = function () { authService.login($scope.loginData).then(function (response) { $location.path('/orders'); }, function (err) { $scope.message = err.error_description; }); }; }]); |
Step 7: Add AngularJS Orders Service (Factory)
This service will be responsible to issue HTTP GET request to the end point “http://ngauthenticationapi.azurewebsites.net/api/orders” we’ve defined in the previous post, if you recall we added “Authorize” attribute to indicate that this method is secured and should be called by authenticated users, if you try to call the end point directly you will receive HTTP status code 401 Unauthorized.
So add new file named “ordersService.js” under folder “services” and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
'use strict'; app.factory('ordersService', ['$http', function ($http) { var serviceBase = 'http://ngauthenticationapi.azurewebsites.net/'; var ordersServiceFactory = {}; var _getOrders = function () { return $http.get(serviceBase + 'api/orders').then(function (results) { return results; }); }; ordersServiceFactory.getOrders = _getOrders; return ordersServiceFactory; }]); |
By looking at the code above you’ll notice that we are not setting the “Authorization” header and passing the bearer token we stored in the local storage earlier in this service, so we’ll receive 401 response always! Also we are not checking if the response is rejected with status code 401 so we redirect the user to the log-in page.
There is nothing prevent us from reading the stored token from the local storage and checking if the response is rejected inside this service, but what if we have another services that needs to pass the bearer token along with each request? We’ll end up replicating this code for each service.
To solve this issue we need to find a centralized place so we add this code once so all other services interested in sending bearer token can benefit from it, to do so we need to use “AngualrJS Interceptor“.
Step 8: Add AngularJS Interceptor (Factory)
Interceptor is regular service (factory) which allow us to capture every XHR request and manipulate it before sending it to the back-end API or after receiving the response from the API, in our case we are interested to capture each request before sending it so we can set the bearer token, as well we are interested in checking if the response from back-end API contains errors which means we need to check the error code returned so if its 401 then we redirect the user to the log-in page.
To do so add new file named “authInterceptorService.js” under “services” folder and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
'use strict'; app.factory('authInterceptorService', ['$q', '$location', 'localStorageService', function ($q, $location, localStorageService) { var authInterceptorServiceFactory = {}; var _request = function (config) { config.headers = config.headers || {}; var authData = localStorageService.get('authorizationData'); if (authData) { config.headers.Authorization = 'Bearer ' + authData.token; } return config; } var _responseError = function (rejection) { if (rejection.status === 401) { $location.path('/login'); } return $q.reject(rejection); } authInterceptorServiceFactory.request = _request; authInterceptorServiceFactory.responseError = _responseError; return authInterceptorServiceFactory; }]); |
By looking at the code above, the method “_request” will be fired before $http sends the request to the back-end API, so this is the right place to read the token from local storage and set it into “Authorization” header with each request. Note that I’m checking if the local storage object is nothing so in this case this means the user is anonymous and there is no need to set the token with each XHR request.
Now the method “_responseError” will be hit after the we receive a response from the Back-end API and only if there is failure status returned. So we need to check the status code, in case it was 401 we’ll redirect the user to the log-in page where he’ll be able to authenticate again.
Now we need to push this interceptor to the interceptors array, so open file “app.js” and add the below code snippet:
1 2 3 |
app.config(function ($httpProvider) { $httpProvider.interceptors.push('authInterceptorService'); }); |
By doing this there is no need to setup extra code for setting up tokens or checking the status code, any AngularJS service executes XHR requests will use this interceptor. Note: this will work if you are using AngularJS service $http or $resource.
Step 9: Add the Index Controller
Now we’ll add the Index controller which will be responsible to change the layout for home page i.e (Display Welcome {Logged In Username}, Show My Orders Tab), as well we’ll add log-out functionality on it as the image below.
Taking in consideration that there is no straight way to log-out the user when we use token based approach, the work around we can do here is to remove the local storage key “authorizationData” and set some variables to their initial state.
So add a file named “indexController.js” under folder “controllers” and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 |
'use strict'; app.controller('indexController', ['$scope', '$location', 'authService', function ($scope, $location, authService) { $scope.logOut = function () { authService.logOut(); $location.path('/home'); } $scope.authentication = authService.authentication; }]); |
Step 10: Add the Home Controller and its View
This is last controller and view we’ll add to complete the app, it is simple view and empty controller which is used to display two boxes for log-in and signup as the image below:
So add new file named “homeController.js” under the “controllers” folder and paste the code below:
1 2 3 4 |
'use strict'; app.controller('homeController', ['$scope', function ($scope) { }]); |
As well add new file named “home.html” under “views” folder and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<div class="row"> <div class="col-md-2"> </div> <div class="col-md-4"> <h2>Login</h2> <p class="text-primary">If you have Username and Password, you can use the button below to access the secured content using a token.</p> <p><a class="btn btn-info" href="#/login" role="button">Login »</a></p> </div> <div class="col-md-4"> <h2>Sign Up</h2> <p class="text-primary">Use the button below to create Username and Password to access the secured content using a token.</p> <p><a class="btn btn-info" href="#/signup" role="button">Sign Up »</a></p> </div> <div class="col-md-2"> </div> </div> |
By now we should have SPA which uses the token based approach to authenticate users.
One side note before closing: The redirection for anonymous users to log-in page is done on client side code; so any malicious user can tamper with this. It is very important to secure all back-end APIs as we implemented on this tutorial and not to depend on client side code only.
That’s it for now! Hopefully this two posts will be beneficial for folks looking to use token based authentication along with ASP.NET Web API 2 and Owin middleware.
I would like to hear your feedback and comments if there is a better way to implement this especially redirection users to log-in page when the are anonymous.
You can check the demo application on (http://ngAuthenticationWeb.azurewebsites.net), play with the back-end API for learning purposes (http://ngauthenticationapi.azurewebsites.net), and check the source code on Github.
Maybe it is just me, but app took like 10sec to register at the first time…? after that it worked fine. However, this looks interesting, I haven’t used angular before but it would be cool to try out these things. I actually used token based auth earlier with my multiplayer html5 game, but it was made using against express server (nodejs) and jsonwebtoken. What do you consider as good backend for single page applications? Other than ASP? 🙂
Thanks Mauno for your feedback.
Well I usually use ASP.NET Web API as a back-end for building APIs, but you have tried on the best back-end servers nowadays, the MEAN stack (MongoDB, Express, AngularJS, and Node) is very popular now when you build SPAs as you can share some JS code between your client and server. Good luck in using AngularJS, one of the best front-end frameworks!
Okey & thanks for sharing your opinions! 🙂
Great stuff, thanks for sharing. I’m working on an Web API 2 implementation that requires Network Load Balancing, and I’m trying to figure out how to get it done properly. The main problem I have is this: We can have “N” instances of the web api (let’s say 15 for example) in the farm. So, if a client generates an authentication token and its valid for server 1, how do I make sure it also works for the other servers in the farm? Or, is there a way to move the token generator/validator to an external URL instead of ” TokenEndpointPath = new PathString(“/token”) ” so it can be accessed from all the different instance of the Web API?
Hi Jonathan, please check this url which answers your questions:
http://stackoverflow.com/questions/21102252/how-are-bearer-tokens-stored-server-side-in-web-api-2
As long you are using the same machine-key for encryption for all servers then the tokens will be validated correctly on any server on your farm.
@Mauno — I think the initial delay may have been caused by entity framework having had to initially build the database as this was indeed a ‘code first’ implementation… otherwise, it should have been very fast.
@Jonathan — I’m not sure if the token is stored in the db automatically for retrieval by multiple instances…. however if it is not, then perhaps adding in code to save the token into something like Redis or SQL Server (accessed by many instances in the farm) prior to issuing the token to the client would work.
Thanks Mark for your reply, the tokens are not stored in DB, they are self contained so it should work if you have web farm.
Thank you Mark
Great article! Very helpful for me, since I’m just getting started with ASP.NET.
Just a (probably stupid) question, since I’m not familiar with how azure websites work:
You have a decoupled WebAPI and AngularJS app implementation, which is really nice. But where did you implement the serving of the shell page (index.html)? There should be a route for that somewhere, right? I don’t know if I accidentally skipped it or what, but I didn’t find it in the tutorial.
Hello Simone,
Glad that the article was beneficial for you.
Well the “Index.html” is not a partial view, it is the only full html page in the app.
It is get served by default without typing “index.html” explicitly because in Azure website there is setting called “default documents”. Because it is named “index.html” its getting serviced directly. It will be the same case if it is named “default.aspx” or “default.htm”..etc If it was named something else like mypage.html then I should handle this explicit to service it by default.
Hope this answers your question.
It does, thank you Taiseer 🙂 I imagined it had to do with default settings of the azure service, but I wanted to be sure. Thank you for the clarification!
Is there an easy way to enable TwoFactorAuthentication(Email) on your example ? I spent the whole day trying to figure it out but it seems I just don’t get any closer. Thanks a lot!
Hi Bogdan, I’ll check how I can implement this in the coming two days, Hopefully I’ll come out with something good. Will keep you updated.
Hit F5 … so far so good…
Then registered a new username with password and confirmation keeping my fingers crossed waiting a few seconds for database data to generate…
Entered my new created username and password on login form and then…Yeeeeeeeeeeeeeeees!!!!,
Everything works like a charm… Now that’s what I’m talking about!
I must say I loved your “Nuget bottom-up” stepped approach dealing with Web API 2 OWIN. That’s pretty clean and concise.
Decoupling Web API 2 OWIN services on back-end and AngularJS app implementation on client in two separate independent apps is the way I see things. 110% with you on that approach. Good job.
Thanks mawashikid for your sweet comment 🙂 appreciate it.
Decoupling the front-end from the back-end is the right way to build SPA, really you can switch to any back-end in future.
Hi Taiseer,
Excellent article. Thank you.
The VS2013 ASP.NET MVC project template comes with an AccountController that implements the creation of Roles and UserRoles, so do you have an example of how to implement this in your spa solution?
Any help appreciated.
Regards,
Paul
dynamically generate routeProvider?
Yup it an AngularJS service comes when you inject ngRoute.
Hi Paul, you are welcome. Glad you liked the article.
Currently no, but I will consider looking into Roles and will keep you updated if I come with something useful 🙂
Hi Taiseer,
Appreciate your efforts in creating and posting this article as well as source to GitHub.
Mixing latest and the greatest of angularjs, owin, etc, job well done!
Thanks a ton! 🙂
Cheers,
–Kaush.
Hello Kaush, appreciate your nice comment..Glad that this article was helpful 🙂 Keep tuned as I want to add more features to this tutorial.
Looking forward to it! 🙂
Excellent read. Thank you for detailed explanations.
I tried commenting out the UseCors line and not sending the Access-Control-Allow-Origin header and my solution still worked. I suppose It’s because both client and server are on same host. Although ports are different. Should fail.
Thanks Alex, glad you enjoyed reading it.
If you are using IE then it will pass, try using another browser and the call will fail if the ports are different.
Hi Taiseer,
Excellent tutorial, thank you for sharing.
I was wondering if there is a way to use your solution to connect to a “custom” database. At work, we have a custom-made Users table in our SQL Server database that stores usernames, password hashes, etc. but it was created years ago without any consideration for OAuth, Identity, etc. Perhaps some way to intercept the calls to the Identity database and change them to target a differently structured database?
I’d appreciate your thoughts.
Hello djikay,
Glad it was beneficial for a solution you are working on 🙂 well it is pretty simple to implement this, you do not need to use ASP.NET Identity to implement generating/consuming tokens, in step 10 from the first part of this tutorial you can replace this LOC IdentityUser user = await _repo.FindUser(context.UserName, context.Password); with your custom logic to verify credentials and everything will remain the same, hope this answers your question.
Many thanks for the quick response, Taiseer. As I’m very new with this technology and methodology in general, I wasn’t able to actually implement what you said or even use your solution. I’ve downloaded your code from GitHub but it doesn’t run locally (I get an IIS Express error page), probably because it’s trying to connect to your database instance instead of my own, and I’ve no idea what to do to fix this. I’m really confused about all this, but it’s entirely down to my own limitations and not your code. For my particular requirements, I can’t use IIS (I’m self-hosting using OWIN) and I have installed my own copy of SQL Express 2012, so my development environment is different from your article. Anyway, once again, thank you for a wonderful article and hope one day to be able to understand and use it.
Nice post. I guess the next step is two factor authentication – http://blogs.msdn.com/b/webdev/archive/2014/02/18/adding-two-factor-authentication-to-an-application-using-asp-net-identity.aspx
Thank you for this, it really helped me understand and implement token based authentication. If you’re adding features, I vote for role based access control. I’ve been having trouble restricting access to specific pages or elements based on the user’s role. My scenario will have the user’s roles returned by the server in the form of a number 1-6, but I’d love to see you cover this topic in any form.
Thank you!
Justin
Hi Justin,
Glad it was beneficial for you, the next part will cover roles, this has been requested by many readers. I’m working on it and hopefully it will be published soon.
Hi, Taiseer! Thanks for your awesome articles! Have you published something about using roles? Thanks again for you response
Hi,
to be honest not yet, not enough time:(
Hi Taiser,
Just to let you know…. I’m also interested in how you would implement roles.
BTW: This is a great article and is very much appreciated.
Regards,
Paul
I also interest in Roles too. But I think it will take time to write it. Below is my idea for further working with Roles in Web Api:
– Work with Claims-based identity
– Write a custome Authorization Filter.
How do you think about this approach, Taiseer?
Taiseer, is there any possibility to have the client side in backbone? Thanks!
Hi Alex,
I’m really sorry but I do not have solid hands on experience with backbone, but I believe it is really easy to transfer it. Why not to fork my project and once you applied this using Backbone I’ll update my post to point to your repo.
Adding a custom IdentityUser would be a good addition to the tutorial.
Could there be a single routes file and separate the views and controllers for modules?
Hi Rodrigo,
Could you elaborate more please? You mean that application structure should be changed to match the modules?
is more a question
is not regarding their application
I want to separate module in my application
app
controllers
views
policies
services
– modules
—- main
——– controllers
——– views
——– service
——– index. html
—- financial
——– controllers
——– views
——– service
——– index. html
app.js
And only can to have a file of routes, these routes must come from the api for user permission
I like your example of authentication,I will implement something in my app
But my question is how to create dynamic routes for module after logging
you can give me a help to go study and search?
Try looking here:
http://alexfeinberg.wordpress.com/2014/04/26/delay-load-anything-angular/
Or here
http://alexfeinberg.wordpress.com/2014/04/26/delay-load-anything-angular/
Thanks Alex, Taiseer
I not use RequireJS, I use the module’s own angularjs
Reference: http://clintberry.com/2013/modular-angularjs-application-design/
Can I change $ location.path (‘/ orders’); to windows.location = “other paste”?
Because I want to use another file for routes
My idea is:
app
– controllers
– views
– js app.
– main
—- modules
—— module1
——— controllers
——— views
—— module2
——— controllers
——— views
—- app. js
ie after login I want to redirect to another module and have my app.js file with the routes according to the user’s permission, dynamically
Taiseer I m using your app as the base of my app
I’ll be glad if you help me on that last question
Hi Alex,
You can do redirection using window.location but I believe this will do full postback to the server i.e hitting refresh button (F5). I cant get what you are trying to do with app.js file and permissions 🙁
Great tutorials Taiseer. Have you tried passing the Authorization headers with jsonp requests? I couldn’t get that working, the headers are never passed.
Hi Ravi, you are welcome, to be honest no, but why it will fail? Can’t you enable CORS in the back end API and stop using JSONP?
Nice Work Taiseer!!! good introductory start for log in functionality in angular. Was very helpful. Thanks
Thanks Asad, glad it was useful 🙂 keep tuned as I’m implementing refresh tokens in detailed post.
Hi Taiseer Joudeh
Thanks for the material helped me a lot
I have a question
I see that is in the memory the user token through clains
I’m developing a multitenancy, recognize my schema for subdomains, but I can leave in memory my schema without need to always check?
Hi Maxwell,
Can you elaborate little bit more about your scenario? Can’t understand what you are trying to achieve.
Hi Taiseer, I’m having a problem with register method.
I don’t know why, the saveRegistration method in authService factory is always sending a post request with a Content-Type text/plain header and the request fails. I’ve tried to force including headers in the $http.post but angularjs always send the post as text/plain.
Any idea about what could be the problem?
Hi Jose, can you make sure it is Post request or a preflighted request (Options) request which issued by the browser to check if your origin is allowed send post request to this API? If its preflighted request make sure that your API supports CORS correctly.
Excellent series! Everything seems to work out great – with one exception. I’m seeing that when I attempt to log in, the browser is sending a “preflight” OPTIONS request before it calls the “~/token” url. This results in a 400 Bad Request, where the payload says {“error”:”unsupported_grant_type”}. How do we work around the browsers preflight request for CORS requests?
Hi Todd,
Are you sending the request against the API I’ve built or something you are building?
I’ve tried it and its work correctly, anyhow if you are using PostMan I’ve prepared the request for your here: https://www.getpostman.com/collections/1ff438e088efac658be8 you can import it and test it out.
If your building new API; double check that you’be enabled CORS correctly on the API.
Let me know if you need further help.
I guess I should have prefaced my previous comment. Everything seems to work fine except for one thing, which is probably easily fixable: If I log in successfully, then manually navigate back to “/login” (via the address bar) and enter my credentials again, the browser sends a preflight request instead. It fails with a 400 sending back “{“error”:”unsupported_grant_type”}”. The code then goes through the failed path, in which “logout” is called. Then if you go to log in again, the request goes through fine. This is because we’re not adding the “Authorization” header in that request (since we don’t have the token anymore because we just logged out). Would the solution to the problem be in the loginController when “login” is called to first check if the user is logged in. If they are, then skip calling the authService.login() function..? Or, if the user navigates to “/login” but they’re already logged in, we navigate them to somewhere else?
Hi Todd,
You are correct I was able to reproduce this issue, well I think it is better to redirect the logged in user to the /home view if the are logged in.
I will check why it is returning bad request when the user is logged in, most probably because am sitting the authorization header as you suggested.
Another alternative that I think feels better, is to just call Logout right before the Login service call is invoked. That way, there’s no need to do any special-case redirection. Thoughts?
Great article, can you recommend a resource that would cover using openId providers like Facebook, Google, etc. along with this?
Hi Larry,
Just wait couple of days and I’ll publish the 4th part which covers external logins. Subscribe to my blog to keep tuned.
Hi Larry,
I’ve blogged about this in detail, you can check it here: http://bit.ly/1rfBOW6 hope it will be useful for your case.
Hi,
I’ve been looking for a way to do authentication with web api and angualrjs for a while and i came across this article, which is not only excellent and well explained, but has help out out with a project i’m working on.
I have stumbled across an issue and unfortunately its with our friend CORS and wondered if you have seen this happen before.
First time round on authenticating it hits:
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
but that is as far as it goes before returning: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource.
on second time it hits both:
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add(“Access-Control-Allow-Origin”, new[] { “*” });
using (var authRepository = new AuthRepository())
{
IdentityUser user = await authRepository.FindUser(context.UserName, context.Password);
if (user == null)
{
context.SetError(“invalid_grant”, “The user name or password is incorrect.”);
return;
}
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(“sub”, context.UserName));
identity.AddClaim(new Claim(“role”, “user”));
context.Validated(identity);
}
and in it goes and everything works.
I’ve lost a couple of hours looking into what it could be and have come up against a brick wall.
Thank you
Hi Steve, thanks for your sweet comment, glad I was able to help.
Could you please check “Todd Behunin” comment thread on this post. Most probably you are trying to login again while you are already logged in. Most probably there is bug in the JS code. Can you please confirm if this is the same issue?
Bang!! exactly the same issue as “Todd Behunin” was having. I can’t believe I missed it. I used exactly what Todd suggested “just call Logout right before the Login service call is invoked” and it worked and why wouldn’t it and its so simple. Think you get to a stage when the banging of a head on the desk clouds simple thinking.
Thank you Taiseer and thank you Todd!!
Taiseer keep up the good work 🙂
Great and your are welcome 🙂 I’ll update the source code and code snippet soon to fix this.
Glad to know the work-around I proposed may have helped someone! 🙂
Hi Taiseer,
Thanks for great article.
I am stuck in one problem. I have one MVC app in which i want to use token based authentication. In this app i am using web api for all database communication. I am also generating access token on webapi with the help of your article. This part is working fine. Now in application part, when user login first time i am generating acces token and store that in cookies. My question is what should i do here for authentication. for now i am using form authentication by using that i am showing user is authenticated. can you clear me on this. how app flow should be once you get the access token and on which global event i have to work on.
Thanks
Hello Manish,
If I understood your correctly, In my case where I’m using AngularJS as front end app, I’m storing flag named “IsAuth” along with the access token I’v received on HTML5 local storage. Based on this flag I control UI elements only so I can show/hide links tabc etc…So I believe you need to do this manually too in your MVC app. One question here: Your MVC app and Web API are living on same project? If this is the case then User.IsAuthenticated property should be set and you can control UI elements. Hope this answers your question.
so how do u access the role value added to the claim from angularjs?
You can return the role value along with the “/token” response and you’ll have it in your front end app. You might need it to control UI elements only but do not depend on this value for doing any kind of authorization on the JS application; anyone can tamper with this value using developer tools.
i saw in your example you doing this but the token is encoded and so i don’t see how you can extract the role from the token?
Hi Marco,
I meant to return it in the response not in the token it self, so it will be on the same level as fields (expires_in, userName, token_type, etc..) if you couldn’t do it let me know and will prepare gist for you showing this.
Thanks for this awesome set of articles! I am yet to discover how to set the encryption keys, encrytpion algorithm and signature algorithm for bearer tokens manually. Seems like the only option is by setting the machinekey node in the web.config file. The idea is to be able to read these settings from a custom location in order to be able to deploy multiple instances of the same application. Any chance you know how to do this? I have searched everywhere and no luck. Thanks in advance for your help.
Hi Mario, glad you liked the series.
Well if you have control on your web farm; then I’ll unify the encryption/decryption keys on machine.config level not web.config.
You can try to use middle-ware “Microsoft.Owin.Security.Jwt” where I believe you can set your keys manually, not sure about this but need to read more about it.
Let me know if you find out how to do this 🙂
Thanks for the lovely tutorial, Can you get the Part 1 running again, the link is not working.
You welcome, thanks for your note, there is plugin for caching I’m using on my blog and it seems it is not working properly! Link is working again.
Hei Taiseer,
First of all huge thanks for this. This was “the” article that helped me get most 🙂 Have you any suggestions (ideas/ links) on how I can get my front web which is an MVC5 app to connect to the WebApi from part 1. To be concrete I like to use MVC5 frontweb that does same job as your angular project here. Any help is much appreciated.
Regards
Gustav
Hi Gustav, glad that it was useful,
Are u planing to use the API to support different applications or just your MVC 5 website? If just you have the MVC app then you can use the MVC5 template with individual accounts which contains all this features and much more such as 2FA and email confirmations. I find asp.net/identity is good place to check too. Hood luck in implementing the project!
Hello Taiseer
Thanks for the wonderful article, I was able to implement the same and everthing works like a charm. But currently I am bit confused and need help.
Instead of implementing the Web Api with an angularjs app I integrated the same with MVC app. Now my query is after authenticating and getting the token where should I store the token in cookie or session so that for subsequent request I can use that.
You are welcome, glad it was useful,
It is up to you where to store it, both should work, but If I were you I will store it in Cookie, as well you can store it in HTML5 local storage and access it via JS.
Hi Taiseer,
I’m new to API world… I download your application … setup it on local… and started exploring it…. everything working as expected… when i start Fiddler in parallel (so that i can record all activity) .. Login go to orders… logout… and then if i rerun (Replay> Reissue Request) orders in fiddler it gives me the orders… where as this is fired after user is logged off…
So if any browser the authentication bearer token is set… it can pull any information… correct? (even after logout)… unless the claims/or token gets expired.
Is there any way to avoid this?
Hi Mark,
Well there is no “direct” way to revoke granted access tokens or do “logoff”. if the user has the token then he can access the secured server resources until the token is expired.
So in some situations it is better to use the refresh tokens along with the access token. So you issue short lived access token (30) mins and you use the refresh token to obtain new access tokens. The nice thing here that refresh tokens can be revoked from the backend system so there is control on them,
You can read part 3 which talks about refresh tokens, and you can see how I manage to do the logoff in AngularJS application on part 2.
Hope this answers your question.
Hi Taiseer,
I hope my comment and question does not come through twice, I did try comment from my phone, but it died, and I assumed it did not go through as I don’t see it here.
Before I ask a question, I just would like to say thanks. for this brilliant article, and by the looks of things, many people do indeed feel the same. Certainly a job well done.
I have implemented your concept into a web application that I am currently writing. While both parts in in our control, our API and UI does not reside on the same server. We have mainly done this so we could scale the various components in our system independently from each other. So your post did allow me to tie up a few loose ends, specially regarding bearer tokens.
While everything works exactly as expected, I have one issue I was hoping you could help me with. I have just started integrating SignalR into the Angular application, I am sure you know that while SignalR does support various methods of performing its server connection, I would like that the “primary” mode does work. Websockets. The issue I am having is that with websockets we cannot attach the authorization header as we do for example with our api requests. So I have attached the token in the query string object exposed by SignalR. I started out by creating a new class implementing the SignalR Authorize attribute. In the AuthorizeHubConnection method, I can successfully get the token from the incoming request.
It is now at this point where I am fairly stuck. From a bit of research I can see that I do need to set request.Enviroment[“server.User”] = new ClaimsPrinciple(ticket.identity);
The ultimate action part of my question would be, once I have the token in string format, how do I now get a AuthenticationTicket from this string and then pass that to the ClaimsPrinciple constructor?
Regards
Louis
Hi Louis,
You are welcome and really glad that my posts were helpful in implementing your “exciting” solution, I know about SingalR but never done serious development with it yet.
To understand what you want to achieve here clearly; you have the encrypted/signed token on the back-end (AuthorizeHubConnection) method and you want to find a way to de-serialize it in order to build Authentication ticket and get identity/claims from this token string, is this right? If this is the case; currently I’ve no clear answer and I need to investigate it more. As I told you I’m not familiar with SignalR but I’ll try to find out something helpful.
If you were able to find a solution for this please share it with us here.
Hi Taiseer,
I have just got everything working. I would love to share this with people out there. I searched the net and could not find a solution and in turn just knuckled down and did some real coding for a change and invent stuff 🙂
Could I possibly email the details through to you and you perhaps add it as a follow up on your post?
that way people looking to implement any of these items will find all the related information together in one place?
You can dm on Twitter @Louis_lewis
Regards
Louis
Sounds great, followed you on twitter, you can email the details using the contact page on my blog.
Hi there. Did you happen to get the SignalR details Louis Mentioned?
Awesome posts. Super clear and amazingly helpful. Thanks so much.
Hi, yes I’ve then on my email sent by Louis, I’ll share it as comment later at night.
Hi Rich,
Our friend Louis has forked the repo and integrated SignalR, you can check his work here
Thanks Louis again for your help!
i try this everyting work but error log not showing on chrome..ie works file
for example , when i try login with wrong password ie tell me i put wrong script but chrome not…
do you have any idea?
Hi Brian,
Use the developer tools to debug the issue (F12) I can’t trouble shoot this.
sorry..i just test it again it works.
Thanks for the great article.
A basic (maybe unrelated) question: why are you using the data- directives instead of ng- (as in data-ng-view instead of ng-view).
Thanks!
this not work with ie
Hi Alex,
What is exactly not working? What is your version for IE? Should be IE9 and higher and this solution has been tested on all the major browsers including IE.
Adding my gratitude to the long list of similar comments. Excellent article and almost a perfect match for my current app project. You just saved me a couple of days researching/ developing!
Thanks!
Sounds great, really glad that this series of posts helped you 🙂 Let me know if you need further help.
Hi
Great article and really helpful. I’m running my project with this setup and it works really fine. However if I only have the “Authorize” attribute on the api, I’m able to get access to my views and only the api would be able to reject me. How would you make use of the auth server on the router lever? Basically it could be wether a view/route should be available public or private (authenticated users)
Best regards
Rasmus
Hi Rasmus, glad to hear that code is working perfectly on production environment.
Well I’ll direct you to this link where you will have clear idea on how you can listen to the event “routeChangeStart” and check if this route can be accessed anonymously or it is only available for logged in users (with token).
Let me know if this help in checking it on the Angular router service.
Hi,
Thank you for your great effort and this beautiful sample , i could use your sample and it is useful for me, but i have a problem , I’ve updated the solution with nuget package manager and the version of owin changed to 3.0.0 and the version of asp.net web api became to 2.2 , then i always get an error access_denied after logging in google when i use external login , i want to know if you have checked your sample with the latest version of packages or not , and is there any solution for me
You are welcome Masoud, always happy to help 🙂
Yes I’ve been told this but really didn’t have the time to investigate what happened to OAuth.Google library. I’ll check this during weekend and will keep you updated. If you find solution please share it with us.
Hi Taiseer,
Very good blog. It helps me so much.
I have one question: Now, I apply your method to my personal project (not follow exactly your instructions).
I applied:
– ShellPage
– authService / authInterceptorService
– home / index / login controller
And each time I press reload the page, the Top Menu do not display:
– Welcome …
– Logout
Do you have any idea?
Hi Trung, glad it was useful 🙂
Well I believe that you forgot to call app.run method in your app.js file which is responsible to fill this data once the shell page is loaded, check the highlighted code here. If this didn’t work make sure you are using the “indexController” correctly on the shell page which is responsible for these feature.
Cool. It works!
Thank you so much (y)
I would very much like to add role based security – so that I can not only use [Authorize] but also [Authorize (Roles = “bla”)]. I also would like to have a these roles and a user id returned to the ‘authService’.login method. Any suggestions?
Hi,
For question 1: You can set the roles manually in method “GrantResourceOwnerCredentials” after you validate the user credentials like this identity.AddClaim(new Claim(ClaimTypes.Role, “Admin”));
The other option is when you register the user so you can assign it to a certain role using UserManager.AddToRoleAsync(currentUser.Id, “Admin”);
For question 2: you need to add the below methods in class “SimpleAuthorizationServerProvider”: data = new Dictionary
private AuthenticationProperties CreateProperties(OAuthGrantResourceOwnerCredentialsContext context)
{
IDictionary
{
{ “userName”, context.UserName},
{ “role”, “pass your role”}
};
return new AuthenticationProperties(data);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context) property in context.Properties.Dictionary)
{
foreach (KeyValuePair
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult
Hope this answers your question.
It does. new Claim(ClaimTypes.Role, “Admin”)); was what I was looking for. Thank you
CreateProperties is never called. Where should I call it? and return Task.FromResult(null); does not compile.
You call it as the below inside GrantResourceOwnerCredentials
var props = CreateProperties(context);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, props);
context.Validated(ticket);
Make sure you are importing System.Threading.Tasks
YEAH! It works! TokenEndpoint override shoulde be:
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult(null);
}
this was awesome. Thank you for the interesting article and the clean demo code
My pleasure, always happy to help 🙂 Subscribe to keep tuned with new posts.
Great Article. I’m wondering how you would implement forgot/reset password. Typically I would create a token in a reset table and send token (guid) in an email to the account if found. They then would need to go to that link and they could reset the password. I’m just not sure how that would work with this approach.
I’m also wondering why you do not redirect to the Orders page after SignUp.
One little remark.
In authService.js in _login function you must add:
if (_authentication.isAuth) {
_logOut();
}
Just try without it to login with one credentials and, without logout, again login with another credentials.
PS: Sorry for English, I’m from Russia…
Thanks Vladimir for the tip, you are correct we have to log out before login again otherwise it will throw a 400 error.
Hello Taiseer,
I have one suggestion to the Angular app. In your code you add Authorization header to each XHR request. It could be much better to attach the bearer token only to those requests which are addressed to the RESTful service. Otherwise, unnecessary data are transfered with each HTML request.
Hi Aibol,
I agree with you, it is better just to send the Authz header only when needed, no need to transmit this extra 1 KB with each call.
Hi Taiseer,
Thanks for this good article.
A few things to notice :
1- All your code controllers/services/etc needs to be wrapped by an Immediately-Invoked Function Expression (IIFE) to avoid polluting the global environment, so something like this
(function(){
‘use strict’;
/* Your code goes here */
}());
2- Just make sure that all your controllers/services are min safe by adding the injecting services to the string array before you define your function.
Thanks again for your nice work.
Omar
Also, have a look to my article in code project about Web API 2 and AngularJS. you can see how I configured $httpProvider in app.js file to encode all my requests to the server to be url encoded.
http://www.codeproject.com/Articles/742532/Using-Web-API-Individual-User-Account-plus-CORS-En
Thanks,
Omar
Thanks Omar, definitely will take a look.
Hi Omar,
Thanks for taking the time to read and comment on the article.
I agree with you regarding first point, but for the second point I believe they already min safe, check it here
Hi Taiseer,
Yes, The majority are min safe, but some are not like : “app.config(function ($routeProvider) {“.
Thanks,
Omar
Hi Taiseer,
Thanks for a wonderful series on SPA, webapi, token auth and angular. How can we implement remember me functionality when a user signs in
You welcome, I guess you need to incorporate this with refresh token concept, the remember me period will be the same as refresh token validity.
Hi Taiseer,
First of all thanks for writing such good articles on Token Based Authentication. I am facing some issues while implementing SimpleAuthorizationServerProvider without refresh token.
– Client is not logged out when browser closed.
– Also access token is not getting expired.
Could you please guide me where I am doing wrong.
You welcome.
You are not doing anything wrong, read part 1 carefully and notice that bearer tokens are not revokable, so I didn’t specify in the post that closing browser will remove token or log you out.
Hi Taiseer,
I have a quick question: Can we implement csrf antiforgerytoken? It’s possible ?
Thanks
Hi Quan,
We are building a stateless API and securing it using bearer tokens, there is no cookies involved which is transmitted by the browser with every request, so I believe there is no way and no need (do your exercise and validate this please) to prevent against CSRF attacks, please read this SE question and this one. Let me know what do you think too.
I got it . Thanks for your links.
Thanks for this great article! It was very useful, and I’ve subscribed to your blog for updates.
I’ve run into a problem: I tried adding a simple orders.html view to display the Orders generated by the API, like so:
# Orders: {{orders.length}}
{{order.orderID}}: {{order.customerName}} – {{order.shipperCity}}
and added an ordersController.js:
‘use strict’;
app.controller(‘ordersController’, [‘$scope’, ‘ordersService’, function ($scope, ordersService) {
$scope.orders = ordersService.getOrders();
}]);
The API gets called and returns the canned orders JSON from part 1 of your tutorial. But the data doesn’t get assigned to the $scope.orders variable.
What am I doing wrong? Thanks.
Well the HTML I included all got removed, but I think you get the idea. It just had an ng-repeat=”order in orders” on an LI element.
Hi Doug, Thanks for subscribing 🙂
Well I believe that there is issue in your current code, you can’t call $scope.orders = ordersService.getOrders() directly, getOrders method return a promise and you need to resolve it in the controller, check how I’m doing this in the repository here.
Hope this answers your question.
Hi, this really is an excellent article. I’ve built a few websites using a ASP MVC so have built MVC controllers which AngularJS has consumed data from.
For my next application I was thinking about using ASP.NET Web API’s with AngularJS, where angular would get all it’s data from the API.
Is this a good idea/architecture? OR should I stick with MVC and just write ‘proper’ WebAPIs for parts of the application I want to expose to other systems….?
Many thanks
PJ
It depends on the application you are building, sometimes there is no need to build full SPA for each view, you can mix MVC views with SPA, recommend to read this nice post by Shwan.
Hi Taiseer,
I have read your tutorials about token based authentication. I think It’s very cool . And, I try apply it for my project . I have some questions:
I build app with different domains (multiple tenant) and using same authorize server . I think it same with google account . Example :
– Authorize : authorize.com
– example.com, user1.example.com
– abc.com, user1.abc.com
1. How to managed token and refresh token when it expired ?
2. User only login as the first time and auto redirect every we access app. How do that?
This is my ideal about sso and my questions . If you have any suggestion or ideal please let me know.
Thanks,
Quan Truong
Hi Quan,
Please find the answers “as for my understanding for your question because I don’t find it very clear”
1 – When the access token expires, you will receive 401 response from the server, then the client application will be responsible to try and issue new access token using the refresh_token identifier he has before (grant_type=refresh_token) if it works correctly he will recieve a new access token. Check Nikolaj implementation where I’ve reference to this in my post where he implements this automatically.
2 – This is something proprietary for your UI, once he logins and get the access token you can redirect him to any page/view you want and you store the access token locally so you start sending it with each request to any protected endpoint.
Hope this answers your questions.
I implemented your token authentication with angular js and asp.net identity backend; however, I couldn’t figure out how to implement confirm email with your system where it would send them an email to make them confirm that the email they entered was actually theirs. Is there a way to do that using your system?
Hi Avery,
If you following along with my post and you are using asp.net identity as your membership system then it is fairly easy to implement email confirmation once the user registers in your web site. I recommend you to check this detailed post which shows you step by step how to do it, it is using MVC5 but there is no difference if you want to implement in Web API, it uses Send Grid for sending the emails but you can use your own SMTP server for sure.
Hope this answers your question.
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
This is their lines in the code they say in that example. ApplicationUser doesn’t exist in the code you did.
Yup I know, you need to re-factor my code little bit to work with this, to get started I recommend you to start a new project using VS 2013 so you can borrow some code from the template (Web API) with Individual accounts selected as this image.
I would still be able to get token authorization using some of that template?
Sure, as I told you add a new VS 2013 Web API template with Individual accounts, get the code needed for sending the confirmation email and add it to your Owin API.
I’m having a problem:
I just setted the code below to allow roles to allow the roles admin, and have added my user to the role Admin and added the role to the database.
Now when im trying to go to /tokens it says everytime Unauthorized.
Already tried to allow lazyloading, not worked…
Code:
[Authorize(Roles = “Admin”)]
[Route(“”)]
public IHttpActionResult Get()
{
return Ok(_repo.GetAllRefreshTokens());
}
[Authorize(Roles = “Admin”)]
[AllowAnonymous]
[Route(“”)]
public async Task Delete(string tokenId)
Hi Lana, just answered the same question please check it here. As well you should not receive 401 when you call the “/token” end point it is unprotected endpoint.
Let me know if you need further help!
Hi!
I’m using this exempla with ionicframework.com and the only “problem” i get is i can’t hold the session.
everytime i close the app, when i open they redirect to login page…
I increase the login time on API, but nothing, i miss something?
Thanks!
Hi Daniel,
Just to clarify there is no session between your hybrid mobile app and your API, your problem resides in not storing the token in HTML5 localStorage when you obtain the token from the Authorization server, there is nothing to do with increasing the token time.
Make sure that you store the token locally on your app and you are sending it in the Authorization header for each request to a the API protected end points. That’s the areas you need to investigate and make sure they are working correctly in your hybrid mobile app.
Heya! Thanks for rapid reply!
I’m definitely storing the token on localStorage, but now I wonder if he really persists when app closes… I will check.
By the way, thanks for the excellent job
Its Work!
The problem was in redirect to /refresh
Thank you!
I’m using Internet Explorer in testing this application but I can’t Login nor Sign up and i use this one to Google Chrome and no error happened. I used it in my Website and same problem with IE it cannot Login. Please help me what is the problem. It showed me an error “unsupported_grant_type”.. Note: Functional in anybrowser except Internet Explorer. What will I do to fix this problem?
What IE version you are using? It should work on IE9 and higher.
I am using IE 11, I am still getting the error “unsupported_grant_type”
But the site login works perfectly in Chrome and Firefox. What could possibly be the issue?
I didn’t test it with IE 11, but when you open the developer tools console what is the error appearing? Is it pre-flight request missing? Can you try to use IE 10 standards and compatibly mode and try again?
How do you suggest debugging this locally? For serviceBase should we assign it the iis express url such as: var serviceBase = ‘http://localhost:49803/’;, or should we just put everything in one project so we can use a relative URL? Is there a better approach?
You can debug it while the projects are separated, if you need to debug the JS files you can use IE and set breakpoints, or you can use Google Chrome and set break points using developer tools.
What I mean is, we have to replace this line:
var serviceBase = ‘http://ngauthenticationapi.azurewebsites.net/’;
when debugging locally. What should we replace it with? It it’s just one project we can use a relative URL instead of an absolute one. What was the approach you took when debugging before deploying it to azure? Did you have the iis express URL hardcoded and then replace it before deploying to production?
Thanks!
Hey i’m also curious about this, i finished part 2 of this great article and want to debug it locally but when i assign serviceBase = ‘http://localhost:59423/’ (which is obviously different from my client-end localhost), I get all sorts of issues trying to post to http://localhost:59423/token…… what’s the best practice to debug a solution with decoupled backend and client-end projects??
Hi,
You have to allow insecurehttp when debugging locally.
In your Startup.cs ConfigureOAuth method:
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
#if DEBUG
AllowInsecureHttp = true, // Add this line for debugging only!
#endif
TokenEndpointPath = new PathString(“/token”),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(6),
…
};
great work. I am trying to find out where is the endpoint that responsible to validate the token posted to http://ngauthenticationapi.azurewebsites.net/token.
Thanks
Hi Tamer,
There is no endpoint for validating the token in this tutorial, if you need to implement one you might need to store the tokens in DB. What you want to verify in the token? The claims inside it and if it is expired or not?
Hi Taiseer,
Thank you for detailed explanations.
I faced the following problem:
When use Chrome i have to click twice the login button because the first time I get the following errors:
Failed to load resource: the server responded with a status of 400 (Bad Request) http://localhost:33484/token
XMLHttpRequest cannot load http://localhost:33484/token. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:16583’ is therefore not allowed access. The response had HTTP status code 400.
I have set the client allowed origin column to match my host: http://localhost:16583 and cors is enabled.
the second time all work and the user is authenticated.
ie and firefox work well.
Thank you!
paolo
Hi Paolo, that’s strange because it is working correctly at my end, can you try it on other chrome browsers? Maybe chrome for mobile. Let me know the results.
Hi Taiseer,
Thanks for rapid reply.
I have checked in other chrome browser (mobile and other computers) and the result is the same, sometimes work fine and sometimes the first time I click to login button I get un error.
For testing purpose I published the app, you may try to login,the test should be made more times opening and closing chrome.
http://www.paolocostantini.it => demouser/demo123
thanks
I’ve checked and it is working fine at my end using Google chrome version (39.0.2171.65 m). Maybe you are facing this issue when you are logged in and you try to login again. I remember there is fix for this in the comments, you can search for it.
I am having the same problem too, but I can’t find a fix for this in any of the comments in this series of blog posts.
Hi Rick,
Is it doing the same behavior on the live demo application? If it is the same then there is issue with your Chrome browser, if it worked correctly then you need to check your source code and make sure your are setting the correct value for the AllowedOrigin. You can put it as * to allow all origins.
Hope this help.
Try this. Log into the website, http://ngauthenticationweb.azurewebsites.net/#/login.
After login, you’re redirected to the orders page. Click on the ‘welcome user’ link in the header to go back home, and then click on the login button in the body. Try to log in again and you’ll see the error in IE 11 and Chrome 39.
Thanks for your detailed message, this is happening because you already logged in and your try to login again, try to call logOut function before you login again.
I took a bit of time off from this. Looking at this again with fresh eyes, I have an update. You have to configure CORS before you configure the bearer token. Once I did that, I am able to login in again, and again, without any errors.
https://aspnetwebstack.codeplex.com/discussions/467315
I was unable to login. In my case the solution was commenting the following line in SimpleAuthorizationServerProvider class:
//context.OwinContext.Response.Headers.Add(“Access-Control-Allow-Origin”, new [] {“*”});
I think it’s because Cors it’s already defined in Startup class:
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
Hi Rick, the line context.OwinContext.Response.Headers.Add(“Access-Control-Allow-Origin”, new [] {“*”}); is for enabling CORS for the “token” end point which is not part of the API (doesn’t inherit from “ApiController” class). If you are testing this from Rest Client not browser then it will work. But once you try it from browser it will fail. I do not know what is your exact issue but failing to login should not be related to CORS. If the issue still exists please elaborate more about it and hopefully I will be able to help.
Hi
After completing part 1 and 2 I face the exact same problem as Paolo. For IE and FF it works like a charm, for Chrome Version 39.0.2171.71 m the login gives CORS exception for the first try then it works the second time, and many consecutive times. I am not logged in or anything to cause this behavior.
I’m looking forward to see if there is an easy fix to this
Thank you
That is really strange, I’ve tested this on my local copy of the project as well on http://ngauthenticationweb.azurewebsites.net/ and it works with no errors. Are you facing the same error on live demo or just your local machine? If it is on your local machine please proceed to part 3, I’m afraid that blog post in part 2 is not sync with GitHub code repository.
Thank you for getting back to me. At the moment I don’t have time to implement part 3 but will do in the near future and get back to you. Also I will test on the live demo so I can have more information for you on this matter.
Hi,
I have question : How to store token across domains? Does it secure token when we shared token across domains?
Any ideal for this situation.
Thanks,
Quan
Hi Quan,
Currently I’m storing the token in HTML5 localStorage, you can’t share the same storage across domain, please check this answer here.
Good article! But I want to ask you about authorization. For example, about orders. When you try to load orders you check – user is authorized or not (if not authorized authInterceptor decide what need to do).
But what if could be another situation? For example, you just load view (form for add another order) and you don’t need to call any web api cause you don’t need to get any data from database. You just wanna get the view. BUT THIS VIEW MUST BE AVAILABLE ONLY FOR AUTHORIZED USERS. How this problem can be resolved?
Of course, when user try to submit this form his request will be rejected. But I need to disallow user to open any view which I want. For example, in user interface even couldn’t be a link which open this view but user can write this url directly in browser itself.
Sorry for my English. From Russia with love. =)
Hi,
This is mainly related to AngularJS and has nothing to do with the Web API, because this will be implemented on client side and any developer can turn off JS and have access to your views, this will help you to provide better user experience, you can check this post and this post to get started.
Hope this answers your question 🙂
thank you! sorry for late reply!
I get an error message that says “Sequence contains more than one element”, with the following exception.
at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable
1 source)
1.GetResult()at Microsoft.Owin.Security.AuthenticationManager.d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter
at System.Web.Http.HostAuthenticationFilter.d__0.MoveNext()
— End of stack trace from previous location where exception was thrown —
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at System.Web.Http.Controllers.AuthenticationFilterResult.d__0.MoveNext()
— End of stack trace from previous location where exception was thrown —
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()
The request returns the appropriate response if I remove the “Authorize” tag from my API controller and comment-out the push of the auth interceptor to $httpProvider i. However, it doesn’t work if put the auth interceptor back in. This suggests to me that there’s something wrong in my auth interceptor, but my code is almost identical to what is used in the blog.
The main difference is that my token is stored in a cookie instead of local storage.
Do you know what is going wrong?
That is strange, it is .NET runtime error which appears when you call .Single on IEnumerable object, to be honest I’ve no clear answer what is happening but check your Statrup.cs file again and make sure you have registered all the middle ware only one time and compare it with my code on Github.
Turns out the problem was an OAuth configuration issue. Fixed it with the solution in http://stackoverflow.com/questions/24978940/webapi-oauth-useoauthbearerauthentication-gives-sequence-contains-more-than-one
Thanks for sharing this.
I have completed Part 1 & 2 of the token authentication, but I get an error when I try to try to store the username and token locally. I even get the error when I download your code. What step am I missing? My browser does support local storage.
The error in the console reads…
ReferenceError: isUndefined is not defined
at Object.addToLocalStorage (http://localhost:39241/scripts/angular-local-storage.js:121:17)
at http://localhost:39241/app/services/authService.js:31:33
When debugging,the line in question is…
localStorageService.set(‘authorizationData’, { token: response.access_token,userName: loginData.userName });
That is strange, try to call the demo application and check if it works, if it failed then there is issue with your browser. No one reported this issue before.
I have the same issue
Will check what is the issue with this Google chrome version.
I had the same issue. You’ll need put the common.js file in your solution also, and then will be solved this issue.
Hi Lucas,
What is common.js file? I can’t recall having such file in this repo.
I’m getting the same error as well.
What common.js file are you talking about?
I see what Lucas is talking about..
Angular Local Storage has common.js
common.js is included when you download the angular-local-storage package.
Hi Brady, why you do not issue Pull request to fix this if there is an update on package angular-local-storage package. Thanks
How would you implement logging out on the server? Simply deleting the authorization key on the server doesn’t strike to me as the most secure thing to do.
There is no logout with tokens, they die after they expire. Issue short lived tokens. What AuthZ key you want to delete from the server?
I’d like to safely log out the user, meaning all issued tokens must be immediately revoked when the user decides to log out.
You need to store token identifiers then and check there exisestance in a custom Authorization filter. Check my Stack Overflow profile, I remember that I’ve answered relative question to yours, sorry I don’t have access to the question now.
Taiseer great post. I was wondering if you had an over view architectural diagram of this post? Thanks
Hi Ahmed, glad it was helpful. Unfortunately I do not have architectural diagram.
Good day,
Thank you so much for the tutorial, It’s very helpful.
Haven look at the Back end and play with Web API which went fine. i was trying to run the sample application that i downloaded from git. I set the Web as startup then run the app, on signing up i get no response http://prntscr.com/5eustn. I haven change anything from the source code. I notice that yesterday when i was working on another copy of the file trying to make it run locally, i change the API web config connection string and the app.js service base http://prntscr.com/5eutix, it worked on my local system, but now i cant even get the new file working.
Please can you help me figure out if there is any other configuration that i need to work on or fix.
Thanks
Hi,
your issue is with CORS, complete to next part to see how you need to configure OAuth clients and set the allowed URI for clients correctly, The live application you see uses the same code in Github, so any failure most probably will be related to miss configuration at your local copy.
Hope this helps.
I cannot load the module, I get the following error:
http://errors.angularjs.org/1.2.16/$injector/modulerr?p0=AngularAuthApp&p1=…gleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.2.16%2Fangular.min.js%3A32%3A445)
Syntax error, download the repo and compare your files with the downloaded code. The love demo application code is the exact code in Github, so it should b working perfectly.
I encountered the exact same issue but I was able to get better diagnostic information by taking the .min out of the reference to angular.min.js. Then it told me exactly what the problem was. While your problems may be different mine were that I needed include the two files under the “scripts” folder mentioned in step 2 above. Do as Taiseer recommends and grab these files from Github.
Thanks for your comment, maybe there are some changes in the latest AngularJS version I’m not aware of, if you used the lates version and fixed an issue please share it here.
You ROCK!!! Thanks a bunch for this tutorial. It’s a perfect mix of technologies.
Glad you liked it Brian, subscribe to keep tuned 🙂
I have the same issue.. .What do you mean by the common.js file?
Hello Taiseer,
First of all, thank you for your great tutorial. It is what I was been looking for many months.
I downloaded your demo project from codeproject and without any modification, I get the following error:
XMLHttpRequest cannot load http://localhost/MyApp/token. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:32150’ is therefore not allowed access. The response had HTTP status code 400.
It see that similar problems have been commented in the forum but I cannot find the reason of the problem.
It only happens when I run the app under local IIS. Using IIS Express, everything works like a charm.
I am not using refresh tokens and it is not related with the Client table records since it happens also when I enter a non-existing username in the login screen.
Thank you
Hi Dabiel,
Can you try to set the allowedOrigin value to “*” in this highlighted LOC and this LOC too so we allow any origin. In my case it is working on both versions of IIS, local and IIS express.
Let me know if this helps.
Same result, In any case, debugging it, allowedOrigin was null and then your code assigned ‘*’
Thank you.
I tried to do what I explain in the article in a Multi-Device-Hybrid App, but it does not work ..
Does not see the sights in the tag NG-View!
I would like to manage the login from a Multi-Device-Hybrid App with AngulaeJS.
Can you suggest anything?
not use Ionic Framework!
BR
Hi francesco,
What is the issue you exactly facing, maybe it is related to localStorage so the token you obtain is not stored correctly. Please elaborate more to help.
I used the example to login in a Multi-Device-Hybrid app. The code compiles correctly and in the emulator displays well. When I run the app in the device, the app does not Displayed all those who should be in “ng-view.” Not to be no errors and displays it enough! .. Should I use different components to display the content?
I have tested by creating an app from scratch and creating routing rules similar to the following code:
app.js
(function () {
‘use strict’;
var meApp = angular.module(‘meApp’, [‘ngRoute’]);
meApp.config([‘$routeProvider’, function ($routeProvider) {
$routeProvider.
when(‘/’, {
templateUrl: ‘views/home/home.html’,
controller: ‘homeCtrl’
});
}])
meApp.config([‘$routeProvider’, function ($routeProvider) {
$routeProvider.
when(‘/map’, {
templateUrl: ‘views/map/homeMap.html’,
controller: ‘mapCtrl’
});
}])
meApp.controller(‘homeCtrl’, function ($scope) {
$scope.model = {
message: “Sei in Home”
}
})
meApp.controller(‘mapCtrl’, function ($scope) {
$scope.model = {
message: “Sei in Map”
}
})
})()
index.html
Title
<!–Go to home–>
I would like to make this login system within a mobile device.
br
That is strange, maybe you need to change how your store the token data on the mobile, I’ve no experience in building mobile apps so I’ve no recommendation.
I continue getting the following error:
“XMLHttpRequest cannot load https://myweb.com:4001/token. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:32150’ is therefore not allowed access. The response had HTTP status code 404.”
It works fine in my development PC, in local IIS and also in IIS Express, but it fails when I upload to the production server. The main difference is the https instead of http, but I do not think it is the problem.
I followed your steps, in fact, for now I do not use refresh token, only the normal token, and within GrantResourceOwnerCredentials, I use:
context.OwinContext.Response.Headers.Add(“Access-Control-Allow-Origin”, new[] { “*” });
Using Fiddler, I can see the difference in the returned header, The https server returns:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost:32150
but the local IIS returns
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
I have no idea about the reason. Please, could you give me clue about it?
Thank you
Hi Dabiel, you need to make sure that you are setting the correct value for the response header “Access-Control-Allow-Origin” in the server to match your front-end full domain, in my tutorial you need to set this “GrantResourceOwnerCredentials” as you did, but is your front end using https too or http? I can from the response it is http. If it is https then tehy both should match.
As well double check the order of registering Owin component in Startup class, they should be as this. Can you make sure that you are using the same version of of Owin and Owin.Cors(2.1.0) as the blog post.
Great post. Just a minor point of feedback that the authInterceptorService seems to always read from local storage. You actually do have in memory state for the auth credentials with you load into memory with fillAuthData. However this in memory state is never used and you are always reading from local storage in the interceptor service. Might be a small optimization there. Great work!
Hi Taqi, the fullAuthData used to fill an object in the authService which runs only once when you run your application, this object is needed to decide if the user is logged in previously and mange the UI state, i.e. show/hide login link, display welcome {{userName}}, etc..
Taiseer, we exchanged tweets, so I’m adding my question here to your blog as requested. I’ve got a requirement where upon sign in, the user needs to be shown a screen with a dropdown to select a “tenant” context before landing on home page. Users can be associated to many tenants, and their roles are different based on which tenant they’ve selected upon login.
The tenant selection dropdown must be security trimmed and only show tenants that have been associated with the user in the DB (similar to the Azure mgmt portal, except you can’t change tenant context in the same login session, you’d have to logout and log back in….this last bit is not a business req).
I wanted to leverage the authentication ticket and add a claim with the selected tenant id into the bearer token so that it functions very much like a role upon login….except the tenant selection is done after the user has logged in and the token has been created.
I’ve managed to implement what i need using cookies auth and mvc razor views for the login and tenant selection pages, and then making the home landing page be the entry point of the angular app, not the entire HTML. But now I am in the processing of modifying the authentication to use tokens instead, as I don’t want the risk of CSRF from the angular app.
I’ve got a bit of a “chicken vs egg” problem where I can’t security trim the tenant dropdown unless the user has been authenticated, but I also want to store the selected tenant in the auth bearer token as a claim. However by that point the token has already been created. The reason for me wanting to add the selection as a claim is so that it makes it easy for me to retrieve and use it on the server side, since the middleware is already decrypting and parsing out the claims from the token with every request. If I don’t leverage it, then I’ll have to implement a similar mechanism in parallel, just to grab that tenant. By grabbing it out of the token’s claims I trust that it was selected in a secure fashion, not just shoved into the request – kinda like a tampering of the request.
Hi, thanks for your comment and apologies for the late reply.
To be honest I’ve been trying to find solution for your scenario and each time I write something I come and delete it, it is not a simple use case, I’m thinking of creating custom authentication filter to protect the tenant end point and you issue temp access token (very short lived 1 min [enough for the user to select a tenant]), and after the user select tenant you simulate the login process and issue another access token.
I’m not sure if this will work 🙁
I’ve run thru a dozen different scenarios, and each time I’d run through one, as you likely did when writing this reply, I’d find a flaw and start over.
As you described, the only problem I see with issuing a short lived token is that it assumes the user won’t step away from the screen between logging in and selecting the tenant. I will also probably have to write logic in the angular app to make sure that if a user has authenticated but the tenant ID property is not stored in the authentication data (localStorage), that they cannot use the app and take them to the tenant selection view.
What you described is very similar to what I’ve got now without tokens, but rather with cookies and the razor views. I log them in, then they select a tenant, I sign them out and then sign them in again but this time I add the tenant ID as a claim in the auth ticket.
Thanks for taking some time to think thru this with me. I’ll spin some wheels and see what I come up with. This may merit a blog post if it succeeds…If you’re ever in the Dallas,Tx area, ping me and I owe you lunch 🙂
You are welcome my friend, and thanks for the invitation:) once you find permanent solution and you are happy about it, please share it here. Thank you and good luck!
@tafs7
I’m curious, what was the final architecture you decided on?
I am in a similar situation, wanting to offer tenant selection.
I am following this from scratch (not using github source demo). I don’t see where you specify the source of the LocalStorage module, it just exists in your project in the first screenshots. I have found two different ones via Google but neither seems to be the one you are using. I realize that I can get it from your github but I’d like to know the original source of the module.
Thanks for a great set of tutorials. These are very well done and extremely helpful!
Hi Justin, You are welcome 🙂
I’m using the Angular Local Storage Module and here is the one I’m using in this project. Be careful that there might be changes with local storage API if you grabbed the latest one from CDN or Github. Recommend you to use the same version I used in this tutorial if you want to follow along without any issues. Good luck.
I had some dependency hell issues with NuGet – it wouldn’t pull your specified version. So I’m already rolling the dice using latest versions. Even the latest versions of Owin claim to reference v3.x but actually load 2.x which compiles but immediately throws runtime errors. You have to specifically install the latest Owin sub dependencies that have the wrong version numbers via NuGet. Then everything works (but it’s a newer version than yours).
I’m competent with Angular, MVC and WebAPI. I’m new to Asp.NET Identity, OWIN, OAuth2 and Cors so those are the real areas of learning here for me. Ultimately I want to end up with three application components: an OWIN/OAuth2 User Auth server that can function like a really light SSO, a WebAPI that uses tokens to authorize access to specific endpoints, and an Angular SPA that gets the token from Auth and resources from API.
This tutorial is an excellent starting point.
very impressed with the interceptor idea,i like it.
Thanks for the post!
Very comprehensible!
You are welcome Thomas, glad it was useful 🙂
Hi Taiseer,
I’m using refresh tokens in my web api and also my front end is using angularJS, when I request some data to the web api and I’m not authenticated I received a 401 code response and I request a new token and try to make the previous request again with the new token, the main problem here is when I get a 401 code for a sencond time I enter in an infinite loop because even if I’m authorized but if I dont have the permissions to access the uri I will received a 401 code and the interceptor will keep trying to refresh the token and retry the request.
if (rejection.status === 401) {
var authentication = $injector.get(‘authentication’);
authentication.refreshToken().then(function (response) {
_retryHttpRequest(rejection.config, deferred);
}, function () {
authentication.logOut();
$injector.get(‘$state’).go(“page.login”);
deferred.reject(rejection);
});
}
I read here: http://www.codeproject.com/Articles/655086/Handling-authentication-specific-issues-for-AJAX-c that you can overwrite the AuthorizeAttribute filter and the provide a custom error code for both cases
401 code for unauthorized requests
403 code for forbidden requests
Do you think this is the rigth way to do it? or do you have any suggestion to acompplish this issue?
Thanks,
Hi Alex, could you please check this form of my repo, Nikolaj should implemented the automatic refresh token successfully, check it here
Hello Tayseer, GREAT Tutorial,, really life saver..
BUT I have a problem..
the web API is working great,, the signup,, registration views are working perfectly…
my problem is with the orders page… or in my demo I called it Projects.. the projects controller is working fine I tested it using Postman,, with authorization and using the token,, it works fine,, but when using on the page it gives me error
“OPTIONS http://localhost:6065/api/projects net::ERR_CONNECTION_REFUSED”.
The Interceptor is working fine,, it gets the authentication token from the local storage… and save it into the header object.. after that I couldn’t debug..
I am lost and I don’t know what to do,.. please help
here is the code for my controller :
‘use strict’;
app.controller(‘projectsController’, [‘$scope’, ‘projectsService’, function ($scope, projectsService) {
$scope.projects = [];
projectsService.getProjects().then(function (results) {
$scope.projects = results.data;
}, function (error) {
//alert(error.data.message);
});
}]);
and this is the code for my service…
‘use strict’;
app.factory(‘projectsService’, [‘$http’, ‘ngAuthSettings’, function ($http, ngAuthSettings) {
var serviceBase = ngAuthSettings.apiServiceBaseUri;
var projectsServiceFactory = {};
var _getOrders = function () {
return $http.get(serviceBase + ‘api/projects’).then(function (results) {
return results;
});
};
projectsServiceFactory.getProjects = _getOrders;
return projectsServiceFactory;
}]);
BY the way I just checked the call from my webpage through fiddler,, It doesn’t attache the authorization token.. WHY is that :)??
Thanks Mohammad for your message, If it is working on fiddler and not working from web browser then I suspect you didn’t configure CORS correctly. Make sure you are not receiving 405 response. There should preflighted request to the API which sets the Access-Control-Allow-Origin header. Hops this solves the issue.
If you configured CORS correctly please check this link maybe it is related to chrome and accessing local storage
Thanks Taiseer for the reply,, now I am sure it is a CORS issue,, I checked it in IE and Firefox, firefox tool mentioned that it is a CORS issue.. I’ve configured the CORS in the startap class as you mentioned in the Tutorial, I wonder if there is any further configuration there.. check my startap class:
using AprilIT.ProjectManager.API.Providers;
using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
[assembly: OwinStartup(typeof(AprilIT.ProjectManager.API.Startup))]
namespace AprilIT.ProjectManager.API
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString(“/token”),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
}
Why it is causing a problem only when loading the Projects class, why it is not doing the same for the Token class???
again thanks for your help…
Ok,, here is my last investigation…
when runing the registration page using view in browser with out running the web API project it gives me cross origin error… BUT when I run the web api project, then view the client website in the browser the registration works fine, it transfers me to the login page.. I login successfully,,., BUT again when I go to the projects /orders page it is simply give me the error of CORS is not enabled.. I am literally losing my mind here…
For the reference,, I deleted all the work,, remade the project,,then used CORS 5.2.2.. using this article http://goo.gl/oxEZfG and everything went fine.. THANKS again for your help… You r the best
Great, finally it is working 🙂
Hello Everyone,
Please i would appreciate any urgent answer/help on the above subject. Actually, am about to start working on a project that involves developing a website where registered employees (on a remote desktop application) can come and view their basic information.
I understand i need to make some API calls that would fetch the data from the remote desktop database and render it on html for the users.
Please can anyone provide me with useful tip/tutorial/guide/reference on how to start?
Thanks in advance.
Check my archive posts where they can teach you how to build HTTP Service (Web API) from scratch.
Hello Tayseer,
I have a small question… I am using Parsley validation with my forms,,, and I am using Parsley remote to validate using ajax to check if user exists in the database already… it has a nice way to give a url and check the status code…if not 404 then the user is already existed and you cannot use it….
the issue here.. if the AccountController is Authenticated,, and I want only authorized users to use it… it works fine when using ngresource and in the service it will just be called by the interceptor to modify the headers… BUT out side the service it doesn’t use the interceptor..
In other words my validation method is in the Controller.js file.. it doesn’t use a service.. and the interceptor is not being called…. HOW can I make the interceptor work outside the service…this code shows the validation method inside my controller
window.ParsleyExtend = {
asyncValidators: {
checkusername: {
fn: function (xhr) {
return 404 === xhr.status;
},
url: authService.serviceBase + ‘api/account/CheckUserName/’ + $(‘[name=”userName”]’).text()
},
checkemail: {
fn: function (xhr) {
// console.log($(‘[name=”email”]’)); // jQuery Object[ input[name=”q”] ]
return 404 === xhr.status;
},
url: authService.serviceBase + ‘api/account/CheckEmail/’ + $(‘[name=”email”]’).text()
}
}
};
any idea 🙂
Hello,
I am doing a website and for the authentication I followed your tutorial. I deleted the global.asax.cs and instead I used the startup.cs file. When I try to publish the file, the server is searching for the Global.asax therefor the publishing proccess fails.
What should I do in this case? Is there any solution or I should bring back the Global.asax.cs?
Thank you for the great tutorial. It is the best I have seen online
It is hard to troubleshoot the issue, but it seems there is code depends on Global.asax file. Is this MVC application or Web API? Did you start from scratch or used VS templates?
Its MVC application. I manage to push the project to the server by adding the Global file back in the project and it works fine. For now it’s OK but I am not sure if it is the best solution.
Let me know if you have any suggestion.
Thank you so much
Hi Taiseer,
Thanks for the great article.
I have scenario. Here I need to open a new tab/window, to open a new angular app (based on certain user input). On server side I have MVC controller route that will render my new app html view.
Problem is I am not able to authenticate. How can I pass the jwt in this post. The click handler in the current controller is
var link = ‘/App/Create/’ + a + ‘/’ + b;
window.open(link, ‘_blank’);
Please advise!
Thanks,
Savinder
Hi Taiseer, I kinda worked around it. The html for new angular app is allowed to be loaded unauthenticated. Then in a round trip the angular app, taking jwt from localstore, loads the state (authenticated).
I would appreciate your expert opinion on that.
Thanks,
Savinder
Hi..On execution of below code in authService.js,
$http.post(serviceBase + ‘token’, data, { headers: { ‘Content-Type’: ‘application/x-www-form-urlencoded’ } }).success(function (response)
I am getting below error,
POST http://localhost:12901/token 404 (Not Found)
Is token folder to be created in the project….? Can you please explain….?
Hi Setu,
Can you make sure that you sent the token edn point correctly in this LOC, 404 means you are hitting an unexisting endpoint.
Hi Taiseer,
I have a question for you about the fillAuthData mehod in the authService. When I call the method from run in the app.js file, I get an undefined error. If I removing the call to fileAuthData on run, I am able to call the register, login and logout methods successfully. But when I call the fillAuthData, I get an undefined error. I check the localStorage in chrome and the token is there. But I can’t read it. Do you have any idea why this would be?
Thanks
Tim
Hi Tim,
It is hard to debug it like this, have you tried to open developer console (F12) and watch the errors window? There might be an error with dependency injection.
var serviceBase = ‘http://ngauthenticationapi.azurewebsites.net/’;
here u r using this url instead if i try mine its not working,not showing error message too plz gimme suggestions
..
Make sure you configured CORS correctly and keep Chrome developer tools window opened and watch Console window for any errors.
I am new to WIF, Tokens, JWT, Authentication, Authorization, etc…
I was wondering….
If I only create intranet applications, I am already authenticated (and can just use id.IsAuthenticated) because I am logged in to my work PC, right.
Do you have a small sample code that skips all the log in and lets me assign new claims used by the current application, e.g., I get all the roles for a user from a database table (UserId, Role), or for userDelegation (UserId, DelegateOfId), etc. and assign each record to a claim. Front-end is AngularJS, back-end is WebApi 2 using EF6.
Google searches always results in having to maintain a UserStore and such things.
Currently I do not have sample code to cover your scenario, and you are correct if windows authentication is enabled on your API, then you are authenticated using your AD credentials.
If anybody else would find it useful, I am posting my version of the .Api and .Web projects that I got working.
Article: “AngularJS Token Authentication using ASP.NET Web API 2, Owin, and Identity” part 2
https://www.dropbox.com/s/il3zam0zq64tphn/AngularJSAuthentication-part2-solution.zip?dl=0
it includes all files, not just the CDM references. Remember to change the base addresses to your dev environment. I need to get rid of the magic strings and put them in the App.Config or something.
Thanks fir sharing the URL, I’m sure it will benefit someone.
Hi,
I have three applications with different api’s projects and diferrent ng-app’s, but hosted in the same IIS server (every API is deployed in a different application under IIS, but all are in the same domain/host).
Basically I have:
http://myserver/myapp/module1/ –> ng-app 1 accessing API 1
http://myserver/myapp/module2/ –> ng-app 2 accessing API 2
http://myserver/myapp/module3/ –> ng-app 3 accessing API 1 and 3
Is there any way to keep the authentication token between those API’s and the angular app’s?
Hello TAISEER,
Thanks for the great article. This article gave me more information on the AnguarJS and as well as in web api. I downloaded your project and tested in locally.
When I checked the login part you are calling token method for login the user. For accessing the url you are passing username & password. How can we do secure this one. I don’t want to see my password anyone.
http://tinypic.com/r/xbh4sh/8
Hi Ravi, glad you find it useful.
Well for the first time you need to present your username/password over HTTPs only to validate them then you get an access token, so your credentials are secured and encrypted when it’s over https.
Thanks a ton for a great article – from Hyderabad,india.
I was trying to debug this application with Katana Source (http://katanaproject.codeplex.com/SourceControl/latest#README) to learn underlying katana implementations, but keep failing with the build error
Error 3 The type ‘Microsoft.Owin.IOwinContext’ is defined in an assembly that is not referenced. You must add a reference to assembly ‘Microsoft.Owin, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’. \AngularJSAuthentication-master\AngularJSAuthentication.API\Controllers\AccountController.cs 28 19 AngularJSAuthentication.API
Though i mentioned in assembly redirection to latest microsoft.owin, i get this error.
What am I missing? kindly suggest.
I’m afraid that you are facing an issue with NuGet versions for those packages, make sure you are using the right versions in my project which match the versions of Katana project you want to debug.
It works great for me on the local IIS express 8, but when I uploaded to a hosting server with IIS 8.5 it I started getting 405s on PUT and DELETE, with no problems with POST or GET,
I have tryed a ton of thing in the web.config. Googling most people blame WebDav for this problem. I tryed removing it from the web.config and nothing.
If i remove it in the section then every request starts giving 405s, post, get,put and delete. When I tryed the remove name=WebDav in the section, nothing changed. PUT and DELETE only get me 405s.
Do you know any other solution for this problem?
Hi Samuel,
To be honest I have no answer to this, you were able to solve this issue?
Nope, I tryied a million different things in the web.config to get rid of the webdav module and nothing worked, I asked the server admin to disable it and got a NO for an answer, so I had to make do with what I had, and used things like:
[HttpGet]
[Route(“api/controller/{id}/delete”)]
public IHttpActionResult FakeDelete(int id)
{
return this.Delete(id);
}
to go to my put and delete methods in every controller, not a very elegant solution but it got the job done. I think I’ll have to use java next time I do a rest api on a server I don’t control.
Hi Taiseer,
it’s great article, thank you. I implemented this approach to my Project. I also use web api services from Windows forms.
I tried to use Microsoft.AspNet.WebApi.Client – HttpClient. Login request response “bad request” error. could you please give some sample code using Windows forms. Thank you.
Hi,
Could you please take a look on this post where I used HttpClient from console application, hope it will be useful.
Hello Taiseer,
Thank you very much for your post. It’s really helpful to implement our security architecture for new project.
I’m just confused with following thing:
In our case we have single authentication/authorization server, multiple clients (web applications (server side asp.net MVC), Client App (AngularJs)) and multiple resource servers.
And from one web application, we open another application in new window.
So my question is, can we share the access token between parent and child web app (passing token in querystring or something)? or should we do that or authenticate child app again with same authorization server?
Please reply soon.
Thanks in advance.
Darshan
One tip I’ve just come across using this fantastic tutorial – in the _login JS method, you should encode your params:
var data = “grant_type=password&username=” + loginData.userName + “&password=” + loginData.password;
should become:
var data = “grant_type=password&username=” + encodeURIComponent(loginData.userName) + “&password=” + encodeURIComponent(loginData.password);
Had a prob with authenticating a user with an ampersand in their password…
Otherwise, top job!
Thanks for the heads up, agree with you should be done Url encoding.
Hi! Thanks for the article!
I have implemented the above, but its responding very slow. Can you please let me know what could be the potential bottlenecks?
Thanks
Cant get your question..what do u mean it is slow? The first call? It is an issue with EF 6 Code First.. you can google it and see the tricks to overcome this issue.
I have just followed your article step by step and it works brilliantly.
I have trawled the internet and bought books looking for a straight forward answer and a good explanation to this problem. Your explanations are clear and to the point.
Have you ever thought of writing a book?
Many Thanks
John
Thanks for this nice message John 🙂 happy to hear that my posts are useful! To be honest no but why not, I might start by writing a mini e-book 🙂
Thanks Taiseer for the great series, really helpful one!
I have a couple of questions for you:
1. How to implement reference token? Can you point to some guidelines?
2. Authentication token persists in the browser event after closing it, which means the next hit to the site with a new browser window indicates that the user still logged in. How to solve this undesired behavior?
3. How to immediately revoke the authentication token if the user’s password been changed?
Thanks
I really like your coding pattern, it is really easy to understand of what your trying to achieve for each code block.
Is it possible if we can get a example of extending the authentication such as multiple user role / permission for each control using the ui router ?
Thank you 🙂
Hi Ekran, thanks for your message. I need to arrange for this in coming weeks but not promises soon as I’m very busy.
Thanks for your reply, looking forward to it.
Hi Taiseer,
Should I keep app.UseCookieAuthentication,app.UseTwoFactorSignInCookie, app.UseTwoFactorRememberBrowserCookie method calls? The first one tries to redirect me to /Account/Login so I disable it. What about the rest ones?
Thank you
You do not need to use cookies authentication with JS app running in a browser, so you can depend only on bearer tokens and you can disable those
Thanks a lot!
Hi Taiseer,
I’m encountering an issue in IE11 where the browser is overriding the Authorization header in my requests even though I am setting it via AngularJS.
Basically, I have an HTTP interceptor registered for all requests that looks like this:
AuthInterceptorService.request = function (config) {
config.headers.Authorization = “Bearer ” + bearerToken;
}
The first requests works fine with the Bearer , but after a while the Authorization changes to Negotiate (even though the interceptor adds the correct headers authorization).
I have my app set up in IIS as integrated windows authentication.
I would appreciate your help,
I’m stuck on this for weeks..
Thanks
Yael
Hi Yael,
So this issue is happening only on IE11, and it is working fine on Chrome? As well why you are using Windows Auth for this App? Can you try disabling it and enable only Anonymous access? You are already protected by bearer tokens so using windows auth makes no sense here.
Hi,
The App is working on Intranet. We use windows auth to identify the user
and determine his role.
The Tokens are used for securing application Api’s .
This is an Angular Js app, therefor access to Api’s is done directly from client computer (no session can be used). The idea was to avoid identifying the user on each call.
Could you please explain me in step 9 as the updated menu works after User login I’m not getting the update menu navigation that is required to change the css variable modicar for this condition and refresh the screen.
<li data-ng-hide = "! authentication.isAuth"
Hello Ederson,
Sure no problem, the navigation bar in the index.html page uses a controller named “indexController”, if you check the LOC https://github.com/tjoudeh/AngularJSAuthentication/blob/master/AngularJSAuthentication.Web/app/controllers/indexController.js#L9 you will notice that I’m passing the “authentication” object and checking if the user is authenticated or not, based on this I’m hiding/showing this list item. Hope this clarify your concern.
Hello Taiseer,
It turns out that even with this object authentication returning true with the logged User is not updating my navigation menu not know if you need to add som css for checked as another example to run the hide-ng. Add.ng-ng-hide-hide-add-active also noticed that you do not add angular.module (‘app’, [‘ngAnimate’]); veriquei as another example has some part of css or javascript more work is needed to update the navigation bar?
You do not need ngAnimate in my case, this module is used once you navigate between views to add transition effects. As well there is no need for any extra css classes, “ng-hide” is built-in AngularJS directive. Make sure you are setting the controller to the navigation section in HTML as this LOC: https://github.com/tjoudeh/AngularJSAuthentication/blob/master/AngularJSAuthentication.Web/index.html#L16
Yes I am configuring the controller for the navigation section in the HTML but still not updated if I press F5 appears menus mean that my variable is populated in the controller.
There’s something that is taken when clicked on login that makes updating the navigation menu?
Even indexController being outside the ng-view it updates itself with the changes in the scope of the controller?
This example will work if I upgrade the version of Angular v1.2.18?
Hi,
WONDERFUL work, thank you. All seems well except that navbar. The collapse button does not function at all when screen is small. NO expansion of menu items etc. Can you please post a working example of the navbar? Your menu looks nothing like any other example out there so I am confused. Then again, none of the examples posted on site work either. Everywhere I look the posts seem to say to use ui-bootstrap??????
I have same problem.. User menu is not updated..
Hello Marc, you are right, not working on this example, but you can check my other tutorial (Building SPA using Angular JS) where the menu is working correctly on mobiles.
1. Download ui-bootstrap from here: http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.2.min.js
2. add this script to your index.html
3. in app.js add ‘ui.bootstrap’ as a dependancy
Thank you Sir!!!!!!!!!!!! Another noob question for you….
I am trying to make a GET request work but the uri cannot be located. I keep getting 404 errors.
Here is my webapi…
// GET api/Permissions/GetPermissionByUser begin snippet
[Authorize]
[Route(“GetPermissionByUser”)]
public IHttpActionResult Get(string userName)
/// end snippet
// The webapi config…
config.Routes.MapHttpRoute(
name: “DefaultApi”,
routeTemplate: “api/{controller}/{id}”,
defaults: new { id = RouteParameter.Optional }
);
// My javascript call…
return $http.get(serviceBase + ‘api/Permissions/GetPermissionByUser’, userData).then(function (results) {
return results;
});
// userData is a string with “userName” in it.
Please advise if you can!
Many thanks!
What is your Controller class name? Is it in Web project or in another?