Introduction
Recently, a fellow Microsoft Premier Field Engineer reached out to me to ask for some assistance in solving a problem that he had encountered while working with a customer that was building an MVC based SharePoint add-in that was attempting to access Azure Active Directory via the Azure Graph API. The customer had code that was quite a bit more sophisticated, but generally performed the following work:
[SharePointContextFilter] public ActionResult Index() { Uri baseServiceUri = new Uri(ConfigurationManager.AppSettings["AzureGraphURL"]); ActiveDirectoryClient adClient = new ActiveDirectoryClient(new Uri(baseServiceUri, ConfigurationManager.AppSettings["O365Domain"]), async () => { return await GetAccessToken(); }); Microsoft.Azure.ActiveDirectory.GraphClient.User user = new Microsoft.Azure.ActiveDirectory.GraphClient.User(); user.GivenName = "John"; user.Surname = "Doe"; user.DisplayName = "John Doe"; user.PasswordProfile = new PasswordProfile() { Password = "pass@word1" }; user.UserPrincipalName = "john.doe@jonhussdev.onmicrosoft.com"; user.UsageLocation = "US"; user.MailNickname = "john.doe"; user.AccountEnabled = true; adClient.Users.AddUserAsync(user); return View(); } private async Task<String> GetAccessToken() { string url = ConfigurationManager.AppSettings["AzureGraphAuthURL"]; string appId = ConfigurationManager.AppSettings["AzureID"]; string appSecret = ConfigurationManager.AppSettings["AzureSecret"]; string serviceRealm = ConfigurationManager.AppSettings["AzureServiceRealm"]; AuthenticationContext context = new AuthenticationContext(url); ClientCredential credential = new ClientCredential(appId, appSecret); AuthenticationResult token = await context.AcquireTokenAsync(serviceRealm, credential); return token.AccessToken; }
Seems simple enough, right? Right. That’s because it is. However, when they ran the code, it would hang on the following line:
AuthenticationResult token = await context.AcquireTokenAsync(serviceRealm, credential);
They made all sorts of efforts to find a solution. Debugging, Fiddler trace, NetMon, etc. and nothing gave any indication of what might be happening. As it turns out, the problem was pretty obvious once you knew the answer.
All they needed to do was follow the async/await pattern all the way up through the controller. For the sample code above, they needed to make two simple changes:
[SharePointContextFilter] public async Task<ActionResult> Index() { Uri baseServiceUri = new Uri(ConfigurationManager.AppSettings["AzureGraphURL"]); ActiveDirectoryClient adClient = new ActiveDirectoryClient(new Uri(baseServiceUri, ConfigurationManager.AppSettings["O365Domain"]), async () => { return await GetAccessToken(); }); Microsoft.Azure.ActiveDirectory.GraphClient.User user = new Microsoft.Azure.ActiveDirectory.GraphClient.User(); user.GivenName = "John"; user.Surname = "Doe"; user.DisplayName = "John Doe"; user.PasswordProfile = new PasswordProfile() { Password = "pass@word1" }; user.UserPrincipalName = "john.doe@jonhussdev.onmicrosoft.com"; user.UsageLocation = "US"; user.MailNickname = "john.doe"; user.AccountEnabled = true; await adClient.Users.AddUserAsync(user); return View(); } private async Task<String> GetAccessToken() { string url = ConfigurationManager.AppSettings["AzureGraphAuthURL"]; string appId = ConfigurationManager.AppSettings["AzureID"]; string appSecret = ConfigurationManager.AppSettings["AzureSecret"]; string serviceRealm = ConfigurationManager.AppSettings["AzureServiceRealm"]; AuthenticationContext context = new AuthenticationContext(url); ClientCredential credential = new ClientCredential(appId, appSecret); AuthenticationResult token = await context.AcquireTokenAsync(serviceRealm, credential); return token.AccessToken; }
And that’s it. Just those simple changes and it ran fine.
For more information on asynchronous operations in MVC, take a look at this ASP.NET blog post: http://www.asp.net/mvc/overview/performance/using-asynchronous-methods-in-aspnet-mvc-4
Awesome Job, Have wasted more than 2 days on this. Its people like you who make google a truly wonderful thing. Thank You once again.
Thank you very much, that was great help
I have to say you are a time saver!
What the value of AzureServiceRealm? From which place in Azure portal I can copy it?
Hi Michael,
It’s just a known value. In the case of that particular demo, the value is: “00000002-0000-0000-c000-000000000000/graph.windows.net@jonhussdev.onmicrosoft.com”. Just replace my tenant name with your tenant name and you should be set.
Jonathan
you are good helper, thank you