Keisha W
Keisha W

Reputation: 756

Accessing SharePoint using Microsoft Graph through Office Add-in (Word JS)

I have developed an Office.js Word add-in and I am using the SSO feature as described here. It uses the Office.Auth interface method getAccessTokenAsync, shown here. This works as described, but the issue comes when I try to use more scopes to access SharePoint using Microsoft Graph.

TL;DR I am unable to successfully make Microsoft Graph calls to access SharePoint with the token generated from the Office.Auth interface getAccessTokenAsync method.

Full Description

I have done the following:

  1. Added the Sites.ReadWrite.All and Sites.Manage.All permissions in the Azure AD app registration.
  2. Added the additional scopes in the Word Add-in manifest.

    <WebApplicationInfo>
      <Id>$application_GUID here$</Id>
      <Resource>api://localhost:44355/$application_GUID here$</Resource>
      <Scopes>
          <Scope>Files.Read.All</Scope>
          <Scope>Sites.ReadWrite.All</Scope>
          <Scope>Sites.Manage.All</Scope>
          <Scope>offline_access</Scope>
          <Scope>openid</Scope>
          <Scope>profile</Scope>
      </Scopes>
    </WebApplicationInfo>
    
  3. Confirmed using Graph Explorer that I can make the SharePoint Graph calls, as my user.

    GET https://graph.microsoft.com/v1.0/sites/{site-id}/lists/{list-id}/items/{item-id}
    
  4. Added the additional scopes in the Authorize method for when the user is not logged into Word, as described here.

    ConfidentialClientApplicationBuilder clientBuilder = ConfidentialClientApplicationBuilder.Create(Settings.AzureADClientId);
    clientBuilder.WithClientSecret(Settings.AzureADClientSecret);
    clientBuilder.WithRedirectUri(loginRedirectUri.ToString());
    clientBuilder.WithAuthority(Settings.AzureADAuthority);
    
    ConfidentialClientApplication clientApp = (ConfidentialClientApplication) clientBuilder.Build();
    string[] graphScopes = { "Files.Read.All", "User.Read", "Sites.ReadWrite.All", "Sites.Manage.All" };
    
    // Get and save the token.
    var authResultBuilder = clientApp.AcquireTokenByAuthorizationCode(
        graphScopes,
        Request.Params["code"] // The auth 'code' parameter from the Azure redirect.
    );
    
    try
    {
        var authResult = await authResultBuilder.ExecuteAsync();
        ViewBag.AccessToken = authResult.AccessToken;
    }
    catch (Exception e)
    {
        ViewBag.Error = e.Message;
    }
    
    return View();
    

What Works When the user is not logged into Word and has to call the Authorize method, I am able to use the returned token to successfully make my Graph calls.

What does not Work When the user is already logged into Word and the add-in JS generates the token using the getAccessTokenAsync method, I get 'Unauthorized' when trying to make the same Graph calls using the returned

Question Is there something extra I need to do for the Office JS getAccessTokenAsync method to return an authorized token for use with my Graph calls to SharePoint?

Edit Post Further Investigation
I used jwt.io to decode the tokens received.

  1. With the older getAccessTokenAsync method when logged in (scenario that does not work), I get a token containing the following:
{
  "aud": $application_GUID$,
  "iss": "https://login.microsoftonline.com/$tenant_GUID$/v2.0",
  "iat": ...,
  "nbf": ...,
  "exp": ...,
  "aio": ...,
  "azp": ...,
  "azpacr": "0",
  "name": $user_full_name$,
  "oid": $GUID$,
  "preferred_username": $user_email$,
  "scp": "access_as_user",
  "sub": ...,
  "tid": $tenant_GUID$,
  "uti": ...,
  "ver": "2.0"
}
  1. With the newer getAccessToken method, both when logged in and prompted to log in, (scenarios that also do not work), I get a token containing the same as above.
  2. With the older getAccessTokenAsync method, when prompted to log in and taken to theAzureADAuthController, (scenario that does work!), I get a token containing the following:
{
  "aud": "00000003-0000-0000-c000-000000000000",
  "iss": "https://sts.windows.net/$tenant_GUID$/",
  "iat": ...,
  "nbf": ...,
  "exp": ...,
  "acct": 0,
  "acr": "1",
  "aio": ...,
  "amr": [
    "pwd"
  ],
  "app_displayname": "Office-Add-in-ASPNET-SSO",
  "appid": $application_GUID$,
  "appidacr": "1",
  "deviceid": $GUID$,
  "family_name": $user_last_name$,
  "given_name": $user_first_name$,
  "ipaddr": ...,
  "name": $user_full_name$,
  "oid": $GUID$,
  "platf": "3",
  "puid": ...,
  "scp": "Files.Read.All openid profile Sites.Manage.All Sites.ReadWrite.All User.Read email",
  "sub": ...,
  "tid": $tenant_GUID$,
  "unique_name": $user_email$,
  "upn": $user_email$,
  "uti": ...,
  "ver": "1.0",
  "xms_st": {
    "sub": ...
  },
  "xms_tcdt": ...
}

Notice the difference in scopes, where the latter, older version has fully listed scopes, and I am able to authorize this way.

What could explain this?

Upvotes: 2

Views: 748

Answers (1)

Rick Kirkham
Rick Kirkham

Reputation: 9674

Not much of an answer, but too long to put in a comment:

That sample changed recently to use the new OfficeRuntime.auth.getAccessToken. You seem to be using an older version that uses Office.auth.getAccessTokenAsync. That should still work, but it was a preview API and may stop working at some point. Consider cloning the newer version. But wait a few days. I'm about to push some fixes to the new version.

Check to see if you've also added the new scopes to the graphScopes array in the Login action method. (In addition to adding them to the Authorize action method as you've already done.)

Exactly which HTTP call is returning the "Unauthorized"?

What error is being retuned from getAccessToken (or getAccessTokenAsync)? It usually has a 13xxx number.

UPDATE 12/19/19:

Based on your description in your comment "To clarify...", it looks like you are including the token that you get from getAccessToken in your call to MS Graph. This will not work. That token is a "bootstrap token". It gives Office access to your add-in's web app. The web app then needs to use the On Behalf Of Flow to swap that token for a token that gives the web app access to MS Graph. You then include this second token as the Authorization header in the call to Graph. The sample that you linked to in the first sentence of your question does that. Please also see these articles:

Enable SSO in Office Add-ins

Authorize to Microsoft Graph

Finally, be sure that there is a blank space between the string "Bearer" and the token in the Authorization header.

Upvotes: 2

Related Questions