OfficeDev / TeamsFx

Developer tools for building Teams apps
Other
427 stars 165 forks source link

r.UiRequiredError: Failed to get access token cache silently, #10379

Closed shabbir-dhangot closed 3 months ago

shabbir-dhangot commented 6 months ago

Describe the bug We have encountered a critical issue in our production application when using the latest version of Microsoft Teams. It appears that Teams has implemented Tracking Prevention for the application, causing certain functionalities to break.

In our app, during the initial load, the application relies on localStorage, which was preventing the app from loading. We managed to resolve this issue by eliminating or bypassing the use of localStorage, allowing the app to load successfully. However, after the initial load, we are facing problems with a dropdown that calls the Graph API to retrieve a list of users. This functionality works correctly with the older version of Teams, but with the newer version, we are experiencing difficulties, and the exact error message is as follows:

To Reproduce Steps to reproduce the behavior:

  1. Deploy application which is using graph API
  2. Open it in new Teams Application
  3. Graph api is throwing below error

Expected behavior Application should work normally in new Teams application in desktop client as well.

Error GraphErrorHandler.ts:44 Uncaught (in promise) r.UiRequiredError: Failed to get access token cache silently, please login first: you need login first before get access token. at e. (teamsUserCredential.browser.ts:266:15) at d (regeneratorRuntime.js:45:16) at Generator. (regeneratorRuntime.js:133:17) at Generator.throw (regeneratorRuntime.js:74:21) at sC (asyncToGenerator.js:3:20)

VS Code Extension Information (please complete the following information):

CLI Information (please complete the following information):

Additional context App is not working only in the new teams desktop client.

shabbir-dhangot commented 6 months ago

You can find more issue detail here. https://techcommunity.microsoft.com/t5/teams-developer/teamsfx-react-3-0-2-teamsusercredential-gettoken-results-in/m-p/3984411. This has raised by different user.

blackchoey commented 5 months ago

@shabbir-dhangot Does your application has login related logic (by using TeamsUserCredential.login(...) )? The UiRequiredError means the application needs user to login explicitly in order to acquire the token. So the quick solution is trigger the login logic when your application gets the error. After that, your session storage should have the token and the TeamsFx SDK can acquire token from there.

To provide more technical background: The TeamsUserCredential.getToken(...) function will try to acquire cached token from session storage first. If there's no cached token, it will leverage MSAL's silent login functionality to login current user silently. This is why you didn't meet UiRequiredError error previously. If both does not work, it will throw UiRequiredError asking people to login in order to acquire the token, which is your current situation. The MSAL's silent login functionality is not guaranteed to always work under every situation. So please trigger the login flow as suggested above if you got UiRequiredError error.

aliaksei-lebedzeu commented 5 months ago

Hi,

Observing exactly the same issue in a web client (FireFox, Edge - Classic and New Teams), the following code is used:

export default function TeamsApp() {
    console.log('Running in `Teams` mode', process.env.REACT_APP_START_LOGIN_PAGE_URL, process.env.REACT_APP_CLIENT_ID);

    const [ channel, setChannel ] = useState<TeamsChannelIntegration | null>(null);
    const [ auth, setAuth ] = useState<Auth | null>(null);
    const [ error, setError ] = useState(null);
    const [ teams, setTeams ] = useTeams();
    const { loading, error: xError, teamsUserCredential} = useTeamsUserCredential({
        clientId: process.env.REACT_APP_CLIENT_ID ?? 'unknown',
        initiateLoginEndpoint: process.env.REACT_APP_START_LOGIN_PAGE_URL ?? 'unknown'
    });

    useEffect(() => {
        (async () => {
            if(teamsUserCredential) {
                try {
                    // TODO `teamsUserCredential.login` for some reason opens popup, so use the common way for now
                    setChannel(TeamsChannelIntegration.build(new MSGraph(teamsUserCredential!)));
                    setAuth(new TeamsAuth());
                    console.log('... UserCredential. Before Login')
                    await teamsUserCredential.login(['User.Read']);
                    console.log('... UserCredential. After Login')
                    const tUser = await teamsUserCredential.getUserInfo();
                    console.log('... UserCredential. After User Info', tUser);
                    const tToken = await teamsUserCredential.getToken(['User.Read']);
                    console.log('... UserCredential. After Get Token', tToken);
                    const tGraph = new MSGraph(teamsUserCredential);
                    const tMe = await tGraph.getMe();
                    console.log('... UserCredential. Me', tMe);
                    setChannel(TeamsChannelIntegration.build(tGraph));
                    setAuth(new TeamsAuth());
                } catch (e) {
                    console.log('Failed to Process due to', e);
                }

            }
        })();
    }, [teamsUserCredential]);
    return (...);
}

