Rhythm Section: AngularJS and Windows Authentication w/ .NET Web API

Sometimes you can spend far too long making decisions about how to organize your project. Guidance can be lacking, and we don’t want to have spend time reorganizing our project at a later stage. This is one of many reasons why creating .NET applications using John Papa’s AngularJS HotTowel Template is highly recommended. The template directs you on the ‘what goes where’ questions and you can learn a lot by the answers.

Yet as helpful as it is, no template can answer everything.

Let’s modify it to use Windows Authentication in a Single Sign On manner.

check out the Gist, located here: Source

First, we’ll add in our authentication factory:

authentication.js

angular.module('app')
.factory("authentication", ["$http", "$q", "$window", authentication]);

function authentication($http, $q, $window) {

    var user;

    function login() {
        
        // check if the user already exists for this session
        if (user) {
           return $q.when(user); // resolve with given value, necessary because calling function expects a promise.
        }
        
        var url = 'api/users/current/';
        return $http.get(url).then(function (result) {
            var result = result.data;
          
            user = {
                id: result.UserId,
                displayName: result.DisplayName,
                guid: result.ADGuid,
                isAdmin: result.IsAdmin
            };

            addUserToStorage();

            console.log("user created.");
            return $q.when(user);
        });
    }

    function addUserToStorage() {
        $window.sessionStorage["user"] = JSON.stringify(user);
    }

    function getUser() {
        return user;
    }

    function init() {
        if ($window.sessionStorage["user"]) {
            user = JSON.parse($window.sessionStorage["user"]);
        }
    }

    init();

    return {
        user: user,
        init: init,
        addUserToStorage: addUserToStorage,
        login: login,
        getUser: getUser
    };
};

The above is a factory that checks to see if a user already exists (by looking in session storage). If they’re not yet there, it calls the specified Web API URL to get a new user. A call to getUser executes once, without making additional calls to the server.

example-controller.js

// in the controllers, we can get the current user by deferring to authentication service

(function () {
    'use strict';
    var controllerId = 'sales';
    
    angular.module('app')
        .controller(controllerId, ['$q', 'common', 'authentication', sales]);
    
    function sales($q, common, authentication)
    {
      // ...
       
    // each controller has a call to getUser(), 
    // which defers to the authentication service for auth status
    var vm = this;
    vm.user = {};
    
    activate();

        function activate() {
            
            // call the getUser promise
             common.activateController([getUser()], controllerId)
                .then(function () {  });
        }
        function getUser() {
            return authentication.login().then(function(data) {
                vm.user = data;
                return $q.when(data);
            });
        }
        
        // ...
        
    }
})();

For my particular application, I never know what section of the SPA the user will arrive at first, so each ViewModel should know how to authenticate the current user on its own.

And now on to the Server Side:

User.cs

public class User
    {  
        public int UserId { get; set; }
        public string DisplayName { get; set; }
        public string ADGuid { get; set; }
        public bool IsAdmin { get; set; }

    }

A simple class that represents an Entity Framework user entity designed for transferal to the front end.

UserFactory.cs

public class UserFactory
    {
        private readonly IDbContext db = new DbContext(); // demo, could be injected

        public User Create(WindowsIdentity currentWindowsUser)
        {
            var user = new User();
            
            string name = currentWindowsUser.Name.Replace("DOMAIN\\", ""); 
               
            // a much simplified case for example (better to retrieve by GUID found in AD)
            User userInDatabase = (from u in db.Users
                                         where u.FirstName.ToLower() + u.LastName.ToLower() == name.ToLower()
                                         select u).FirstOrDefault();

            if (userInDatabase != null)
            {
                user.UserId = userInDatabase.UserId;
                user.DisplayName = userInDatabase.DisplayName;
                user.IsAdmin = userInDatabase.IsAdmin;
                user.ADGuid = userInDatabase.ADGuid;
            }
            
            return user;
        }
    }

In this UserFactory class, we’re getting the current logged in user account and verifying the name exists and matches up in the database.

And now the Web API:

UsersController:

[RoutePrefix("api/users")]
public class UsersController : ApiController
    {
        /// 
        ///     Gets the currently logged in user account in AD as a User object
        /// 
        /// 
        [Route("current/")]
        public User GetCurrentUser()
        {
            User user = new UserFactory().Create(WindowsIdentity.GetCurrent());
           
            return user;
        }
    }

The “current/” route calls the UserFactory with the current windows identity, which creates a user we can pass back to the front end.

With this in place, we now have a working Single-Sign On version of our HotTowel template!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s