Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can I get access token for multiple resources based on sample Office-Add-in-Microsoft-Graph-React #588

Open
tiwarigaurav opened this issue Aug 30, 2023 · 16 comments

Comments

@tiwarigaurav
Copy link

Note: This repo is only for questions related to its samples. If you have questions about how to use office.js or the Office developer platform, please post your question on https://stackoverflow.com. Tag your question with office-js or outlook-web-addins

Question

Ask your question here.

I am developing an Outlook Web Add-In using

  • Yeoman generator-office
  • Office.Js
  • REACT framework
  • azure/msal-browser version 2.32.2

I followed the example from "Office-Add-in-Microsoft-Graph-React" and I am able to successfully get the access token for Graph API.

My question is how can I request access token for multiple resources using the msalInstance i.e. SharePoint, Onedrive.

@Rick-Kirkham
Copy link
Contributor

@tiwarigaurav Please ask this question on on https://stackoverflow.com/. Tag your question with office-js or outlook-web-addins

This repo is only for questions related to its samples.

@tiwarigaurav
Copy link
Author

@Rick-Kirkham - not sure why this was closed. My question specifically relates to the sample "Office-Add-in-Microsoft-Graph-React"

Using this sample I can get the Graph Access Token just fine and everything works.

My Question is - How can I get access token for multiple resources using the msalInstance.acquireTokenSilent(scope) within the office-apis-helpers.ts file?

The main idea is to use the login popup once to initiate the msalinstance and for any subsequent scopes/resources (or when the initial access token expires) use the acquireTokenSilent method - so that the user does not need to click the login button again to get another token.

@tiwarigaurav
Copy link
Author

@davidchesnut Please let me know if you need more details in investigating our issue. We anticipate quick response and resolution to above.

@Rick-Kirkham
Copy link
Contributor

@tiwarigaurav I leave it to @davidchesnut to decide, but I don't think this "relates" to the sample in the way that is intended. The sample doesn't have a bug and your question is not about the sample as it is. You are asking for help for a distinctly different scenario. You really should ask this question on Stack Overflow.

@tiwarigaurav
Copy link
Author

@Rick-Kirkham - what I am trying to imply is that the sample provided does not work in real-world scenarios, neither can we make it work in real world.

As per Microsoft documentation the -

Office dialog API, specifically the displayDialogAsync method is a completely separate browser instance from the task pane, meaning:

  • It has its own runtime environment and window object and global variables.
  • There is no shared execution environment with the task pane.
  • It does not share the same session storage (the Window.sessionStorage property) as the task pane.

Which means (my interpretation) is that we cannot use msalInstance.acquireTokenSilent(scope). And that every time we need to get a new access token we will need the user to click the login button.

With the above statement I can safely say that the sample provided under OfficeDev -> Office-Add-in-samples -> auth -> Office-Add-in-Microsoft-Graph-React cannot be used in real world (or the concept cannot be reused in real world). Unless I am missing something quite obvious which is why I have posted this question.

@tiwarigaurav
Copy link
Author

@davidchesnut - any updates for us on this issue?

@mattgeim
Copy link

mattgeim commented Sep 6, 2023

Hi @tiwarigaurav,

It appears you are looking for information on how to use MSAL in react to get additional scopes beyond the one used in this sample.

This sample in the Azure Active Directory documentation may be better aligned to help with your question. Specifically, the scopes are what you pass in to specify what API/scope of access you want granted in the token, which you can find in the 'Acquire token for an API' part of that sample. If you are still having issues with MSAL and accessing resources using it, the best spot to ask would be Stack Overflow using msal and msal.js tags.

Closing the issue on this sample as your question is about MSAL usage - if you find issues with MSAL.js you can more information on their GitHub page/open an issue there.

@mattgeim mattgeim closed this as completed Sep 6, 2023
@tiwarigaurav
Copy link
Author

tiwarigaurav commented Sep 6, 2023

@mattgeim - It is possibly my fault - the title of the question is a bit misleading. Let's take out the multiple scopes part out of the equation and concentrate on this sample - OfficeDev -> Office-Add-in-samples -> auth -> Office-Add-in-Microsoft-Graph-React

I am trying to imply is that the sample provided does not work in real-world scenarios, neither can we make it work in real world. i.e. every time we need to get a new access token we will need the user to click the login button and show the dialogue popup.

Perhaps this should be raised as a bug in the sample?

Note: from what I have gathered so far is that "Office Add-In + React + MSAL.JS + Graph API" will not work cross platform and cross browsers.

@mattgeim
Copy link

mattgeim commented Sep 7, 2023

Thanks for the response clarifying @tiwarigaurav.

This sample doesn't do SSO, its MSAL based and isn't trying to reduce sign-ins. If you are interested in SPA flows for SSO, you can read more about it here: https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-js-sso please note this is not related to add-ins, but rather SSO flows and MSAL.

This sample works for what it should do, but I will happily take your feedback and discuss with our team on ways to improve on clarity for others in the future - thank you for the feedback!

@tiwarigaurav
Copy link
Author

tiwarigaurav commented Sep 7, 2023

@mattgeim - this isn't about SSO with Office Add-Ins.

