10/15/16

App Service Authentication with Azure AD

 When building SaaS solution, one of the most common cross cutting concerns is the authentication of users. For some solutions, the security implementation is handled by the application itself. In this article, we explore the use of Azure Active Directory (AAD) to provide the security services for our apps.

The application we are building is a Node.js backend API with an AngularJS front end. To follow along, create a web app on Azure and download the initial codebase by running the following command using the command window:

Clone the repo from Github. This creates a new folder (nodejs-azure-ad) and downloads the code. We should note that we are cloning from the simple-auth branch.


git clone -b simple-auth https://github.com/ozkary/nodejs-azure-ad.git


Update all the npm packages by typing (from the newly created folder nodejs-azure-ad)


npm init


This should update all the packages that are listed as dependencies in our package.json file. Now that we have the code, we can run it on its current state. From the command line and in our working directory, type the following:


node server


A NodeJs app should start showing the server location and some instructions on how to load the client user interface on the browser. Once the client application is loaded, the application looks like:

App services Azure AD Authentication


At this point, the app is showing us that the content is secured, and we need to login to the system. The AngulaJS code uses a service and controller to make an API call to the server to fetch the current user context (see section Client App Code). However, there is currently no integration with any identity provider that can allow us to authenticate the user, so there is no user context, and the application shows us the message to login.

  To get our application to work under the Azure AD authentication process, we can deploy the app to Azure and then we can enable the authentication. Let’s start by deploying our app first.

Deploy to Azure Using Local Git

To deploy the application on Azure, we first need to set the deployment options on our app. Login to Azure and follow these steps:

  • Select your web app from the app services view

  • From the App Deployment options select Deployment Options and select Local Git Repository which allows us to push changes from or local repo to Azure

  • From the App Deployment options now select Deployment credentials, set a username and password. This is needed when we are ready to deploy the code to Azure

Azure Deployment Settings

The last step is to get the Git Clone url from the Overview section. This is the url that we need to be able to create a remote Git repo and push our changes to Azure. Click on the Overview option, and find both the Git deployment username and clone url.

We have now created our deployment profile with Azure using a local Git repo, and we are ready to deploy.

Deploy to Azure

To deploy our app to Azure, let’s go to the directory where our code is located using the command line. When using Visual Studio Code, we can use the integrated terminal option from the view menu. On the terminal, we can type the following commands: (replace azure-clone-url with your Azure Git clone url)


git remote add azuread  azure-clone-url
git push azuread master


The first command adds a remote repo with the name of azuread that points to the repo on Azure defined by the clone url. The second command pushes our code to the remote repo to update our Azure app.  When pushing the code, we should see a prompt asking for the username and passwordThis is the information we created under the deployment credential configuration area, not your Azure login.

After entering our credentials, we should see information about the publishing process to Azure. Once this is complete, we can go back to the Azure app deployment -> Deployment options, and we should see that our initial deployment is successful. This is a confirmation that the app is deployed to Azure.

We can now run the app from Azure using the App url (overview blade on Azure -> Url).  We should see that the app is running and indicating us to login. We still have no authentication enable on our app, so the app continues to load the same as it did when running locally.  We now need to enable AD authentication on our app.

Enable Azure AD Authentication

Let’s get back to Azure and select our web app. Click on Select Settings -> Authentication / Authorization  and click on On for the App Service Authentication option. This enables additional options for which we need to select Log in with Azure Active Directory for the action to take and configure AD with the Express option to build an Active Directory (AD) protected app.

*Note: The Login with Azure AD when the request is not authenticated option forces a redirect and does not allow the option to provide anonymous access to any of the app content.

The express option is a quick way to create an app registration with AD authentication. It adds the basic settings to create the app profile under the Azure Active Directory configuration settings. It also configures the app to allow access to all the users under the current AD.  Yes, these are the login profiles.

Azure AD App Configuration


Testing our AD Enabled App

