Dev Diary S01E06: Azure MVC Web API, Angular and Adal.JS and 401s


Introduction

In my previous post, we discussed how to implement Adal.JS into your Angular application. This allows us to use Azure Active Directory as our authentication mechanism.

In this post I am going to go through and create the MVC Web API which we will then start to call from our Angular client to manipulate the Invoices.

 

Creating the MVC Web API Project

Right then, so first of all we need to create an MVC Web API Project. There are lots of guides out there such as this one on the ASP.NET site.

Anyway, I fired up Visual Studio 2015.

  • File->New Project
  • Chose the Visual C# ASP.NET Web Application template
  • Created an MVC 4.5.2 template Web Application

image

  • Selected the folders and core reference for MVC and Web API
  • Selected to “Host in the cloud”
  • Then clicked Change Authentication
    • Clicked on Word and School Accounts
    • Choose Single organisation and selected my domain ithinksharepoint.com
    • Also selected Read Directory data option

image

  • Now click Ok
  • Clicked Ok to start creating the project

Next, you are presented with a screen to start configuring your App Service.

So I setup an appropriate name for the Web App

  • appropriate Web App Name
  • Choose my subscription
  • Chose Resource Group
  • Chose App Service Plan
  • Clicked Create

Visual Studio then worked its magic and created the Project with all the Azure services behind the scenes.

Then it setup the Organizational Account and App Insights. Once this was all completed we have a working Azure Web Application and are ready to start adding the Web API components.

 

Configuring Azure AD Authentication for the WebAPI Application

To be honest I was a bit surprised that I had to do this next step but when I was trying to find the application in Azure AD, I could not find anything. Also I could not find anything in the project either related to Azure AD.

So to configure Azure AD Authentication for the WebAPI application I had to do the following:

  • Right-click on the project
  • Choose “Configure Azure AD Authentication”

This fires up the Azure AD Authentication wizard

  • Click Next
  • Under Single-Sign-On
    • Choose your domain
    • Provide an appropriate App ID Uri.
    • Choose create a new Azure AD Application
    • Click Next
  • Under Directory Access Permission
    • Select Read Directory Access is not already enabled
    • Click Finish

This will start a wizard which will set everything up. The end result is visible in https://manage.windowsazure.com

 

 

Adding the WebAPI Components

We have a .NET MVC WebAPI project with very little in it.

Let me explain what I did next, so I would like to have two services for the first phase.

  • Configuration Controller – used to get the system and user configuration for the application.
  • Invoice Controller – used to add, edit, remove and list the invoices found in the application.

What we will do is create the Configuration service with a simple stub, firstly with no authentication so that we make sure it works and then we will make changes to the WebAPI layer so that it requires authentication.

 

I created the Configuration Controller. This was achieved by the followingNyah-Nyah

  • Browsed to the Controllers folder
  • right-clicked Add->Web->Web API->Web API 2.1
    • ConfigurationController.cs
  • Modified the initial code so that it looked like this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace Itsp365.InvoiceFormApp.Api.Api.Controllers
{
public class ConfigurationController : ApiController
{
public IDictionary<string,string> Get()
{
var configuration = new Dictionary<string, string>();
configuration.Add("SharePointUrl", "https://ithinksharepointltd.sharepoint.com/sites/dev&quot;);
return configuration;
}
}
}

 

Next, we need to deploy this code to Azure and try it out.

I published the project to Azure by doing the following:

  • Right-click on the project –> Publish

image

  • Next choose Publish from the wizard

This will start the publishing process and push the code up into Azure.
Oh dear, this did not go so well and I received the following screen:

image

I ended up having to switch on customErrors as recommended by the error message. It turned out that there was a reference to the System.Spacial.dll. The project did not need this and removed it from the project references. The site was published and then ended up with the following screen.

image

I accepted the request for permissions and then was redirected to http://localhost which failed to work as I was not debugging the application.

Third time lucky, this time I ran the project using F5 and it all worked nicely, redirecting to http://localhost.

Just in case you do not believe me, I took the screenshot below:

image

 

We can also see that our API is working by browsing to https://localhost:44356/api/configuration and we receive a block of Xml as shown below.

image

Ok, now that we have the service up in the cloud, lets update Angular to consume it.

 

Update Angular client application to consume WebApi

So I thought I would update the Angular application, add a new page for settings. Firstly, the page would just load in the configuration but eventually this page will allow users to setup and configure the settings for the application.

In order to be able to consume the configuration api endpoint, I needed to do a couple of things to the application.

  • create a new view called settings.html
  • create a new service called configurationService.js
  • create a new controller called settingsController.js
  • configure the app.js to load these new modules
  • configure the index.html to load the scripts

So lets go through each of the components, starting with the settings.html page

<section>
<h1> Settings </h1>
<h2> Login Information </h2>
<p class="form-control">
Username: {{userInfo.userName}}
</p>
<p class="form-control">
Profile: <br/>
<ul>
<li ng-repeat="profileItem in userInfo.profile">{{profileItem}}</li>
</ul>
</p>
</section>
<section>
<h2>Api Settings</h2>
<p>
{{apiConfig}}
</p>
</section>
<section>
<h2>Application Information</h2>
<p>
{{applicationInformation}}
</p>
</section>
<section>
<h2>Additional Settings</h2>
</section>
<section>
<h2>Installation Information</h2>
<a class="btn" href="#/installation" title="Show Installation Information">Installation Status</a>
</section>
view raw settings.html hosted with ❤ by GitHub

 

Next, we have the settingsController.js

'use strict';
var settingsControllerModule = angular.module('settingsController', ['configurationServiceModule']);
settingsControllerModule.controller('settingsController', ['$scope','$http','$routeParams','configurationService','adalAuthenticationService', function ($scope, $http, $routeParams, configurationService, adalService) {
$scope.userInfo = adalService.userInfo;
$scope.apiConfig={};
$http({
method:'GET',
url: configurationService.settings.apiUrl + "/configuration"
}).then(function processConfigurationResponse(response){
$scope.apiConfig=response.data;
}, function processError(response){
alert(response);
});
}]);

 

Lastly, we have the configurationService

'use strict'
var configurationService = angular.module('configurationServiceModule', []);
configurationService.constant('applicationConstants', {
'clientId':'7c27b6a6-{4 digits}-{4 digits}-9b92-2eee2264cc71',
'tenantName':'{domain}.com',
'instance':'https://login.microsoftonline.com/&#39;,
'endPoints': {},
'apiUrl':'https://{url}.azurewebsites.net'
});
configurationService.service('configurationService',[]);
configurationService.provider('configurationService', function configurationServiceProvider(){
this.endPoints=null;
this.clientId=null;
this.apiUrl=null;
this.tenantName=null;
this.hasInternetConnection=function(){
return true;
}
this.init=function(apiUrl, tenantName, clientId, endPoints)
{
this.apiUrl=apiUrl;
this.tenantName=tenantName;
this.clientId=clientId
this.endPoints=endPoints;
}
this.load=function(){
var promise = $q.defer();
};
this.$get = [function initialiseConfigurationService(){
var configurationServiceInstance=new configurationSettings();
configurationServiceInstance.settings.apiUrl=this.apiUrl;
configurationServiceInstance.settings.adalSettings.clientId=this.clientId;
configurationServiceInstance.settings.adalSettings.tenant=this.tenantName;
configurationServiceInstance.settings.adalSettings.endPoints=this.endPoints;
return configurationServiceInstance;
}];
});
function configurationSettings()
{
this.settings={};
this.settings.siteUrl="";
this.settings.apiUrl="";
this.settings.logoUrl="https://blog.ithinksharepoint.com/logo.png&quot;;
this.settings.adalSettings={};
this.settings.adalSettings.instance="";
this.settings.adalSettings.tenant="";
this.settings.adalSettings.clientId="";
this.settings.adalSettings.applicationId="";
this.settings.adalSettings.endPoints="";
this.vatRate=20;
this.currency="£";
this.unitTypes = [
{name: 'once'},
{name: 'hour'},
{name: 'day'}
];
};

 

We have the guts of the changes required, of course we need to link these pages into the application.

So the app.js needs to be updated with the new route for the settings page. Also we need to setup the configurationService.

'use strict';
var invoiceFormApp = angular.module('itspInvoiceFormApp',
[
'ngRoute', 'invoiceControllersModule', 'dataModelService', 'AdalAngular', 'configurationServiceModule', 'settingsController'
]);
var appStart = function($routeProvider, $httpProvider, adalProvider, applicationConstants, configurationServiceProvider) {
$routeProvider.when('/invoices/add', {
templateUrl:'/app/views/add-invoice.html',
controller: 'addInvoiceController',
requireADLogin: true
}).when('/invoices', {
templateUrl:'/app/views/list-invoices.html',
controller: 'listInvoicesController',
requireADLogin: false
}).when('/settings', {
templateUrl:'/app/views/settings.html',
controller: 'settingsController',
requireADLogin: true
}).otherwise({
redirectTo: '/invoices'
});
var instance=applicationConstants.instance;
var clientId=applicationConstants.clientId;
var tenantName=applicationConstants.tenantName;
var endPoints=applicationConstants.endPoints;
var apiUrl = applicationConstants.apiUrl;
configurationServiceProvider.init(apiUrl, tenantName, clientId, endPoints);
adalProvider.init({
instance: instance,
tenant: tenantName,
clientId: clientId,
endPoints: endPoints,
anonymousEndpoints:{},
extraQueryParameter: 'nux=1',
cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
}, $httpProvider);
};
invoiceFormApp.config(['$routeProvider', '$httpProvider', 'adalAuthenticationServiceProvider', 'applicationConstants', 'configurationServiceProvider', appStart]);
view raw app,js hosted with ❤ by GitHub

 

Also the index.html needed to be updated so that we have a link for the settings page.

<!doctype html>
<html lang="en" ng-app="itspInvoiceFormApp">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>iThink SharePoint Invoice System</title>
<link rel="stylesheet" type="text/css" href="/bower_components/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" type="text/css" href="/app/css/invoiceformapp.css" />
</head>
<body>
<div>
<nav class="navbar navbar-default">
<div class="container-fluid">
<ul class="nav navbar-nav">
<li><a class="btn primary" href="#">Home</a></li>
<li><a class="btn primary" href="#/invoices/add" title="Create Invoice">New Invoice</a></li>
<li><a class="btn primary" href="#/settings" title="Settings">Settings</a></li>
</ul>
</div>
</nav>
</div>
<div ng-view></div>
</body>
<script src="/bower_components/angular/angular.js"></script>
<script src="/bower_components/angular-route/angular-route.js"></script>
<script src="/bower_components/angular-resource/angular-resource.js"></script>
<script src="/bower_components/jquery/dist/jquery.js"></script>
<script src="/bower_components/bootstrap/dist/js/bootstrap.js"></script>
<script src="/bower_components/adal-angular/dist/adal.min.js"></script>
<script src="/bower_components/adal-angular/dist/adal-angular.min.js"></script>
<script src="/app/controllers/invoiceControllers.js"></script>
<script src="/app/controllers/settingsController.js"></script>
<script src="/app/services/dataModelService.js"></script>
<script src="/app/services/configurationService.js"></script>
<script src="/app/app.js"></script>
</html>
view raw index.html hosted with ❤ by GitHub

 

Phew, that is quite a big change, so lets go and try this out.

image

I browse to the application and click on the settings button.

 

Unfortunately, I get an error and looking into the browser developer tools we see the following message:

XMLHttpRequest cannot load https://itsp365invoiceformappapitest.azurewebsites.net/configuration. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access. The response had HTTP status code 404.

 

Cross-Origin Resource Sharing (CORS) issues

So, what is going on?

Well we haven’t allowed the API to be able to process requests from domains that isn’t its own. The way we do that is through CORS configuration within the WebAPI.

To get this to work we need to configure the WebAPI by

  • Installing the Microsoft.AspNet.WebApi.Cors NuGet Package (current v5.2.3)
    • Not the Microsoft.AspNet.Cors NuGet Package (this does not give you the assembly System.Web.Http.Cors).
  • Modify /App_Start/WebApiConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.Cors;
using System.Web.Http.Cors;
namespace Itsp365.InvoiceFormApp.Api
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var corsConfiguration = new EnableCorsAttribute("http://localhost:8080,http://127.0.0.1:8080,https://itsp365invoiceformappapitest.azurewebsites.net&quot;, "*", "*");
config.EnableCors(corsConfiguration);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
view raw WebApiConfig.cs hosted with ❤ by GitHub

Now,its able to call in and get the configuration settings correctly!

image

 

Switching on Adal.js

We have proven that we can talk to our MVC Web API endpoint. So that is good news but now I had to get it to authenticate through Azure AD, get a access token and use that when making requests to the API.

Fortunately, Adal.js comes to the rescue here and a lot of the heavy lifting is done for you!

So let’s have a look at this. This bit did take me a while and I will explain why a little later. Though the following people and posts really helped me get to understand how Adal.js handles authentication when  authenticating against different endpoints.

First, we need to tell Adal.Js about the endpoints that will require authentication and we need to provide a way to map those endpoint URLs to resource name.

So to do that we configure the endPoints object when setting initialising the Adal library. Currently this is being setup by the following code snippet in app.js :-

 

var instance=applicationConstants.instance;
var clientId=applicationConstants.clientId;
var tenantName=applicationConstants.tenantName;
var endPoints={'https://{myurl}.azurewebsites.net/api':'https://{myappuri}.com'};
var apiUrl = applicationConstants.apiUrl;
configurationServiceProvider.init(apiUrl, tenantName, clientId, endPoints);
adalProvider.init({
instance: instance,
tenant: tenantName,
clientId: clientId,
endPoints: endPoints,
anonymousEndpoints:{},
extraQueryParameter: 'nux=1',
cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
}, $httpProvider);
view raw snippet-app.js hosted with ❤ by GitHub

 

The important part of this is the endPoint array object. This is an array of objects where you have the URL associated to the application Id Uri.

If you remember that is this section of the application configuration in Azure Ad.

 

Once we have that value we need to associate the APi Url to the App Uri Id.

This allows Adal.JS to provide the right resource id when requesting an auth token.

 

Turn on Authentication in our Web API layer

So now we have Angular configured to authenticate against Web API with Adal.js. We just need to switch on authentication for the Web API layer.

This is pretty straightforward and requires the addition of the [Authorize] attribute to our Web API Class or Methods. The change is shown below:

 

A recompile of code and Publish now allows us to test the changes out.

 

HTTP 401s and Debugging Azure WebApps

So I thought I had everything setup to allow the Angular application to authenticate against the Web API. Unfortunately I was seeing 401 Unauthorised when using the browser’s development tools.

image

 

After quite a lot of trying things out, I was stumped so I tried out debugging the WebAPI code running on Azure. Fortunately, Azure has some pretty cool development debugging tools.

To start debugging the API, I had to fire up Server Explorer. Apparently you should be able to use Cloud Explorer in Visual Studio 2015 but I have not been able to get this to work.

To debug your application do the following:

  • View->Server Explorer
  • Expand the Azure node in Server Explorer
  • Expand the App Service node in Server Explorer
  • Expand the resource group
  • Right click on the App and choose “Attach debugger”

image

The debugger takes a few moments to organise itself but eventually it will attach.

Now we can setup our breakpoints and start debugging the application. I put a breakpoint in the startup.cs file on the ConfigureAuth(app)  line within the Configuration function.

Even when I had the debugger running and tried to access the application, at no time did the breakpoint get picked up.

So what is going on?

I looked at the Azure configuration for the API application and made the following changes:-

  • Added a reply url for the Angular application which is currently being run using http://localhost:8080
  • Added an application permission so that the application can read directory data

This had no effect and I was still getting the error.

 

Examining the Angular Azure AD Application Configuration

Next I thought I better make sure that Angular is sending over an authentication token when making the request to Azure. For more information, watch this brilliant video on OAuth: Deep Dive into the Office 365 App Model: Deep Dive into Security and OAuth in Apps for SharePoint

 

I used the browser development tools and set a breakpoint in the settingsController.js on line 14 which is where the error is processed.

image

When the breakpoint is reached then we could look at the response object, fortunately the response object is pretty detailed and it has a config object which provides information about what was sent with the request. We can see here that there is a header object, now this should have an Authentication header with a Bearer token. There is not one, so what is going on? It must be our Adal Angular configuration.

 

Immediately when I saw this, I thought ah maybe its our endPoint configuration. There is a function as part of the Adal Angular service object, in our code that is adalService. This function is called getResourceForEndPoint(), this function will return back the App Id Uri based on a provided URL. Now interestingly, when this function was called with the following argument:

  • adalService.getResourceForEndPoint(“https://itsp365invoiceformappapitest.azurewebsites.net/api”);

This function returned null. This is very strange as our endPoints are configured. After a lot of playing about I realised my error. The configuration of the adalService is performed in app.js.

adalProvider.init({
instance: instance,
tenant: tenantName,
clientId: clientId,
endPoints: endPoints,
anonymousEndpoints:{},
extraQueryParameter: 'nux=1',
cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
}, $httpProvider);

The problem was the object name given for endPoints. The object name should be endpoints. So the updated app.js  was changed to the following:

adalProvider.init({
instance: instance,
tenant: tenantName,
clientId: clientId,
endpoints: endPoints,
anonymousEndpoints:{},
extraQueryParameter: 'nux=1',
cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
}, $httpProvider);
view raw adal app.js hosted with ❤ by GitHub

 

Now that we have this change made, I tried to login but got the error that the application could not reply back to the URL http://localhost:8080/app/

The fix was made to the Azure Application for the Angular client app, itsp365.invoiceformapp.client, by adding http://localhost:8080/app to the list of Reply URLs.

image

 

Whilst looking at this page, I realised that the client Angular application did not have any permissions to access the API Azure application. So to fix this, I added a new permission, chose the application:

image

Once this was added then the delegated permissions to access the application was given.

If we had not set this permission we would have seen the following error when trying to access the API application.

“response = “AADSTS65001: The user or administrator has not consented to use the application with ID = ‘{clientid}’”

 

So, actually these two changes really started to make a difference. Now when I accessed the settings view, we received the following error:

“AADSTS70005: response_type ‘token’ is not enabled for the application ↵Trace ID: 2c370e50-63ff-41e1-8cc1-fe90c8ef7b98 ↵Correlation ID: 0d1dd4db-cc4f-4bc1-b7c6-54812dfc3142 ↵Timestamp: 2016-05-14 21:35:39Z”

This requires the application manifest to be modified to enable something called the oAuth2AllowImplicitFlow.

image

 

This setting is changed by browsing to the itsp365.invoiceformapp.client Azure Application:

  • scroll to the bottom, click “Manage Manifest”
  • Click Download Manifest

image

  • Open up the downloaded file, this will have the filename of {clientid}.json, I use VS Code to do this.
  • Find the line below and change the false to true.image[27]
  • Save the file and then back on the Azure Application page choose Manage Manifest and click Upload Manifest

Wait for the manifest to be uploaded and processed.

Now when I made this change I kept getting the error “response_type token is not enabled…”. So I opened up the browsers settings and deleted the site cookies. This deleted the Azure AD authentication cookies and when I next opened up the Angular application I had to login. Now accessing the settings page, it worked!

So if you make changes , either do what I did above or run the browser in incognito mode to force an authentication.

Now the call to the configuration service API was successful!

 

Quick Refactor of the Application Constants

After I did everything, I was not happy with the way that the applicationConstants were part of the configurationService. This does not make sense so I moved them into their own folder and file. A file was created in /app/common/constants.js

The constants.js file had a new module called “applicationConstantsModule” and this defined the constant, applicationConstant.

You can see the code below:

'use strict'
var constantsModule = angular.module('applicationConstantsModule', []);
constantsModule.constant('applicationConstants', {
'clientId':'{clientguidhere}',
'tenantName':'ithinksharepoint.com',
'instance':'https://login.microsoftonline.com/&#39;,
'endpoints': {'https://{apiurlhere}.azurewebsites.net/api':'https://ithinksharepoint.com/Itsp365.InvoiceFormApp.Api.Mark2&#39;},
'apiUrl': 'https://{apiurlhere}.azurewebsites.net/api'
});
view raw constants.js hosted with ❤ by GitHub

 

I then had to update the index.html to load the /app/common/constants.js and also tell the invoiceFormApp to load the module, applicationConstantsModule within the app.js file.

 

With those changes made the refactoring is done.

 

Source Code

The source code for this blog post can be found in the following GitHub repositories:-

Conclusion

So now in this episode we have an application which has a basic REST API which is secured by Azure AD. We have made it possible for the Angular client to be able to authenticate against the REST API endpoint.

I hope you found this useful and there are some good resources for troubleshooting.

 

Next Episode

In the next episode, I will talk about my big mess up where I deleted the Azure AD app by mistake.

Thanks for reading, as always I would be interested in hearing what you think. Are these instructions useful, do they make sense?

 

 

Azure Active Directory Logo

Dev Diary: S01E05: Angular App – Setting up Azure Apps and ADAL.JS


Introduction

In the previous post we setup a page to add an invoice, adding the controller to manage that process. We explained in a bit more detail about the difference between factories and services. Hopefully, the explanation helped and made sense.

Anyway, today we are going to go and create our Azure Application, talk about setting up ADAL.JS. This will get us ready to create the Visual Studio 2015 project which will host the MVC Web API. This will be how we communicate with the Azure Document DB database.

Right lets get started.

 

Creating Azure Applications

Firstly, when ever I do this, I get it wrong and cannot remember what I need to do. Before we go into the detail, lets briefly explain what Azure Applications are and what it will do for us.

Also, I need to explain a bit about the design of the Azure Applications and how we are going to create two Azure Apps, one for the Angular bit and another for the Web API component.

 

What are Azure Applications?

Azure Applications are entities that are associated to an instance of Azure Active Directory. They can be found in https://manage.windowsazure.com by clicking the  Active Directory heading on the left hand side. Once we have loaded Active Directory, then we click on the name of the Active Directory item in the list.

image

The “Applications” tab contains the applications that you have available in your Azure AD.

image

Applications have various attributes including a Name, Sign-On URL, Client ID, App Id, Reply URL and also provide you with a way to manage permissions that users have when using this application.

Also, it is possible to assign whether someone has access to an application as well. By default, this capability is switched off.

We will explain the configuration in more detail shortly.

 

Why are we creating two applications?

We are creating two applications:

  • Angular SPA Web App
  • Web API MVC App

The two apps are one for the Web API service components and one for the Angular SPA Web App component. This allows us to manage who can access the Angular application  separately as to who can access the Web API.

Also this helps to keep the Web API component more secure as an app has a key which allows the application to be accessed and configured. By having two separate keys we won’t have the situation that the application cannot interfere with each other. Also the nature of an Angular component means it is client facing and so slightly less secure that the Web API component.

As we have support for new clients, then we can start creating new applications for each of the clients.

Anyway, lets get to creating the Azure Applications in our Azure Active Directory.

 

Creating the applications

To create the application, I browsed to https://manage.windowsazure.com and logged in with Work account.

image

Browsed to the Active Directory icon and clicked the link, next I opened my Active Directory Domain “iThink SharePoint”.

I clicked on Applications

This brings up the dashboard for the domain, which looks like below

image

From the footer, I clicked “Add” and chose “Add an application my organisation is developing”.

Then the following screen was displayed, a name was given to the application and the web application / web api option was chosen

image

Next we need to provide the Sign-On URL and App Id Url.

The Sign-On URL is the start page when the application needs to login and the App Uri Id, is the unique URI that that the application is identified with when trying to authenticate with the application.

image

The App Id Uri, should not be confused with the Client Id which is a GUID that we use to identify the application later on.

Now we click the tick box and the application is created.

Within our application we can see what the Client Id is by clicking on the Configure tab.

image

The example of the app shown below has been deleted but shows you all the settings.

image

image

The Reply Url section will need to be populated with the URLs that we are going to use to debug and test the application. These URL are trusted by the application as a URL that can be redirected to as part of the OAuth flow.

You can also upload a custom logo here, maybe we will do that when we get a bit further with the development of the app!

 

The section as the bottom is the Application permissions which we will cover this later.

image

 

The last point that I will make with this page is the Manifest button

image

We can use this to download and upload the application manifest. This can be used to configure additional application settings, that cannot be configured by the UI. An example would be , setting up custom application permissions. More on that later.

 

Now we have created one application, I could create one for the Web API application, but Visual Studio does a great job of setting these up so we will let it do its magic once we get to the Web API project configuration.

 

Adal.JS (Active Directory Authentication Library)

So now that we have the application, we need to setup Angular to use Adal to login.

I want users to login to the Angular client before they can use it.

This library allows us to integrate our application so that we can authenticate users with Azure AD.

As I have mentioned in my first post, we are using Adal.JS and the man with the knowhow is Vittorio Bertocci who has a great post, Introducing ADAL JS v1.

Adal.JS is packaged up for Angular as a bower package, so can be installed using bower install adal-angular. I have already done this in the client environment post.

Now that we have the adal-angular package installed in the solution, we can start to integrate it with our application.

 

Linking Adal.js into the application

First we need to add the Adal.js library so that it is referenced in our application. Next, we need to tell our Angular application that it is a dependency and then finally we can start to configure Adal. This will ensure that it knows about our application and where it is hosted.

Firstly, lets add the Adal.JS references to the application. This is achieved by the following to /app/index.html

/bower_components/adal-angular/lib/adal.js
/bower_components/adal-angular/lib/adal-angular.js
 

Adal Angular comes with two versions of the library, a minified version which is found in /bower_components/adal-angular/dist/ folder and a debug/dev version which is in the folder /bower_components/adal-angular/lib/ folder.

I am linking to the debug/dev version which is not minified but allows us to be able to debug what’s going on!

 

Ok, so now we have the library referenced in our application. We need to tell Angular to load it as part of its list of dependencies. To do that I modified the following line in /app/app.js  to add the dependency ‘AdalAngular’

 

var invoiceFormApp = angular.module(‘itspInvoiceFormApp’,
[
   ‘ngRoute’, ‘invoiceControllersModule’, ‘dataModelService’, ‘AdalAngular’
]);

Once we have the dependency, we are ready to setup Adal.JS configuration. How exciting!

 

To initialise the Adal,JS library it needs to know the following information.

  • instance – this is the Microsoft Azure login page to direct to, it always seems to be “https://login.microsoftonline.com/
  • tenant – this is the domain name for your tenant, so I have the domain ithinksharepoint.com so that is what I use it, but it might be something like company.onmicrosoft.com if you do not have a custom domain setup.
  • client id – this is the GUID that we saw when setting up the Azure Application, it is sometimes referenced to as the App Id.

image

  • endpoints – we will talk about these later but they allow us to map a URL endpoint to resource, so that Adal,JS can authenticate against the endPoint correctly.

 

Angular Constants

When I was looking at where to store this information, I did originally have it hardcoded into the /app/app.js file. However, after more reading and thinking I decided on another approach, using Angular constants.

To ease the configuration for the app, I decided to keep it all in one place and created  an object stored as a Constant.

This will be part of the configurationService module which we will complete later on but let’s create this constant first.

The configuration service module is going to live in /app/services/configurationService.js. So a new file was created and the following added

var configurationService = angular.module('configurationServiceModule', []);
configurationService.constant('applicationConstants', {
'clientId':'7c27b6a6-1bb8-43c0-9b92-2eee2264cc71',
'tenantName':'ithinksharepoint.com',
'instance':'https://login.microsoftonline.com/&#39;,
'endPoints': {}
});

Please note that the clientId is not a valid one, but just shows you the value that you need to use.

Finally, we need to add the reference to the script in our /app/index.html file.

/app/services/configurationService.js

 

Configuring Adal.JS

As mentioned previously, there are some things we need to tell Adal.JS before it can do its magic. These are the instance, tenant and CliientId settings.

The following changes were made to the /app/app.js. file.

'use strict';
var invoiceFormApp = angular.module('itspInvoiceFormApp',
[
'ngRoute', 'invoiceControllersModule', 'dataModelService', 'AdalAngular', 'configurationServiceModule'
]);
var appStart = function($routeProvider, $httpProvider, adalProvider, applicationConstants) {
$routeProvider.when('/invoices/add', {
templateUrl:'/app/views/add-invoice.html',
controller: 'addInvoiceController',
requireADLogin: true
}).when('/invoices', {
templateUrl:'/app/views/list-invoices.html',
controller: 'listInvoicesController',
requireADLogin: false
}).otherwise({
redirectTo: '/invoices'
});
var instance=applicationConstants.instance;
var clientId=applicationConstants.clientId;
var tenantName=applicationConstants.tenantName;
var endPoints=applicationConstants.endPoints;
adalProvider.init({
instance: instance,
tenant: tenantName,
clientId: clientId,
endPoints: endPoints,
anonymousEndpoints:{},
extraQueryParameter: 'nux=1',
cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
}, $httpProvider);
};
invoiceFormApp.config(['$routeProvider', '$httpProvider', 'adalAuthenticationServiceProvider', 'applicationConstants', appStart]);
view raw app,js hosted with ❤ by GitHub

 

Error: Authentication Context is not defined

When I first put the code together, I got the error above. This was resolved by making sure index.html referenced both :

/bower_components/adal-angular/lib/adal.js
/bower_components/adal-angular/lib/adal-angular.js
 

Having Adal.JS installed also allows us to configure which routes require authentication, so we need to modify those too. This is achieved by adding the requireADLogin: true property to the route. Please refer to the app.js gist above for more info.

 

Changing the Homepage to handle Authentication

Now that we have the Adal.JS configured in our application, I thought I better handle authentication when the user hits our homepage.

So the following changes were made to the /app/contollers/invoiceControllers.js for the listInvoiceController.

'use strict'
var invoiceControllersModule = angular.module('invoiceControllersModule', []);
invoiceControllersModule.controller('listInvoicesController', ['$scope', '$location', 'adalAuthenticationService', function ($scope, $location, adalService) {
$scope.invoices = [];
$scope.error="";
$scope.showInvoiceList=function(){
return true;
};
$scope.load = function(){
var invoice=createInvoice();
invoice.reference="INV01";
invoice.companyName="Contoso";
invoice.agencyContact="Jack Black";
invoice.vatRate=20;
invoice.invoiceTotal=10000;
$scope.invoices.push(invoice);
invoice=createInvoice();
invoice.reference="INV02";
invoice.companyName="Fabrikam";
invoice.agencyContact="Jack Black";
invoice.vatRate=20;
invoice.invoiceTotal=10000;
$scope.invoices.push(invoice);
};
$scope.loggedIn=adalService.userInfo.isAuthenticated;
$scope.login = function loginStub(){
adalService.login().then(function(){
$location('#')
});
}
if(adalService.userInfo.isAuthenticated)
{
$scope.load();
}
function createInvoice(){
var invoiceObject={
reference: '',
companyName: '',
invoiceDate: '',
contact:'',
addressLine1:'',
addressLine2:'',
addressLine3:'',
addressLine4:'',
addressCity:'',
addressCounty:'',
addressPostCode:'',
addressCountry:'',
agencyName:'',
agencyContact: '',
invoiceLines: [
],
invoiceTotal:0,
currencyType:'£',
vatRate:0,
vatAmount:0,
invoiceTotalWithVat:0,
status:'',
createdBy:'',
modifiedBy:'',
created:'',
modified:'',
};
return invoiceObject;
};
}]);
invoiceControllersModule.controller('addInvoiceController', ['$scope', 'dataModelService', '$location', function($scope, dataModelService, $location){
$scope.invoice=new dataModelService.Invoice();
$scope.addInvoiceLine = function addInvoiceLine() {
var newInvoiceLine=new dataModelService.InvoiceLine();
$scope.invoice.invoiceLines.push(newInvoiceLine);
}
$scope.cancel = function cancelStub(){
$location.path('/');
}
$scope.status = "";
$scope.error="";
$scope.saveInvoice = function saveInvoiceStub(){
$scope.status="Successfully Saved Invoice";
$location.path("#");
return true;
};
}]);

 

Also, the following changes were made to list-invoices.html.

<div class="container">
<div class="row">
<div ng-show="loggedIn && showInvoiceList" class="col-md-10 col-sd-3">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<th>Date</th>
<th>Invoice Reference</th>
<th>Company Name</th>
<th>Total</th>
<th>Status</th>
</thead>
<tbody>
<tr class="clickable-row" ng-repeat="invoice in invoices">
<td>{{invoice.invoiceDate}}</td>
<td>{{invoice.reference}}</td>
<td>{{invoice.companyName}}</td>
<td>{{invoice.currencyType}}{{invoice.invoiceTotal}}</td>
<td>{{invoice.status}}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div ng-hide="loggedIn">
<button class="btn btn-primary" title="Login" ng-click="login()">Login</a>
</div>
</div>
<div class="row">
<div ng-show="error !== ''" class="col-sd-3">
Current Error: {{error}}
</div>
</div>
</div>

The changes use ng-hide to show the login button when the user is not logged in.

 

Note: whilst testing this I tried to upgrade to use 1.0.10 of Adal.JS but seem to have an error related to anonymousEndpoints. I am going to look into this but all was fixed when I reverted back to v1.0.8 of Adal Angular.

A couple of problems that I had included a fix to the Azure Application to allow add the redirectUrl: http://localhost:8080/app/index.html  to the list of Reply Urls.

image

Update this section of Azure Application configuration

image

 

 

 

Next Session

So we are now at at the end of the session, thanks for reading.

We have Adal.JS integrated and we have authentication, we are ready to start bring in the WebAPI MVC side. We can move the Invoices to be held in the API and start allowing us to create, edit and view the invoices.

I hope you find the information useful, please let me know if things are unclear or you need more information about anything that I have talked about.

The code that was commited for this episode can be found in Github: https://github.com/ithinksharepoint/IT365.InvoiceFormApp.Client/commit/e02ac5c2af67245cbd5bacd7886b96fb4406c148