My feedback is that this sample Office-Add-in-Microsoft-Graph-React will NOT work in real world. There is a bug with Office Add-Ins Dialogue API I found this on MS documentation.

This sample should clearly state this in on the wiki page.

@davidchesnut
Copy link
Member

Hi @tiwarigaurav, If you want to use MSAL caching so that you don't have to pop up a dialog for every API call, see getAccessTokenMSAL in the Office-Add-in-NodeJS-SSO sample. You can also see how to send the home account ID from the dialog box in the handleResponse method. This is all MSAL code based on the Microsoft identity platform, and nothing specific to Office JS. In the comments for authConfig.js and authRedirect.js there are links to the original MSAL samples those are based on. However it is possible there are some Office platform/webview combinations that might not work with this MSAL caching mechanism (in which case it defaults back to popping up the auth dialog.)

I can look into updating the React sample down the road with the caching code. Are there any other issues you spotted in the sample?

Thanks!
David

@tiwarigaurav
Copy link
Author

@davidchesnut - thanks for responding. I tried this and it does NOT work.

I tried your suggestion with the below code (in my app's login.ts file):

     let msalInstance: PublicClientApplication = new PublicClientApplication({
      auth: {
      authority: "https://login.microsoftonline.com/organizations/",
      clientId: "xxxxxxx-xxxxxxx-xxxxxxx-xxxxxxx-xxxxxxx",
      navigateToLoginRequestUrl: false
      },
      cache: {
      cacheLocation: "localStorage",
      storeAuthStateInCookie: false,
      },
    });
    const authParamsGraph = {
      account: messageFromDialog.result.account,
      scopes: ["https://graph.microsoft.com/.default"]
    };
    console.log(messageFromDialog.result.account.homeAccountId);
    const accByHomeId = msalInstance.getAccountByHomeId(messageFromDialog.result.account.homeAccountId);
    console.log(accByHomeId);
    msalInstance.setActiveAccount(
      msalInstance.getAccountByHomeId(
        messageFromDialog.result.account.homeAccountId
      )
    );

With the above I was able to pass the homeAccountId from the Dialog box to the app but msalInstance.getAccountByHomeId(messageFromDialog.result.account.homeAccountId) returns null.

As per my research there is a bug with the Office Dialog API in which - the web add-in's Task Pane and Dialog Box do not share the same Local Storage. So, when we initialize a new msal instance in the app it has nothing in common with the msal instance from the Dialog box so the getAccountByHomeId will fail.

Note: I am testing this on Outlook desktop app running on Windows

@davidchesnut
Copy link
Member

Hi @tiwarigaurav,

I did some more research into this. The Microsoft Graph React auth sample solves this problem by storing the token in a variable. See line 51 in App.tsx. I checked with the team, and in the context of SPA using implicit grant with PKCE, this is fine. This code pattern allows you to reuse the token on subsequent calls without needing to sign in again.

I noticed the sample had some out of date libraries and a minor typeScript issue. I fixed these in PR #605. So you should be able to use this sample once the PR goes through.

Hope this helps,
David

@tiwarigaurav
Copy link
Author

Hi @davidchesnut,

Thanks for taking the time to update this.

I might be missing something here OR I have been unable to explain the issue - please bare with me.

Getting the token back from the "displayDialogAsync" popup saving it in a variable in the App.tsx file and using it to access GraphAPI has been fine and is working as expected.

The issue that I am trying to get help on is:

  1. AccessTokens have a lifespan of about 75minutes - so after the token expires we need to get another token to call GraphAPI
  2. To get another AccessToken we need to use the msalInstance.acquireTokenSilent method of the msal-browser (we cannot expect the user to keep initiating the "displayDialogAsync" popup every hour)
  3. This is where the entire thing falls apart
  4. If we initialize a new msalInstance from the REACT app, this new instance does not have the context of the original msal instance that was used in the displayDialogAsync popup (login.html), hence the acquireTokenSilent fails

Trying to use the acquireTokenSilent from the REACT app fails with error:
Exception has occurred: ClientAuthError: token_refresh_required: Cannot return token from cache because it must be refreshed. This may be due to one of the following reasons: forceRefresh parameter is set to true, claims have been requested, there is no cached access token or it is expired.

@davidchesnut
Copy link
Member

Hi @tiwarigaurav, you are correct, and the token expires. I've been looking into this for any possible workarounds but it doesn't look like this is possible and you have to sign the user in again. This is definitely a pain point and I'll share this with the rest of the product team to look into.

The one approach that will work is using SSO. Office will refresh the token for you automatically if you request it through getAccessToken() and it has expired. If SSO works for your scenario I'd recommend using that approach.

Hope this helps,
David

@tiwarigaurav
Copy link
Author

@davidchesnut - thanks for acknowledging that there is an issue. I was thinking that I was going out of my mind ;)

SSO does not help - if we need to create cross browser/cross platform Add-Ins we need a fall back method (SSO will not work in Safari and other exceptions).

The only conclusion that I have reached is - msal-browser/msal-react does not work with Office web add-ins in real world scenarios and because of this, even msal-node OR msal .net will not work. If we need to implement a real world add-in we need to use OBO flow and manage access and refresh tokens ourselves i.e. using a DB.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants