Freigeben über


Connecting to Office 365 APIs from a Windows 10 UWP

Unless you have been living under a rock, you probably heard that Microsoft released Windows 10 last week. For app developers, Windows 10 and the new Universal Windows Platform (UWP) realizes a vision of write-once run on any Windows device (desktop, tablet, mobile). In this post, I’ll illustrate how to build a Windows 10 UWP connected to Office 365 using the new WebAccountProvider approach.

[View:https://www.youtube.com/watch?v=Ui2g8Fl79y0]

Truly Universal

Microsoft first introduced the concept of a Universal Windows App at the Build Conference in 2014. This first generation universal app contained separate projects for desktop and mobile with a “shared” project for common code. The goal was to put as much code as possible in the shared project, which often required some technical gymnastics to accomplish. The Windows 10 UWP collapses this 3-project solution into a single unified project.

Old Universal App Structure New Universal App Structure

 

Connecting to Office 365

Connecting to Office 365 from a Windows 10 UWP uses an updated Connected Service Wizard within Visual Studio 2015. This wizard registers the native application in Azure AD, copies details from Azure AD into the application (ex: Client ID, authority, etc), and pulls down important Nuget packages such as the Office 365 SDK.

Once the Office 365 Service has been added to the UWP, you can start coding against the Office 365 APIs (either via REST or the Office 365 SDK). However, all the Office 365 APIs require access tokens from Azure AD which requires the app to perform an OAuth flow. In the past, native Windows apps used a WebAuthenticationBroker to manage this flow. The WebAuthenticationBroker was a browser control on OAuth steroids. The Azure AD Authentication Libraries (ADAL) automatically leveraged this when you requested a token. The WebAuthenticationBroker worked great, but didn’t always look great within an app given it was loading a framed login screen. The WebAuthenticationBroker still exists in 2015, but the WebAccountProvider is a new mechanism to UWPs and provides a first class experience.

The WebAccountProvider is optimized for multi-provider scenarios. Imagine building a UWP that leverages file storage across a number of providers (ex: OneDrive, OneDrive for Business, DropBox, Box, etc). Or maybe files from one place but calendar from another. The WebAccountProvider handles these scenarios and token management in a more generic and consistent way when compared to WebAuthenticationBroker. The WebAccountProvider will be the default authentication experience for Office 365 in a Windows 10 UWP. In fact, if you look at the application that the Connected Service Wizard registers in Azure AD, you will notice a new reply URI format that is specific to supporting the WebAccountProvider:

Working with the WebAccountProvider is very similar to traditional ADAL. We will use it to get access tokens by resource. When we do this, we will first try to get the token silently (the WebAccountProvider could have a token cached) and then revert to prompting the user if the silent request fails. Here is a completed block of code that does all of this:

Using WebAccountProvider to get Azure AD Access Tokens

private static async Task<string> GetAccessTokenForResource(string resource){    string token = null;     //first try to get the token silently    WebAccountProvider aadAccountProvider = await WebAuthenticationCoreManager.FindAccountProviderAsync("https://login.windows.net");    WebTokenRequest webTokenRequest = new WebTokenRequest(aadAccountProvider, String.Empty, App.Current.Resources["ida:ClientID"].ToString(), WebTokenRequestPromptType.Default);    webTokenRequest.Properties.Add("authority", "https://login.windows.net");    webTokenRequest.Properties.Add("resource", resource);    WebTokenRequestResult webTokenRequestResult = await WebAuthenticationCoreManager.GetTokenSilentlyAsync(webTokenRequest);    if (webTokenRequestResult.ResponseStatus == WebTokenRequestStatus.Success)    {        WebTokenResponse webTokenResponse = webTokenRequestResult.ResponseData[0];        token = webTokenResponse.Token;    }    else if (webTokenRequestResult.ResponseStatus == WebTokenRequestStatus.UserInteractionRequired)    {        //get token through prompt        webTokenRequest = new WebTokenRequest(aadAccountProvider, String.Empty, App.Current.Resources["ida:ClientID"].ToString(), WebTokenRequestPromptType.ForceAuthentication);        webTokenRequest.Properties.Add("authority", "https://login.windows.net");        webTokenRequest.Properties.Add("resource", resource);        webTokenRequestResult = await WebAuthenticationCoreManager.RequestTokenAsync(webTokenRequest);        if (webTokenRequestResult.ResponseStatus == WebTokenRequestStatus.Success)        {            WebTokenResponse webTokenResponse = webTokenRequestResult.ResponseData[0];            token = webTokenResponse.Token;        }    }     return token;}

 

The WebAccountProvider also looks much different from the WebAuthenticationBroker. This should provide more consistent sign-in experience across different providers:

WebAuthenticationBroker WebAccountProvider
   

Once you have tokens, you can easily use them in REST calls to the Office 365 APIs, or use the GetAccessTokenForResource call in the constructor of Office 365 SDK clients (SharePointClient, OutlookServicesClient, etc).

Using Office 365 SDKs

private static async Task<OutlookServicesClient> EnsureClient(){     return new OutlookServicesClient(new Uri("https://outlook.office365.com/ews/odata"), async () => {        return await GetAccessTokenForResource("https://outlook.office365.com/");    });} public static async Task<List<IContact>> GetContacts(){    var client = await EnsureClient();    var contacts = await client.Me.Contacts.ExecuteAsync();    return contacts.CurrentPage.ToList();}

 

Using REST

public static async Task<byte[]> GetImage(string email){    HttpClient client = new HttpClient();    var token = await GetAccessTokenForResource("https://outlook.office365.com/");    client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);    client.DefaultRequestHeaders.Add("Accept", "application/json");    using (HttpResponseMessage response = await client.GetAsync(new Uri(String.Format("https://outlook.office365.com/api/beta/Users('{0}')/userphotos('64x64')/$value", email))))    {        if (response.IsSuccessStatusCode)        {            var stream = await response.Content.ReadAsStreamAsync();            var bytes = new byte[stream.Length];            stream.Read(bytes, 0, (int)stream.Length);            return bytes;        }        else            return null;    }}

 

Conclusion

The unification achieved with the new Windows Universal Platform (UWP) is exactly what Windows developers have been waiting for. Office 365 is poised to be a dominate force with Windows 10. Together, some amazing scenarios can be achieved that developers have the power to deliver. I have published two complete Windows 10 UWP samples on GitHub that you can fork/clone today:

Contacts API Win10 UWP
https://github.com/OfficeDev/Contacts-API-Win10-UWP

MyFiles API Win10 UWP
https://github.com/OfficeDev/MyFiles-API-Win10_UWP

Comments

  • Anonymous
    September 13, 2015
    The comment has been removed
  • Anonymous
    March 08, 2016
    The comment has been removed