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.
The āApplicationsā tab contains the applications that you have available in your Azure AD.
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.
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
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
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.
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.
The example of the app shown below has been deleted but shows you all the settings.
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.
The last point that I will make with this page is the Manifest button
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.
- 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/', | |
'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]); |
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.
Update this section of Azure Application configuration
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