We can try to load our app again from the browser, and we should see a much different behavior. To login, use your Azure credentials.

*Note: If you are already logged in to Azure with the same browser, a redirect takes place, but no login is required as the identity provider already knows about your current session. 

To test the integration, we should open a new browser on private or incognito mode, depending on the browser. This does not load any current state or cookies that may affect our validation.  Take a few seconds to reload the app and come back.

Yes, the app is now redirecting to an identity provider. For us to see our app, we are now required to enter our AD username and password. This is not the credentials that we created for deployment. This is the credentials that we use to login to our azure portal. To understand what users are available on Azure AD, from the Azure main menu select Azure Active Directory -> Users and groups -> All users.

Azure AD Users


Authenticated Results

App Service Azure AD Authenticated App


Now that the app is running with AD authentication and after a successful login, we should see that our app now shows a welcome message.  We are able to know that the user is authenticated because our client code makes an API call to retrieve the user context information. If the context exists, the client code sets the identity object which is used by the HTML view to render the elements based on whether the user is authenticated or not.

Client App Code

The client code is implemented using AngularJS. The core of the app is written on the auth service, app controller and view.

Auth Service (source: app/app.js)


//source app/app.js

//simple validation of the user context
//calling api/user
function isAuth() {
    var deferred = $q.defer();
    var url = 'api/user';

    $http.get(url).then(function (res) {
        var user = res.data['session'];

        deferred.resolve(user);
    }, function (err) {
        deferred.reject(err);
    })

    return deferred.promise;
}


The service which makes the API calls to retrieve the context from the server.

Controller (source: app/app.js)


// main controller to check the user auth state
function ctrlApp($auth) {
    var ctrl = this;
    ctrl.identity = null;

    function loadContext() {
        //get the user context
        $auth.isAuth().then(function (res) {
            ctrl.identity = res;
        }, function (err) {
            ctrl.err = err;
        });
    }
    loadContext();
}


The controller uses the $auth service to retrieve the user context and sets the controller identity member.

HTML View (source: app/views/about.html)


<div id="secured" ng-if="ctrl.identity">
    <h1>Welcome {{ctrl.identity.firstname}}</h1>
    <i ng-if="!ctrl.user" class="fa fa-lock fa-5x"></i><br>
    <div class="lead" ng-if="ctrl.user">       
        <div class="container">
            <h2>My Profile</h2>
            <ul class="list-group text-left">
                <li class="list-group-item"><b>eMail:</b> {{ctrl.user.username}}</li>
                <li class="list-group-item"><b>FirstName:</b> {{ctrl.user.firstname}}</li>
                <li class="list-group-item"><b>Last Name:</b> {{ctrl.user.lastname}}</li>
            </ul>
        </div>
    </div>
</div>


The view uses the identity member to control what is displayed to the user.

Server Side Code

The server side code is implemented using Node.js. Our server app defines client (app/) and server (api/) routes.

APIs (source: modules/routes.js)


// source modules/routes.js
//secured routes – get user context
var user = null;
app.get('/api/user', profile)

//validate that the user profile is set on the authSession cookie
function profile(req, resp) {
    var authSession = req.cookies['AppServiceAuthSession'];
    if (authSession != null) {
        user = authSession;
    }
    resp.json({ session: user });
}              
  

The routes.js module defines the routes. For the user context validation, the code just looks for the AppServiceAuthSession cookies which is a secured and HTTP only cookie. This means that it is not accessible on the client side via JavaScript.

Both the server and client code are only aware of the security context of the user, but there is not enough information to know about the user’s claims. For this, we need to get more detail information about the user by doing an integration using OAuth and authentication tokens. This however needs to be covered in another article.



Summary
In this article, we are able to see how without doing any implementation changes to our app, we can enable the authentication with Azure AD. We should note that this works well if we are protecting our entire app, but what about when we need to only protect some routes and allow anonymous access to others?  The answer is provided on our next article.

Originally published by ozkary.com