lockangular

It had been a couple of month since I embark in the journey of mixing AngularJS with MVC. Before then, I have utilized AngularJS to render certain view containers here and there. However, for the first time I was after a fully functional AngularJS app, yet with MVC server side. I wanted to use the fully functional Form Authentication MVC template, and make an AngularJS application ready to go with form authentication in no time.

This task introduced two main challenges. First, we have to make the client side fully aware of the authentication and have Angular preserve state – so even as the page refreshes Angular shall response as authenticated. This is inherit function in a MVC Razor app but not necessarily in an Angular application. Secondly, client side authentication – No-Oauth - is not the most secure, we could end up with a cross site request forgery attack. So what I needed was an Anti-Forgery Token, MVC Form Authentication AngularJS app.

Thankfully, I was still able to bootsrap my application in no time thanks to two resources that provided proper guidance for what I was looking for. Credit where is due

Sean KennyForms Authentication in .NET Land

Olav NyboAnti-Forgery Token MVC, Angular JS

Both example are Web API. However, I emulated Sean Kenny authentication principle in an MVC example app. In order to contribute to those that may be looking for a similar example

Example

The server code is pretty simple – a sign In method a sign out method and a method that will be perform only when a user is authenticated.

 Authentication

Info: You could download this example application by clicking on the below "Download Source Code" button

Server Side Code

   1: [HttpPost]
   2: public ActionResult SignIn(UserDataModel user)
   3: {
   4:    HttpResponseMessage msg = new HttpResponseMessage();
   5:     if (this.ModelState.IsValid)
   6:     {
   7:         var authenticated = true;
   8:         // the user authenticated in the above method
   9:         if (authenticated)
  10:         {
  11:             var response = new HttpStatusCodeResult(HttpStatusCode.Created);
  12:             FormsAuthentication.SetAuthCookie(user.UserName, true);
  13:             return response;
  14:         }
  15:         else
  16:         {
  17:             return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
  18:         }
  19:     }
  20:     return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
  21: }
  22: [HttpPost]
  23: public ActionResult SignOut(UserDataModel user)
  24: {
  25:     if (this.ModelState.IsValid)
  26:     {
  27:         var response = new HttpStatusCodeResult(HttpStatusCode.Created);
  28:         FormsAuthentication.SignOut();
  29:         return response;
  30:     }
  31:     return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
  32: }
  33:  
  34:  
  35: [HttpPost]
  36: [Authorize]
  37: public ActionResult PerformAction(UserDataModel user)
  38: {
  39:     var response = "action performed for " + user.UserName;
  40:     /// or no need to pass the user
  41:     var username = Thread.CurrentPrincipal.Identity.Name;
  42:     /// pefrom action with Identity of the user from Thread..
  43:  
  44:     return Json(response, JsonRequestBehavior.AllowGet); ;
  45:     
  46: }

