Condividi tramite


An Architecture for SharePoint Apps That Call Other Services

This post will show an architecture for a SharePoint provider hosted app that calls other services such as the O365 API for Exchange, Azure AD Graph API, PowerBI API, or a custom Web API.

Background

This post is part of a series on building a SharePoint app that communicate with services protected by Azure AD.

I have been asked several times lately how a SharePoint provider hosted app can access other services such as Exchange Online API, PowerBI API, or the Azure AD Graph API.  This post will propose an architecture for how you to create a SharePoint provider-hosted app that calls services protected by Azure AD.  We will also discuss the benefits of this architecture.  I’m not going to show any code yet, we’ll get to that in an upcoming post.  For now, let’s agree on an approach.

Why Can’t I Just Use the SharePoint Access Token?

I’ve had this discussion with numerous developers.  If it’s all just OAuth, can’t I just use the access token that SharePoint gives me to call Exchange?  The answer is no.  When you create a new provider-hosted app using Visual Studio 2013, you are prompted to choose between a low-trust app or a high-trust app.  When creating a low-trust app, you choose the option to use Windows Microsoft Azure Access Control Service.

image

This enables our app to request an OAuth access token from Azure ACS to present to SharePoint Online.  This app is used for a very specific purpose: extending SharePoint.  This includes things like remote event receivers and deploying content to a SharePoint app web.  When the SharePoint app model was created, the Azure AD capability of issuing OAuth tokens wasn’t complete yet, hence why the current implementation relies on Azure ACS.   

image

If I create my SharePoint provider-hosted app, run the application, and decode the OAuth token using a tool like my Fiddler extension that decodes OAuth tokens, I can see that the issuer is Azure ACS and the audience is SharePoint.

image

The “aud” claim is the audience claim, indicating the audience intended for this access token.  Each access token is specific to an audience.  In this case, the audience for this token is <“00000003-0ff1-ce00-000000000000/kirke3.sharepoint.com@03fb46a-1140-4514-89xxxxxxxxx>”. 

It is true that Azure Active Directory also enables the ability to register applications so that you can request an OAuth access token. 

image

The token issued by Azure AD is not the same token as the one issued by Azure ACS, they differ by the claims they contain.  To show this, I used a different application (using the sample WebAPI-OnBehalfOf-DotNet on GitHub) and I decode the access token request to Graph API.  I see that the issuer is different.

image

More importantly, the audience claim is different.  The audience claim in this token is “https://graph.windows.net”, which is the Azure AD Graph API.  Tokens are intended to access a resource identified by the “aud” claim. 

Even though there is an Azure Active Directory implementation behind O365, the current implementation of SharePoint provider hosted apps does not use Azure AD to issue OAuth tokens, it uses Azure ACS.  We have an issuer mismatch problem and the token issued by ACS cannot be used to call a different service that expects a token issued by Azure AD.  More importantly, each token is intended for a specific resource identified by the “aud” claim.  I show an example of this in the post Call O365 Exchange Online API from a SharePoint App where we have four different access tokens for four different resources.

One more thing to point out… the set of claims in the SharePoint app token is different than the set of claims in the Azure AD Graph API token.  There are claims in a SharePoint app token that are specific to supporting SharePoint apps, specific claims that SharePoint requires in order to identify and process the request correctly such as the “identityprovider” claim.  I go into grave detail on this in the post High Trust SharePoint Apps on Non-Microsoft Platforms

A Tale of Two Tokens

We really need two tokens: one to call SharePoint using the app model (the one that uses Azure ACS), and a second one to call a service protected using Azure AD, such as the O365 Exchange API.

image

The access token issued by ACS is needed for provider hosted apps to facilitate things like remote event receivers.  The access token issued by Azure AD enables us to call services protected by Azure AD, such as the Office 365 Exchange Online API.  We’re not reusing the SharePoint app’s token to call other services, there are two distinct tokens. 

As mentioned in a previous blog post, Calling O365 APIs from your Web API on behalf of a user, we could introduce a custom Web API endpoint.  This opens up even more possibilities because we can delegate tokens on behalf of the current user, enabling us to delegate access.  Instead of our app just being a website that calls another service, we can leverage the much broader ecosystem that Azure Active Directory enables. 

image

The Value of an API

Taking it a step further… now that we have a Web API, we can now expose that to multiple types of clients.  Those clients might be a web site written with PHP, Node.js, Java, or .NET, it might be a native client application, or it could be a multi-device hybrid application written with the new Cordova tooling in Visual Studio. 

image

By changing our perspective of the problem, our SharePoint provider-hosted app becomes just another client that calls our custom Web API that is protected by Azure AD.  It just so happens to also call SharePoint using the token issued by Azure ACS to do some SharePoint-specific extensions.  I highlight the provider-hosted app below. 

image

The key to making all of this work seamlessly for the end user is to change your app to authenticate against the same directory that you are using for O365.  An example of doing this is provided online, WebApp-WebAPI-OpenIDConnect-DotNet.  When the user accesses your application, they would authenticate against Azure AD and obtain an access token.  The very best part is that the end user doesn’t have to actually type in their credentials: because they have already authenticated against Azure AD when they accessed the O365 site, the same authentication cookie is used and the user is silently authenticated with your provider hosted app. 

We’ll show an example of how to do this, but hopefully I’ve got you excited enough to see why this is so cool.

For More Information

WebAPI-OnBehalfOf-DotNet – sample for Azure AD that shows how to call a service on behalf of the caller

OAuth Fiddler Extension – extension for Fiddler that enables you to inspect OAuth tokens.

Calling O365 APIs from your Web API on behalf of a user – details on how to create a Web API that calls other services on behalf of the current user

WebApp-WebAPI-OpenIDConnect-DotNet – sample for Azure AD that shows how to protect a web application using OpenID Connect and a Web API using Azure AD bearer authentication.

Comments

  • Anonymous
    March 26, 2015
    But we actually can use access token acquired from the Azure AD to make requests to the SharePoint online. samlman.wordpress.com/.../using-adal-access-tokens-with-o365-rest-apis-and-csom

  • Anonymous
    March 26, 2015
    @Sergei - yes, and I was going to address that in an upcoming post :)  There are still reasons why this approach is required, specifically for those APIs that the O365 SharePoint Online API does not yet expose such as workflow APIs. Further, Steve's post proves my point.  There is a misconception by developers that you can somehow magically reuse an access token to access other services, the most popular being Exchange.  Steve's post shows how to do basically the same thing I am doing here, obtaining an access token to a specific resource.  The fact that you can use CSOM with an AAD access token doesn't change the fact that you need one token per resource. Finally, there can be tremendous value in creating your own consumable APIs that perform data transformation and augmentation instead of putting that burden on the client devices and replicating that logic.  

  • Anonymous
    May 19, 2015
    The comment has been removed