Monday, October 5, 2015

O365 Dev Challenges - Part 6 - Switching to the Office 365 unified API (preview)

I now have a working solution which can read a user’s profile and also send e-mail on that user’s behalf. With my newly acquired understanding of how O365 resources and authorization works, why not simplify how API’s are called a bit and use the unified API? Effectively replacing ActiveDirectoryClient and OutlookServicesClient with just one client.

Before reading on I’ll tell you that I reverted this route myself as the Unified API client was unable to let me send an e-mail, bombing out with and OData class mapping error. This seems to be a bug on the Microsoft side of things, but if you do pure REST it will work. Using multiple clients is not that much of a hassle when you have the authentication part figured out anyways, and what is a few more server side calls behind the scenes between friends anyway? :-)

Still up for it…. continue reading.

Add a nuget package for the Office 365 unified API (preview)

Open up the nuget package manager, check the Include prerelease box and search for Microsoft.Graph.

clip_image002

Click Install to bring down the code.

Add application manifest settings for the Unified API

Once the nuget package is installed the application manifest in AAD has to be updated with settings for the Unified API. This is not yet available via the connected service wizard in Visual Studio, so instead head over to the Azure management portal, pick your AAD, then the application registration and configure it there.

image

Click Add application, pick Office 365 unified API (preview), and configure the delegated permissions to be able to Send mail as signed-in user and Sign in and read user profile. As we’re using OWIN for login it seems you have to keep the Windows Azure Active Directory delegated permissions for login. If not, your application will fail on sign-in.

When a user consents to the application the dual permissions looks a bit weird as allow sign-in and reading the profile appears twice.
clip_image002[8]

Authorization code

Next up is adding the authorization call to StartupAuth.cs for the Unified API endpoint - https://graph.microsoft.com. Replace the two existing ones with:

public const string UnifiedResourceId = "https://graph.microsoft.com";

AuthorizationCodeReceived = (context) =>
{
    var code = context.Code;

    ClientCredential credential = new ClientCredential(clientId, appKey);
    string tenantID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
    string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;

    AuthenticationContext authContext = new AuthenticationContext(aadInstance + tenantID, new ADALTokenCache(signedInUserID));
    AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, UnifiedResourceId);

    return Task.FromResult(0);
}

Switch out ActiveDirectoryClient and OutlookServicesClient with GraphService

Open up UserInfo.aspx.cs and you can now load the user’s profile with this code

var servicePointUri = new Uri(UnifiedResourceId);
var serviceRoot = new Uri(servicePointUri, "/beta/" + tenantID);

var unifiedClient = new GraphService(serviceRoot,
    async () => await TokenHelper.GetTokenForApplicationSilent(TokenHelper.UnifiedResourceId));

IUser user = unifiedClient.Me.ExecuteAsync().Result;

You also need to change the aspx page to the correct type as the IUser interface is different between the Unified API’s classes and the Graph Service classes.

The send email code now looks like this (except it bombs at the time of writing October 2015):

private Task SendEmailTaskUnified()
{
    return Task.Run(async () =>
    {
        var tenantId =
            ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
        var servicePointUri = new Uri(TokenHelper.UnifiedResourceId);
        var serviceRoot = new Uri(servicePointUri, "/beta/" + tenantId);

        var unifiedClient = new GraphService(serviceRoot,
            async () => await TokenHelper.GetTokenForApplicationSilent(TokenHelper.UnifiedResourceId));

        // Load your profile and retrieve e-mail address - could have been cached on initial page load
        var user = unifiedClient.Me.ExecuteAsync().Result;

        var body = new Microsoft.Graph.ItemBody
        {
            Content = "<h1>YOU DID IT!!</h1>",
            ContentType = Microsoft.Graph.BodyType.HTML
        };

        var toRecipients = new List<Microsoft.Graph.Recipient>
        {
            new Microsoft.Graph.Recipient
            {
                EmailAddress = new Microsoft.Graph.EmailAddress {Address = user.mail}
            }
        };

        var newMessage = new Microsoft.Graph.Message
        {
            Subject = "O365 Mail by Mikael",
            Body = body,
            ToRecipients = toRecipients,
            Importance = Microsoft.Graph.Importance.High
        };
        await unifiedClient.Me.SendMailAsync(newMessage, true);
    });
}

In my case the above code bombs on the last line to send the e-mail. Instead you can keep using the OutlookClient without having to register the resource endpoint in StartupAuth.cs. Seems the authorization token from the Unified API handles this just fine. Or you can copy the SendMessageAsync snippet from https://github.com/OfficeDev/O365-UWP-Unified-API-Snippets/blob/master/O365-UWP-Unified-API-Snippets/Users/UserSnippets.cs which uses the Unified API endpoint with pure REST. This actually works compared to the GraphClient which uses OData. Replace the code to retrieve the token in the snippet with the one you already have and pass it in to the function.

string token = await TokenHelper.GetTokenForApplicationSilent(TokenHelper.UnifiedResourceId);
await SendMessageAsync("O365 Mail by Mikael", "<h1>YOU DID IT!!</h1>", user.mail, token); 

No comments:

Post a Comment