And see the following output

Running in `Teams` mode https://localhost:53000/auth-start.html 0406...
Teams 
Object { actionInfo: undefined, app: {…}, page: {…}, user: {…}, channel: {…}, chat: undefined, meeting: undefined, sharepoint: undefined, team: {…}, sharePointSite: {…} }
... UserCredential. After Login
... UserCredential. After User Info 
Object { displayName: "Aliaksei Lebedzeu", objectId: "8f878155-9cc9-4a67-a179-4fe74dce9ac0", tenantId: "36ec...", preferredUserName: "email@x.onmicrosoft.com" }
Failed to Process due to bc.UiRequiredError: Failed to get access token cache silently, please login first: you need login first before get access token.

I see a pop-up appearing and disappearing, but it does not help

blackchoey commented 5 months ago

@aliaksei-lebedzeu Thanks for reporting this to us. I wasn't able to repro your issue. Can you clone https://github.com/KennethBWSong/authCodeTemplate and see whether you can repro with this template project?

shabbir-dhangot commented 5 months ago

@blackchoey, I attempted to use TeamsUserCredential.getToken(...), and it produced the same error as the one I shared earlier. It seems that due to tracking prevention, when Graph attempts to access the cache, it is denied, resulting in the aforementioned error. Below is code which I tried.

useEffect(() => {
    if (!teamsUserCredential) {
        return;
    }

    teamsUserCredential
        .getToken("User.Read")
        .then((token) => {
            console.log("token acquired:", token);
        })
        .catch((err) => {
            console.error("token acquisition failed.", err);
        });
}, [teamsUserCredential, graphApiAuthenticated]);
blackchoey commented 5 months ago

@shabbir-dhangot you need to call the login(...) function when you get the UiRequiredError. You can also leverage the @microsoft/teamsfx-react package when building react app, which handles the login for you.

In the meanwhile, can you also try this sample and see whether you will meet same issue with it? https://github.com/KennethBWSong/authCodeTemplate

ChetanSharma-msft commented 5 months ago

Hello @blackchoey - Another developer is still facing the same issue: https://github.com/OfficeDev/microsoft-teams-library-js/issues/1994

Looks like this issue is more related to TeamsFX side but not on the Teams JS library. Could you please confirm and share your thoughts on the issue?

blackchoey commented 5 months ago

Hi @ChetanSharma-msft The customer is facing a difference issue (a latency issue) and it seems to be a Teams client issue as other client versions are working fine. Can you reach client team for help?

The code of getToken() is fairly simple: https://github.com/OfficeDev/TeamsFx/blob/dev/packages/sdk/src/credential/teamsUserCredential.browser.ts#L210-L278

To brainstorm more: Since it's related to Teams client version, I'm not sure whether following function call to teams-js inside getToken() will be impacted by different version of client:

  1. await app.initialize() to initialize teams-js
  2. await authentication.getAuthToken(params) to get SSO token

The getToken() function will print logs with timestamp. We may ask the customer to check the timestamp to identify which part consumes most of the time.

mr-github-user commented 5 months ago

@shabbir-dhangot Does your application has login related logic (by using TeamsUserCredential.login(...) )? The UiRequiredError means the application needs user to login explicitly in order to acquire the token. So the quick solution is trigger the login logic when your application gets the error. After that, your session storage should have the token and the TeamsFx SDK can acquire token from there.

To provide more technical background: The TeamsUserCredential.getToken(...) function will try to acquire cached token from session storage first. If there's no cached token, it will leverage MSAL's silent login functionality to login current user silently. This is why you didn't meet UiRequiredError error previously. If both does not work, it will throw UiRequiredError asking people to login in order to acquire the token, which is your current situation. The MSAL's silent login functionality is not guaranteed to always work under every situation. So please trigger the login flow as suggested above if you got UiRequiredError error.

After calling await teamsUserCredential.getToken(scopes) I get the following error:

Failed to get access token cache silently, please login first: you need login first before get access token.

I then call await teamsUserCredential.login(scopes) the user is able to login successfully and no error is encountered.

After login I call await teamsUserCredential.getToken(scopes) which results in the same error as before:

Failed to get access token cache silently, please login first: you need login first before get access token.

This issue is only happening to users that are using Teams on iphone. And calls to teamsUserCredentials.getUserInfo() work.

Am I missing something?

blackchoey commented 5 months ago

@mr-github-user We will try to repro this. In the meanwhile, can you also try this project and see whether it has same issue as your project?

mr-github-user commented 4 months ago

