Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

AngularJS & Web API Active Directory Security

4.87/5 (23 votes)
28 Aug 2015CPOL6 min read 94.1K   2.1K  
AngularJS and Web API Active Directory Security (Authorisation)

Introduction

In today’s design methodologies, security is something that is not asked for but expected!  Often a TFS User Story with Tasks cumulating to 100+ points in your backlog!

With newer technologies, it can be a daunting process to lock down security correctly, but one way is to provide a proof-of-concept demo, detailing how your system will restrict users to particular Web API methods or the whole Web API service.

Application security comes in two flavours:

  1. Intranet application (AD or LDAP)
  2. Internet application (Identity & Bearer Tokens).

This blog will concentrate on option 1, intranet security with Windows Active Directory. I will detail how to secure your application using Bearer Tokens in my next blog.

Technologies Used

  1. Visual Studio 2013
    1. Web API
    2. Angular.js (PM> Install-Package angularjs)
    3. Bootstrap CSS (PM> Install-Package bootstrap -Pre)
    4. Angular BlockUI (PM> Install-Package angular-block-ui)
    5. Angular UI-Router (PM> Install-Package Angular.UI.UI-Router)
  2. Local IIS (Express)
  3. Express LocalDB
  4. Entity Framework 6 (PM> Install-Package EntityFramework)
  5. Windows 7 - Computer Management Groups
  6. Soap-UI, Fiddler, Post-Man (test REST services)

In this blog, I want to keep things simple, by using a (SQL) LocalDB and Local IIS Express as security can become bloated with multiple plug-ins or third party controls. This blog will deal with restricting users to a particular service method.

Project Structure

Image 1

The solution is split up into two projects, a client (MVC) AngularJS project and a server (MVC) Web API project. As you can see the Web API project hosts the LocalDB, which Entity Framework interacts with. The client web project is a standard AngularJS application, with modules, controllers, services, routing and multiple views.

From the properties of the Web API project, you will see that the server is hosted using IIS Express.

Image 2

The solution will start both the Web API project and the Web Client project together (Web API first).

Image 3

Server Configuration Prior to Development

Enable Web API Windows Security

Within the Web API web.config file, ensure the following authentication element is present.

XML
<system.web>

    <authentication mode="Windows" />

</system.web>

Computer Management Group

Add a new Group (WebSiteUser), which will be the Role associated with a particular user. Do not add yourself to this group yet, as we will initially test to see that you are restricted form calling the Role restricted service method.

Image 4

 

Microsoft Internet Explorer

In IE you can check the setting with Tools > Internet Options > Advanced and look for a setting Enable Windows Integrated Authentication. When you go to the tab Security and then Intranet and Custom Level, then you will find a setting at the bottom to specify if IE should logon automatically or prompt for the username and password.

Image 5

Image 6

Google Chrome

Chrome uses the same IE settings.

Mozilla Firefox

Firefox automatically supports Windows Integrated Authentication but by default it will prompt for the username and password. If you want to change this, then you have to go the the settings of FireFox. So type about:config in the address bar and look for network.automatic-ntlm-auth.trusted-uris and enter the url of your intranet server. You can add multiple sites by comma delimiting them.

Image 7

IIS Express & IIS

Maybe it is still not working and returning a 401 Unauthorized message. When using IIS Express you have to change a configuration setting. Look for the ApplicationHost.config in your “My Documents\IISExpress\config” folder. There you have to add:

 <windowsAuthentication enabled="true">

When you will host you application in IIS 7 or higher, then you have to do the same. You can also modify this in the ApplicationHost.config file which can be found in the “system32\inetsrv” folder. But it is easier to change it in the Internet Information Services Manager. Go to Authentication and enable the Windows Authentication module.

Image 8

Server Side Code Explanation

Securing Web API Method Authorization

With this Web API method, we are only letting valid active directory users access it, by adding the  [Authorize] annotation to the method.

C#
[Authorize]       
[HttpGet]
[Route("api/People")]
public IQueryable<Person> GetPeople()
{
return db.People;
}

Securing Web API Method by Group

We want to secure the Web API method by only letting authorized (active directory) users and users that are within the group\role WebSiteUser.

C#
[Authorize]
[Authorize(Roles = "WebSiteUser")]
[HttpGet]
[Route("api/AandroidConversions")]       
public IQueryable<AandroidConversion> GetAandroidConversions()
{
return db.AandroidConversions;
}

Enabling CORS (Cross Origin Request Sharing)

Since our web client will be using a different port number to the Web API, we need to enable CORS within the Web API project. This can be done by updating the Global.asax class method Application_BeginRequest to include the following code:

C#
Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");

Image 9

Update the method Register within the class WebApiConfig

C#
// Web API configuration and services
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);

Image 10

Client Side Code Explanation

Index Html Page

The index page is very basic Html & AngularJS, with custom AngularJS code (module, controller & service). Two hyperlinks that load separate views and call the appropriate Controller method. A <Div> statement to inject the views into. The view basically has two links, one will call an authorized enabled service method and the other will call an authorized role enabled service method.

HTML
<!DOCTYPE html>
<html ng-app="app">
<head>
    <title>Angular + WebAPI AD Security</title>

    <!--Styles-->
    <link href="Content/bootstrap/bootstrap.css" rel="stylesheet" />
    <link href="Content/angular-block-ui.css" rel="stylesheet" />

    <!--AngularJS & Plugins-->
    <script src="Scripts/angular.js"></script>
    <script src="Scripts/angular-ui-router.js"></script>
    <script src="Scripts/angular-block-ui.min.js"></script>

    <!--Custom Angular Scripts-->
    <script src="App/app.js"></script>
    <script src="App/services.js"></script>
    <script src="App/controllers.js"></script>