Client Side

   1: var app = angular.module("myapp", ["ngAnimate"]);
   2:     //service
   3:     app.factory('DataPost', function ($http, $q) {
   4:  
   5:         return {
   6:  
   7:             get: function (url, json) {
   8:                 var mydeffered = $q.defer();
   9:                 $http.post(url, json)
  10:                 .success(mydeffered.resolve).error(mydeffered.reject);
  11:                 return mydeffered.promise;
  12:             }
  13:         }
  14:     });
  15:     // controller
  16:     app.controller("SignInCtrl", function ($scope, DataPost, $rootScope) {
  17:  
  18:         /// Privates
  19:         var _signIn = function (user) {
  20:             DataPost.get("/vm/SignIn", user).then(function (data) {
  21:                 console.log(data);
  22:                 user.authenticated = true;
  23:                 $rootScope.user = user;
  24:                 $scope.CurrentUser = user.UserName;
  25:  
  26:             }, function (e) {
  27:                 console.log("error " + e + "");
  28:                 user.authenticated = false;
  29:                 $rootScope.user = {};
  30:             });
  31:         }
  32:         var _signOut = function (user) {
  33:             DataPost.get("/vm/SignOut", user).then(function (data) {
  34:                 $rootScope.user = {};
  35:                 user.authenticated = false;
  36:  
  37:             });
  38:         }
  39:         var _doSomething = function (user) {
  40:             DataPost.get("/vm/PerformAction", user).then(function (data) {
  41:                 console.log(data);
  42:                 $scope.responseError = false;
  43:                 $scope.response = data;
  44:             }, function (e) {
  45:                 console.log("Unauthorized access");
  46:                 $scope.responseError = true;
  47:             });
  48:  
  49:         }
  50:  
  51:         // $scope
  52:         $scope.signIn = function () {
  53:             console.log($scope.user);
  54:             _signIn($scope.user);
  55:         }
  56:         $scope.signOut = function () {
  57:             _signOut($scope.user);
  58:             $scope.performclicked = false;
  59:         }
  60:         $scope.Perform = function () {
  61:             _doSomething($scope.user);
  62:             $scope.performclicked = true;
  63:         }
  64:         $scope.init = function () {
  65:             // for display only
  66:             $scope.performclicked = false;
  67:             $scope.response = "";
  68:             $scope.responseError = false;
  69:         }
  70:         $scope.init();
  71:     });

HTML or View

   1: <body ng-app="myapp" class="container">
   2:     <div ng-init="user={authenticated:@Model.Authenticated,UserName:'@Model.UserName'}">
   3:         <div class="row" ng-controller="SignInCtrl">
   4:             <div class="col-sm-3 nav" >
   5:                 
   6:                 <form name="signForm" role="form" ng-show="!user.authenticated">
   7:                     <h1>Login</h1>
   8:                     <div class="form-group">
   9:                         <label for="username">Username:</label>
  10:                         <input type="text" id="username" ng-model="user.UserName" class="form-control" required />
  11:                     </div>
  12:                     <div class="form-group">
  13:                         <label for="password">Password</label>
  14:                         <input type="password" id="password" ng-model="user.Password" class="form-control" required/>
  15:                      </div>
  16:                     <div class="form-group">
  17:                         <input type="submit" class="btn btn-success" ng-click="signIn()" ng-disabled="signForm.$invalid" />
  18:                     </div>
  19:                 </form>
  20:                 <div class="row">
  21:                     <button type="submit" class="btn btn-danger" ng-click="signOut()" ng-show="user.authenticated" >Sign Out</button>
  22:                 </div>
  23:             </div>
  24:             <div class="col-sm-9 main">
  25:                 <h1 ng-show="user.authenticated">Welcome {{user.UserName}}</h1>
  26:                 <h3>User Tasks</h3>
  27:                   <div class="bs-callout bs-callout-warning">
  28:                     
  29:                     <p>The <code>Perform</code> action will remain visible only for the puropse to atest that the <code>$http</code>ajax call will fail when not authenticated. Otherwise, when authenticated the post method will execute at the server</p>
  30:                   </div>
  31:                   <div class="alert alert-danger" role="alert" ng-show="!user.authenticated">
  32:                       <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
  33:                       <span class="sr-only">Error:</span>
  34:                       Perform call Will Fail!
  35:                     </div>  
  36:                   <div class="alert alert-success" role="alert" ng-show="user.authenticated">
  37:                       <span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
  38:                       <span class="sr-only">Error:</span>
  39:                       Perform call Will Execute!
  40:                     </div>  
  41:                 <div class="col-sm-12 text-center">
  42:                     <button type="submit" class="btn btn-info" ng-click="Perform()"  >Perform</button>
  43:                 </div>
  44:                 <div class="col-sm-12 text-center response animate-show-hide" ng-show="performclicked" >
  45:                     <span ng-show="responseError"  >Unauthorized Access! </span>
  46:                     <span ng-show="!responseError" >responded: {{response}}</span>
  47:                 </div> 
  48:             </div>
  49:         </div>
  50:         
  51:     </div>
  52: </body>