Azure AD ASP .NET Web Application

Hi all. Before I complete my series on Azure AD Provisioning with a look at how provisioning works with the Graph API, I want to take a detour and cover some Microsoft Visual Studio. Over the past month I’ve been spending some time building very basic applications.

As I’ve covered previously in my blog, integration is going to the primary responsibility of IT Professionals as more infrastructure shifts to the cloud. Our success at integration will largely depend on how well we understand the technologies, our ability to communicate what business problems these technologies can solve, and our understanding of how to help developers build applications to consume these technologies. In my own career, I’ve spent a significant time on all of the above except the last piece. That is where I’m focusing now.

Recently I built a small.NET forms application that integrated with the new Azure AD B2B API. Over the past few days I’ve been spending time diving with to ASP .NET Web Applications built with an MVC architecture. I decided to build an small MVC application that performed queries against the Graph API, and wow was it easy. There was little to no code that I had to provide myself and “it just worked”. You can follow these instructions if you’d like to play with it as well. If you’ve read this blog, you know I don’t do well with things that just work. I need to know how it works.

If you follow the instructions in the above link you will have a ASP .NET Web Application that is integrated with your Azure AD tenant, uses Azure AD for authentication via the Open ID Connect protocol, and is capable of reading directory data from the Graph API uses OAuth 2.0. So how is all this accomplished? What actually happened? What protocol is being used for authentication, how does the application query for directory data? Those are the questions I’ll focus on answering in this blog post.

Let’s first answer the question as to what Visual Studio did behind the scenes to integrate the application with Azure AD. In the explanation below I’ll be using the technology terms (OAuth 2.0 and Open ID Connect) in parentheses to translate the Microsoft lingo. During the initialization process Visual Studio communicated with Azure AD (Authorization Server) and registered (registration) the application (confidential client) with Azure AD as a Web App and gave it the delegated permissions (scopes) of “Sign in and read user profile” and “Read directory data”.

In addition to the registration of the application in Azure AD, a number of libraries and code have been added to the project that make the authentication and queries to the Graph API “out of the box”. All of the variables specific to the application such as Client ID, Azure AD Instance GUID, application secret key are stored in the web.config. The Startup.cs has the simple code that adds Open ID Connect authentication. Microsoft does a great job explaining the Open ID Connect code here. In addition to the code to request the Open ID Connect authentication, there is code to exchange the authorization code for an access token and refresh token for the Graph API as seen below with my comments.

AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
// MF -> Create a client credential object with the Client ID and Application key
ClientCredential credential = new ClientCredential(clientId, appKey);
// MF -> Extract the access token from cache and generate an authentication context from it
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new ADALTokenCache(signedInUserID));
// MF -> Acquire a token by submitting the authorization code, providing the URI that is registered for the application, the application secret key, and the resource (in this scenario the Graph API)
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
return Task.FromResult(0);
}

Now that the user has authenticated and the application has a Graph API access token, I’ll hop over to the UserProfileController.cs. The code we’re concerned about in here is below with my comments.


{
Uri servicePointUri = new Uri(graphResourceID);
Uri serviceRoot = new Uri(servicePointUri, tenantID);
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(serviceRoot, async () => await GetTokenForApplication());
// MF -> Use the access token previous obtained to query the Graph API
var result = await activeDirectoryClient.Users.Where(u => u.ObjectId.Equals(userObjectID)).ExecuteAsync();
IUser user = result.CurrentPage.ToList().First();
return View(user);
}

Next I’ll hop over to the UserProfile view to look at the Index.cshtml. In this file a simple table is constructed that returns information about the user from the Graph API. I’ve removed some of the pesky HTML and replaced it with the actions.


@using Microsoft.Azure.ActiveDirectory.GraphClient
@model User
@{
ViewBag.Title = "User Profile";
}
"CREATE TABLE"
"TABLE ROW"
Display Name
@Model.DisplayName
"TABLE ROW"
First Name
@Model.GivenName
"TABLE ROW"
Last Name
@Model.Surname
"TABLE ROW"
Email Address
@Model.Mail

Simple right? I can expand that table to include any attribute exposed via the Graph API. As you can see in the above, I’ve added email address to the display. Now that we’ve reviewed the code, let’s cover the steps the application takes and what happens in each step:

  1. App accesses https://login.microsoftonline.com//.well-known/openid-configuration
    • Get OpenID configuration for Azure AD
  2. App accesses https://login.microsoftonine.com/common/discovery/keys
    • Retrieve public-keys used to verify signature of open id connect tokens
  3. User’s browser directed to https://login.microsoftonline.com//oauth2/authorize
    • Request an open id connect id token and authorization code for user’s profile information
  4. User’s browser directed to https://login.microsoftonline.com//login
    • User provides credentials to AAD and receives back
      1. id token
      2. access code for graph API with Directory.Read, User.Read scope
  5. User’s browser directed back to application
    • Return id token and access code to application
      1. id token authenticates user to application
      2. Access code for graph API with Directory.Read, User.Read scope temporarily stored
  6. Application accesses https://login.microsftonine.com//oauth2/token
    • Exchanges access code for bearer token
  7. Application sends OData query to Graph API and attaches bearer token.

That’s it folks! In my next post I will complete the Azure AD Provisioning series with a simple ASP .NET Web app that provisions new users into Azure AD.

Attribute Uniqueness in Azure Active Directory

As I dive deeper into Azure Active Directory, I am learning quickly that AAD is a very different animal than on-premises Active Directory Domain Services (AD DS). While both solutions provide identity, authentication, and authorization services, they do so in very different ways. These differences require organizations to be prepared to adjust standard processes to get the two services to work together. Today I will focus on the identity portion of the solution and how the different attribute uniqueness requirements in AAD and AD DS can introduce the need for evolution of management processes for AD DS.

The attributes I want to focus on are userPrincipalName, proxyAddresses, and mail. In AD DS userPrincipalName is a single valued attribute, proxyAddresses is a multivalued attribute, and the values included in those attributes must be unique to the object in the forest. The mail attribute (the attribute that populates the E-mail field on the General tab of Active Directory Users and Computers (ADUC)) is a single valued attribute that doesn’t have a uniqueness requirement. In AAD all three attributes retain their single value or multivalued properties, however, the uniqueness requirements change considerably.

AD DS allows these values to be duplicated across different attributes. For example, one object could have a userPrincipalName of john@contoso.com and another object could have a value in its proxyAddresses attribute of SMTP:john@contoso.com. The same goes for an object that has a mail attribute of john@contoso.com and another object has a value in its proxyAddresses of john@contoso.com.

In AAD this is no longer true. User, group, and contact objects synchronized to AAD from AD DS require the userPrincipalName, proxyAddresses, and mail (also targetAddresses if you’re using it) to be unique among all objects in the directory. This means that each of the scenarios I discussed above will create synchronization errors. You can’t have one user object with a value in the proxyAddresses of john@contoso.com and another use object with mail attribute of john@contoso.com.

What happens if you do? Well, let’s make it happen. In this scenario we have two user objects with the configuration below:

Object 1
userPrincipalName: jess.felton@journeyofthegeek.com
proxyAddresses: SMTP:felton@feltonma.com
Sync Status: Already synced to Azure AD

Object 2
userPrincipalName: matt.felton@journeyofthegeek.com
proxyAddresses:
mail: felton@feltonma.com
Sync Status: Not yet synced to Azure AD

After we force a delta synchronization of Azure AD Sync, the errors provided below pop up in Synchronization Manager and an email alert:

Screen-Shot-2016-06-05-at-8.07.18-PM.png

Screen-Shot-2016-06-05-at-8.08.24-PM

The net result of the above matt.felton@journeyofthegeek.com won’t synchronize correctly to AAD and the user will be unable to authenticate to AAD. How about two user objects with the same mail attribute? That’s a common use case, right? Nope, same issue. Take note that just because you receive an error saying the issue is with a duplicate value in the proxyaddresses attribute, it could be the userPrincipalName, mail, or targetAddress of another object in AD DS.

Small differences like this can lead to major changes in how organizations manage AD DS when they begin their journey into AAD. The key take away here is to understand that AD DS and AAD are not the same thing, the differences need to be understood, and you must be prepared to evolve existing processes if you wish to leverage the solution.

I’ll end this with a thank you to Jimmie Lightner from MS for his blog post that brought light to this issue many months ago. You can read that post here.

P.S. Take note that if you opt to an alternate login ID (separate attribute from userPrincipalName for user identifier in AAD), the uniqueness will carry over to that attribute as well.