Monday, October 5, 2015

O365 Dev Challenges - Part 3 - What did the wizard leave behind?

Azure Artefacts

To see all that has happened after the VS2015 wizard has cast all its magical spells, let’s log into the Azure Management portal at https://portal.azure.com/, hit browse allresource groups, and click the new resource group you created in the wizard step.

image

First there is a Visual Studio component (which I actually didn’t check on the ASP.NET creation page, but no biggie), then there is the database server with the application database. Next is the service plan entry and the Azure Web Application I will publish to in the end.

Next navigate to the old Azure Management portal at https://manage.windowsazure.com/, pick Active Directory, pick the AD instance for your demo tenant, and click the Applications heading.

The application page should show the entry for the newly created application.

image

Click the application, and click the Configure heading.

image

You might have noticed the auto-generated name on the consent page, and this screen is the place to change it to something more readable. I’m replacing Pzl.SampleMultiTenancy_20150918180222 with My Cool Sample Multi-tenant App.

Scroll a bit down, and you may want to change the APP ID URI as well. I’m changing mine to https://techmikael.onmicrosoft.com/PzlSampleMultiTenancy.

image

The Client ID listed is the App Principal Id, and inside web.config you’ll see it sitting there with the general Azure login URL and client secret. Also note the SIGN-ON URL and REPLY URL fields, which right now are pointing to your local dev. I’ll change these later when I’m ready for production in Part 7! At the bottom of the above screenshot you see two delegated permissions, which are the once checked off in the creating wizard and which displayed at the consent screen shown.

image
Now I have a basic application up and running which can operate against the https://graph.windows.net/ service end-point in Office 365.

Authorization code

When I checked the Read directory data box during the authorization part of the project creation a couple of things happened. In the file StartupAuth.cs located in the App_Start folder a piece of code got injected to make sure you get authenticated and retrieve an authorization token from the graph.windows.net resource for the resources the application is allowed to use.

image

In the Models folder two files got added, AdalTokenCache.cs and ApplicationDbContext.cs. These two files and the injection into StartupAuth.cs is the automagic I was looking for to make my adventure a smooth ride into the sunset. Let someone else write the plumbing!

A reference to AdalTokenCache can be found in line 65 of StartupAuth.cs.

AuthenticationContext authContext = new AuthenticationContext(aadInstance + tenantID, new ADALTokenCache(signedInUserID));

What happens in these lines of code is that on application start the application will retrieve an authorization code, and cache the token for the resource specified, in this case https://graph.windows.net which contains operations against Azure AD. See Azure AD Graph API Common Queries for a full list of operations available.

Take a look at ActiveDirectoryClient

The home page does not take advantage of or need the consented rights, but another page has been added to the project, UserInfo.aspx. This page uses the ActiveDirectoryClient to load up the users display name, first name and last name from the Azure Active Directory user profile.


image

Uri servicePointUri = new Uri(graphResourceId);
Uri serviceRoot = new Uri(servicePointUri, tenantID);
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(serviceRoot,
      async () => await GetTokenForApplication());

// use the token for querying the graph to get the user details
IUser user = activeDirectoryClient.Users
    .Where(u => u.ObjectId.Equals(userObjectID))
    .ExecuteAsync().Result.CurrentPage.ToList().First();

You can tell this is demo code, as the same values can actually be retrieved from the user’s claim, saving yourself one extra http call.

var displayName = ClaimsPrincipal.Current.FindFirst("name").Value;
var givenName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.GivenName).Value;
var surname = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Surname).Value;