</head>
<body>

    <nav class="navbar navbar-default">
        <div class="container">
            <ul class="nav navbar-nav">
                <li ng-controller="customerController">
                    <a href="#/Authorised" title="'Authorised' only windows request" ng-click="getAllACustomerValues()">
                        Authorised Request
                    </a>
                </li>
                <li ng-controller="androidController">
                    <a href="#/Restricted" title="'Authorised' and 'User Group' request" ng-click="getAllAndroidValues()">
                        Restricted (Authorised) Request
                    </a>
                </li>
            </ul>
        </div>
    </nav>

    <!--inject views-->
    <div class="container">
        <div id="ui-view" ui-view></div>
    </div>
</body>
</html>

Angularjs App

There is nothing special about the App module or it’s routing;

JavaScript
angular.module('app',
    [
        // Dependency Inject Goes inside this array
        'ui.router',  // we inject/load ui-router for routing
        'app.services', // we inject/load the services
        'app.controllers', // we inject/load the controllers
        'blockUI', // display spinner when making AJAX calls       
    ]
)
    .value("baseWebApiUrl", "http://localhost:57158/")

    .config(['$stateProvider',
        function ($stateProvider) {
            // our routers, self explanatory
            $stateProvider
                .state('Authorised', {
                    url: '/Authorised',
                    templateUrl: './Views/Authorised.html',
                    controller: 'customerController'
                })
                .state('Restricted', {
                    url: '/Restricted',
                    templateUrl: './Views/Restricted.html',
                    controller: 'androidController'
                });                       
        }]);

Angularjs Controller

Again nothing on-to-ward happing within the controllers;

JavaScript
.controller('androidController', function ($scope, $http, $log, blockUI, androidService) {

      $log.debug('Enter androidController');

      /*================================================================================
      Author      : Bert O'Neill
      Method      : getAllAndroidValues
      Parameters  : N\A
      Date        : 30-Aug-2015
      History     : Initial Draft (30-Aug-2015)
      Description : To use this Web API the caller must be part of a Windows group (WebSiteUser).
                    Call service to pull back all android related data from database.
                    Bind results to parent scope, which in turn is bound to UI control
      Test Prj.   : N\A
      ================================================================================*/

      $scope.getAllAndroidValues = function () {
          $log.debug('Enter getAllAndroidValues');
          blockUI.start(); // block UI
          androidService.getAllAndroidData() // pass back the promise and handle in controller (service is only a pass through\shared logic between ctrls)
              .then(function (results) {
                  $scope.$parent.sunbursts = results.data // update the parent scope as you have nested controllers in view
              },
              function (results) {
                  alert("Failed to make android data request."); // log error
              });

          blockUI.stop(); // unblock UI
          $log.debug('Exit getAllAndroidValues');
      }
      $log.debug('Exit androidController');
  })

AngularJS Service

But in the service we tie up the Windows credentials that the server side is expecting and will authenticate against.

JavaScript
.service('androidService', ['$http', function ($http) {

    /*================================================================================
    Author      : Bert O'Neill
    Method      : getAllAndroidData
    Parameters  : N\A
    Date        : 30-Aug-2015
    History     : Initial Draft (30-Aug-2015)
    Description : Public API method with user group restrictions.
                  Call Web API Rest method to pull back all android related data from database.                  
    Test Prj.   : N\A
    ================================================================================*/

      this.getAllAndroidData = function () {
        return $http({
            method: 'GET',
            withCredentials: true,
            data: 'json',
            url: 'http://localhost:57158/api/AandroidConversions'
        });
      };    
  }])

AngularJS Views

The views are expected small snippets of html code with angular expression binding;

XML
<div class="row">
    <table class="table">
        <thead>
            <tr>
                <th>Id</th>
                <th>First Name</th>
                <th>Last Name</th>
            </tr>
        </thead>
        <tbody>
            <tr ng-repeat="person in people">
                <td>
                    {{person.id}}
                </td>
                <td>
                    {{person.first_name}}
                </td>
                <td>
                    {{person.last_name}}
                </td>
            </tr>
        </tbody>
    </table>
</div>

Calling Authorised Service

By clicking on the Authorised Request link, you will initiate a request call to the Web API service method, which challanages the client to declare who they are, thus you wiill initially get prompted. Once you have entered valid window credentials , an AngularJS Ajax spinner will be displayed, leading to a simple grid displaying informatio.

Image 11

Image 12

Image 13

If you now click again, your token (session) was cached and you will not be prompted for your credentials.

Image 14

Image 15

Authorized and passing data back to AngularJS client.

Image 16

You’re Active Directory Group WebSiteUser has no entries at this stage, so if you click on the Restricted (Authorised) Request you will again be prompted for your credentials, but when you enter them and click OK, you will keep getting prompted because you are not a member of the WebSiteUser group. In fact nobody will be able to access the Web API method because nobody is a member of this AD group!

Image 17

Windows security will not let you access the Web API method;

Image 18

Valid Authorized & Role

Add yourself to the Active Directory Group WebSiteUser.      

Image 19

Click on the Restricted (Authorised) Request link, the server will challange the client, but this time your credentials are valid for the Web API method.

Image 20

Image 21

Image 22

Image 23

Conclusion

Configuring AngularJS and IIS for Active Directory security is straight forward; you just need to know what has to be configured. Remember this is authentication (given access) not authorisation (who are you) which is where certificates come in.

In my next blog I will detail how to use AngularJS with the WWW security, basically incorporating OWIN & Identity (Bearer Tokens).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)