@mr-github-user We will try to repro this. In the meanwhile, can you also try this project and see whether it has same issue as your project?

Is this the project you want me to try: https://github.com/KennethBWSong/authCodeTemplate

The issue only happens for me when the app is running inside of the MacOS and ios Teams client. I'll start working with our Teams admin to see if he is okay with us adding the project to our instance.

Thanks

blackchoey commented 4 months ago

@mr-github-user Yes, please help try that project. In the meanwhile, you can also leverage the M365 Developer Program to test this project, as well as yours. Which should be easier than getting approval from your admin.

sup3rgalou commented 4 months ago

I am facing the same issue, At first I call await teamsUserCredential.login(scopes) with no error. I then call await teamsUserCredential.getToken(scopes) which triggers Failed to get access token cache silently, please login first: you need login first before get access token.

For me this happens on Teams web. The issue happens when I first launch my app, if I refresh the page then it seems that getToken succeed

Does anybody have an update on this issue ?

mr-github-user commented 4 months ago

@mr-github-user Yes, please help try that project. In the meanwhile, you can also leverage the M365 Developer Program to test this project, as well as yours. Which should be easier than getting approval from your admin.

I cloned the repo: https://github.com/KennethBWSong/authCodeTemplate

I was able to get the app published in Teams, and can confirm that it works on the iPadOS device I am using for testing. I'm not sure this helps me out though.

My application does not use the Microsoft Graph API. I am using teamsUserCredential.getToken(scopes) to obtain an access token for my Microsoft Entra ID application. The teamsUserCredentials.getToken() works in every other client except for iOS and iPadOS.

I tried using the getToken code in my own project and still get the same error

 const apiClient = createApiClient(
      apiBaseUrl,
      new BearerTokenAuthProvider(async () => (await teamsUserCredential.getToken(""))!.token)
    );

Do you have any other suggestions on how to troubleshoot the issue? Is there anything I can provide you to help troubleshoot the issue?

Thanks

blackchoey commented 4 months ago

@sup3rgalou Can you try this project to see whether it has the same issue? If yes, please share the repro steps so we can repro and investigate further.

blackchoey commented 4 months ago

@mr-github-user Since the project we shared does not have the issue, can you compare the project with yours and find the implementation differences? The project is a previous template to demonstrate how to build a project with authentication enabled. In the meanwhile, you could also see whether it's possible to create a sample project that repros the issue. So we can repro and investigate further.

mr-github-user commented 4 months ago

@blackchoey

@mr-github-user Since the project we shared does not have the issue, can you compare the project with yours and find the implementation differences? The project is a previous template to demonstrate how to build a project with authentication enabled. In the meanwhile, you could also see whether it's possible to create a sample project that repros the issue. So we can repro and investigate further.

I think the main difference between projects is I am not querying the graph API. I am using the library to retrieve an access token for an API that I am hosting in Azure.

I went ahead and created a test project that is using the same token logic I am using in my application. When running on teams desktop I encounter no error and I am able to retrieve the token.

image

On iPadOS I run the same test and I get the UiRequiredError, the same error I am encountering in my application.

ipadExample

Here is the sample app you can use to repro the issue: https://github.com/mr-github-user/AuthenticationSample Let me know if you run into any issues with the project. I'll help if I can.

Thanks

blackchoey commented 3 months ago

Thank you. We will try this sample and reach you back later.

mr-github-user commented 3 months ago

When running the application on iOS teamsfx logs the following errors after I call teamsUserCredential.getToken:

Logging in does not result in an error, and sessionStorage appears to be populated with the loginResult.sessionStorage values.

However even after logging in the teamsUserCredential.getToken() still fails.

Hope that helps.

blackchoey commented 3 months ago

@mr-github-user Thanks for all the information. Here's one solution: run npm install @azure/msal-browser@2.21 to your project and redeploy your app. It should work on iOS devices.

Technical background: The auth-start.html and auth-end.html is using MSAL-Browser 2.21 to login users and handle login redirect response. The project does not specify the MSAL version so npm installs latest version. But in newer MSAL, the cache logic is changed so it cannot read cache from 2.21 and fallback to the ssoSilent function. In desktop browser environment, ssoSilent can acquire a token silently so you can get the token. But it cannot acquire the token in iOS so your app cannot work properly. Changing the MSAL version in your react app to 2.21 aligns the version between react app and auth-end.html so it fixes the problem. There could be other solution for your issue, as long as the MSAL version is aligned. You can have your own solution based on your requirement.

mr-github-user commented 3 months ago

Instead of downgrading msal-browser to 2.21, I decided to update the auth-start.html and auth-end.html to use a more recent version of msal-browser. It worked like a charm!!!

