Editor’s note: The following post was written by Office 365 MVP Martina Grom and Client Development MVP Toni Pohl. This is the conclusion of the 4 part series. Read part 1, part 2 and part 3
Identity in Your Own Apps with Windows Azure Active Directory
Part 4: Using GraphAPI
GraphExplorer from part3 (link) is very helpful to test requests against GraphAPI (http://bit.ly/1eNLLnG ) and to see what results you get. If we want to use information from WAAD and offer user and group management in our own app we need use GraphAPI of Windows Azure Active Directory (WAAD). Before WebAPI was available WAAD only could be administered with PowerShell which is great for automated management and scripts, but not for usage in apps – the overhead is too big. So, we´re happy that GraphAPI is available since about spring 2013. Let´s extend our sample project we created in part 1 (link).
To understand how requests to GraphAPI are made first have a look into the method that reads information about the logged in user. The code is available in the Home Controller of the project, in \Controllers\HomeController.cs.
Navigate to public async Task<ActionResult> UserProfile(). This is what happens when the user clicks onto the username. Here we show only the relevant parts of this class.
private const string TenantIdClaimType = "http://schemas.microsoft.com/identity/claims/tenantid";
private const string LoginUrl = "https://login.windows.net/{0}";
private const string GraphUrl = "https://graph.windows.net";
private const string GraphUserUrl = "https://graph.windows.net/{0}/users/{1}?api-version=2013-04-05";
private static readonly string AppPrincipalId = ConfigurationManager.AppSettings["ida:ClientID"];
private static readonly string AppKey = ConfigurationManager.AppSettings["ida:Password"];
// …a little bit more code…
[Authorize]
public asyncTask<ActionResult> UserProfile()
{
string tenantId = ClaimsPrincipal.Current.FindFirst(TenantIdClaimType).Value;
// Get a token for calling the Windows Azure Active Directory Graph
AuthenticationContext authContext = newAuthenticationContext(String.Format(CultureInfo.InvariantCulture, LoginUrl, tenantId));
ClientCredential credential = new ClientCredential(AppPrincipalId, AppKey);
AuthenticationResult assertionCredential = authContext.AcquireToken(GraphUrl, credential);
string authHeader = assertionCredential.CreateAuthorizationHeader();
string requestUrl = String.Format(
CultureInfo.InvariantCulture,
GraphUserUrl,
HttpUtility.UrlEncode(tenantId),
HttpUtility.UrlEncode(User.Identity.Name));
HttpClient client = newHttpClient();
HttpRequestMessage request = newHttpRequestMessage(HttpMethod.Get, requestUrl);
request.Headers.TryAddWithoutValidation("Authorization", authHeader);
HttpResponseMessage response = await client.SendAsync(request);
string responseString = await response.Content.ReadAsStringAsync();
UserProfile profile = JsonConvert.DeserializeObject<UserProfile>(responseString);
return View(profile);
}
First the app creates a new AuthenticationContext with the Login-URL and the domainID https://login.windows.net/{0}. A new ClientCredential with App-ID and AppSecret is created. Then an AuthenticationResult is made with the GraphAPI-URL https://graph.windows.net (we saw in part 3 when using GraphExplorer) and the credentials of our app. With that a new HTTP Get-Request is formed with the GraphAPI-URL for the user we want to get: https://graph.windows.net/{0}/users/{1}?api-version=2013-04-05 . The tenant-ID and username are inserted. Also the version of GraphAPI is passed so that we ensure that we get the expected result (2013-04-05 is the last official version used by Visual Studio). Now the request is sent. VS also created a model for our user-object in \Models\HomeViewModels.cs which we can use for storing the user data. There are not much properties in it, only DisplayName, GivenName and Surname.
The result of our HTTP-request is JSON as seen before in part 3. So this data has to be serialized into our UserProfile which happens in line UserProfile profile = JsonConvert.DeserializeObject<UserProfile>(responseString); .
The last return delivers the data to the View in \Views\Home\Userprofile.cshtml where the UserProfile is visualized.
To show how simple it is to extend these objects change the userprofile-model in \Models\HomeViewModels.cs. We add two lines with City and ObjectId.
namespace MyPortal.Models
{
public classUserProfile
{
public string DisplayName { get; set; }
public string GivenName { get; set; }
public string Surname { get; set; }
public string City { get; set; }
public string ObjectId { get; set; }
}
}
And of course we add these properties in \Views\Home\Userprofile.cshtml . Because <table> isn´t really necessary for our key-values we simply replace the whole HTML <table> with this <div>-block:
<divclass="span12">
<p>DisplayName: @Model.DisplayName</p>
<p>GivenName: @Model.GivenName</p>
<p>SurName: @Model.Surname</p>
<p>City: @Model.City</p>
<p>ObjectId: @Model.ObjectId</p>
</div>
Run the app with F5, log in and click on the user. You see the extended user object with City and ObjectId, it´s working.
When doing more we could also code the other HTTP-Requests GraphExplorer delivers us. But doing this and creating models for all objects including error handling is a lot of work. So, let´s go the simple way and … use a project from MSDN: Download the MVC Sample App for Windows Azure Active Directory Graph project from http://bit.ly/19mOwhY .
Unzip the package and open it with Visual Studio. Modify web.config and replace the first three keys with the adapted ones from your own ASP.NET MVC sample app (part 1).
<appSettings>
<addkey="TenantDomainName" value="mvpdemo2014.onmicrosoft.com" />
<addkey="AppPrincipalId" value="f72129f4-8f1c-4fe5-a13b-16449032a4bf" />
<addkey="Password" value="MwFIu4…" />
Now run the app with F5. You should get the app start page. Click the link User Management. This sample app enables user and group management with GraphAPI. We see all users in our WAAD, can display, edit and change their properties.
There are methods for the Create Update and Delete operations (CRUD).
The best way to see how it works, run the app and analyze the code behind for understanding how the requests are made and handled. This sample works and can be included in your own project. The key element is the “DirectoryService” for accessing the GraphAPI – see the code in UserController.cs:
privateDirectoryDataService directoryService;
publicDirectoryDataService DirectoryService
{
get
{
if (directoryService == null)
{
directoryService = MVCGraphServiceHelper.CreateDirectoryDataService(this.HttpContext.Session);
}
return directoryService;
}
}
To add the functionality we need to integrate the solution of GraphHelper. We can get the whole GraphHelper solution from http://bit.ly/1auDTs3. The project is small and contains the complete library.
After unzipping we can add the GraphHelper project to our own solution, add a reference to the second solution and we´re almost ready to go.
And add the reference to your project:
Try to compile the solution, it should work without errors.
The next steps are to copy and adapt the desired functions (Controllers, Models and Views) from MVCDirectoryGraphSample. Create a directory Helpers, copy the two class files from the sample into your project and adapt all namespaces to yours (f.e. MyPortal). Redo all steps for your own project to compile and run again. This is the copy, paste and modify part. At the end you should have your own WAAD portal.
It´s a good idea to use the MVCDirectoryGraphSample project, it simplifies the whole access. If you look f.e. into DirectoryServiceReference.cs you see what we mean. There´s a lot of code and schemas for accessing the GraphAPI.
The deployment of the functions then is simple. For creating a new user we create a new user object, add it with Addtousers(user) to the directory service and call the SaveChanges() method.
user.userPrincipalName = string.Format(CultureInfo.InvariantCulture, "{0}@{1}", emailAlias, selectedDomain);
user.mailNickname = emailAlias;
DirectoryService.AddTousers(user);
DirectoryService.SaveChanges();
return RedirectToAction("Index");
For updating an user the user object is searched and the properties are updated in an extra function CopyPropertyValuesFromViewObject(user, refreshedUser);. Then UpdateObject(refreshedUser) and SaveChanges() are called.
User refreshedUser = DirectoryService.users.Where(it => (it.objectId == user.objectId)).SingleOrDefault();
refreshedUser.userPrincipalName = string.Format(CultureInfo.InvariantCulture, "{0}@{1}", emailAlias, selectedDomain);
refreshedUser.mailNickname = emailAlias;
CopyPropertyValuesFromViewObject(user, refreshedUser);
DirectoryService.UpdateObject(refreshedUser);
DirectoryService.SaveChanges(SaveChangesOptions.PatchOnUpdate);
These are short code blocks and easy to use within our MVC app.
We encourage you to download the sample app and experiment with GraphAPI. In our opinion Authorization is the next “big thing” for delivering enterprise apps with Single Sign On mechanism. We hope our small series delivers the idea of using Windows Azure Active Directory in your own (enterprise) apps and you find it useful!
About the authors
Toni Pohl has worked for nearly 20 years as an independent IT expert. Toni began his IT career in the early 1990s as a trainer. After working as a trainer, his emphasis changed to the field of consulting and developing software solutions using database systems. In 1999 Toni Pohl founded atwork information technology group along with Martina Grom. They share the business administration. He is responsible for new business development, technology, and quality assurance. Toni Pohl has been awarded in 2013 by Microsoft with the Most Valuable Professional Award (MVP) for his expertise in Client Development. Toni blogs about new technologies for the atwork-blog, in Microsoft-TechNet-Team-Austria Blog, in codefest.at, the Austrian MSDN Blog and in cloudusergroup and is writing technical articles for various magazines. You can connect with him on Twitter, Facebook, or Linkedin.
Martina Grom works as IT-Consultant & is co-founder and CEO of atwork information technology. atwork is located in Austria/Europe and is specialized in development of Online Solutions. Martina & Toni founded atwork in 1999, and she has worked in IT since 1995. Martina is recognized as an expert in Microsoft Office Online Services solutions and was worldwide one of the first 8 MVP’s awarded in 2011 for her expertise in Office 365. She writes numerous articles and blog posts. Her passion is Online & Social Media, cloud computing and Office 365. Martina consults companies on their way to the cloud. Martina has a master degree in international business administration of University of Vienna, You can connect with her on Twitter, Facebook, Linkedin or join her cloudusergroup.
About MVP Monday
The MVP Monday Series is created by Melissa Travers. In this series we work to provide readers with a guest post from an MVP every Monday. Melissa is a Community Program Manager, formerly known as MVP Lead, for Messaging and Collaboration (Exchange, Lync, Office 365 and SharePoint) and Microsoft Dynamics in the US. She began her career at Microsoft as an Exchange Support Engineer and has been working with the technical community in some capacity for almost a decade. In her spare time she enjoys going to the gym, shopping for handbags, watching period and fantasy dramas, and spending time with her children and miniature Dachshund. Melissa lives in North Carolina and works out of the Microsoft Charlotte office.