Both the sample I sent and my own application were built using TeamsFX inside of visual studio code. The auth-start and auth-end files are defaulted to msal-browser@2.21.0. TeamsFx in the templates package.json uses version @microsoft/teamsfx: "^2.2.0" which in turn uses "@azure/msal-browser": "^2.21.0". I think because of that new applications created with teamsfx will not work on iOS. Updating the msal-browser cdn reference in auth-start.html and auth-end.html to version 2.35.0 fixed the issue for me. I did not have to update any packages on my end by doing that.

I have been trying to get this issue fixed for a long time. Thank you for your help.!!

blackchoey commented 3 months ago

@mr-github-user glad to hear that! I'm going to close this issue. Feel free to open new issue when you need other support.

ThomasPe commented 3 months ago

thank you @blackchoey & @mr-github-user! fixed it for me as well, this really should be updated in the template!

leonardjohn commented 1 month ago

Where can we find the version mapping which can work fine between msal-browser and @microsoft/teamsfx?

blackchoey commented 1 month ago

@leonardjohn The issue only happens when MSAL changes its cache mechanism. Since MSAL does not provide information about its cache mechanism changes, we're not able to provide a comprehensive version mapping.

For existing project, since package-lock.json locks the version of packages, you don't need to worry about this issue. If this issue happens due to some force version upgrade, you can check package-lock.json which msal-browser version is installed and change the msal-version in auth-end.html to that version.

If you're starting a new project, it's recommended to start with the "React with Fluent UI" template which includes a backend to invoke APIs. This is the latest recommendation from Teams and avoids some login limitations brought by AAD conditional access policy, which will block user login implemented with msal-browser.

leonardjohn commented 1 month ago

@blackchoey many thanks for your fast response. I built the teams tab application with the latest Fluent UI v9 through Teams Toolkit. we referenced msal-browse.js in auth-start.html and auth-end.html:

<script
    type="text/javascript"
    src="https://alcdn.msauth.net/browser/2.35.0/js/msal-browser.min.js"
    integrity="sha384-o+Sncs5XJ3NEAeriM/FV8YGZrh7mZk4GfNutRTbYjsDNJxb7caCLeqiDabistgwW"
    crossorigin="anonymous"></script>

I tried to add dependency @microsoft/teamsfx with version:2.3.0 and it worked well. but when I updated it to 2.3.1, then the error mentioned in this title occurred. after I checked the depencency in @microsoft/teamsfx:2.3.1 and found the msal-browser version is ^3.0.2. This is the root cause, I think.

Anyway, thanks a lot again.😊

sondh0127 commented 1 month ago

@blackchoey many thanks for your fast response. I built the teams tab application with the latest Fluent UI v9 through Teams Toolkit. we referenced msal-browse.js in auth-start.html and auth-end.html:

<script
    type="text/javascript"
    src="https://alcdn.msauth.net/browser/2.35.0/js/msal-browser.min.js"
    integrity="sha384-o+Sncs5XJ3NEAeriM/FV8YGZrh7mZk4GfNutRTbYjsDNJxb7caCLeqiDabistgwW"
    crossorigin="anonymous"></script>

I tried to add dependency @microsoft/teamsfx with version:2.3.0 and it worked well. but when I updated it to 2.3.1, then the error mentioned in this title occurred. after I checked the depencency in @microsoft/teamsfx:2.3.1 and found the msal-browser version is ^3.0.2. This is the root cause, I think.

Anyway, thanks a lot again.😊

can you share your setup. I try your script but I doesn't work

leonardjohn commented 6 days ago

@blackchoey many thanks for your fast response. I built the teams tab application with the latest Fluent UI v9 through Teams Toolkit. we referenced msal-browse.js in auth-start.html and auth-end.html:

<script
    type="text/javascript"
    src="https://alcdn.msauth.net/browser/2.35.0/js/msal-browser.min.js"
    integrity="sha384-o+Sncs5XJ3NEAeriM/FV8YGZrh7mZk4GfNutRTbYjsDNJxb7caCLeqiDabistgwW"
    crossorigin="anonymous"></script>

I tried to add dependency @microsoft/teamsfx with version:2.3.0 and it worked well. but when I updated it to 2.3.1, then the error mentioned in this title occurred. after I checked the depencency in @microsoft/teamsfx:2.3.1 and found the msal-browser version is ^3.0.2. This is the root cause, I think. Anyway, thanks a lot again.😊

can you share your setup. I try your script but I doesn't work

  1. Open your VS Code

  2. Install the latest Teams Toolkit

  3. Create a New App:

    image
  4. After this, you can run this Tab Application you created on your local and try this script.