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

Azure B2C authentication from SPFX App Teams #2669

Open
Ben-Oops opened this issue Jan 7, 2025 · 7 comments
Open

Azure B2C authentication from SPFX App Teams #2669

Ben-Oops opened this issue Jan 7, 2025 · 7 comments

Comments

@Ben-Oops
Copy link

Ben-Oops commented Jan 7, 2025

Context:

In the context of a POC, I need to make calls to APIs from an SPFX App Teams hosted in my tenant using an Azure AD B2C to authenticate. I create an IDP (Identity Provider) to add my tenant in Azure B2C. To authenticate we use OAuth 2.0 Open ID Connect with Authorization Code flow.

Repro Step

Solution 1: Use @azure/msal-browser SDK for authentication

It works in Teams Web but not in Teams Desktop because it does not allow to open pop-up. See below.

Teams Web Teams Desktop
TeamsWebMSAL TeamsDesktopMSAL

I think there is no way to authorize po-up in Teams Desktop so i tried Solution 2 below.

Solution 2: Use @microsoft/teams-js SDK for authentication

This time the pop-up is now displayed correctly in Teams Desktop but I can't complete authentication in both Teams Desktop and Web. I do receive the authorisation code but it is not intercepted by the Teams connection SDK and is therefore not sent to the /token endpoint to retrieve the authentication token. I use the same redirect URL as the one used with @azure/msal-browser SDK

Redirect URL: https://[tenantname].sharepoint.com/_layouts/15/teamslogon.aspx

Step 1 - Login Pop-Up Step2 - Authorization Code
TeamsWebTeamsJS TeamsWebTeamsJS2
  const authenticateWithTeams = (): Promise<void> => {

    const parameters: authentication.AuthenticatePopUpParameters = {
      url: `https://[AzureADB2CDomain]/[MyTenantEntraId]/oauth2/v2.0/authorize?p=[AzureADB2CAuthorizationNamePolicy]&client_id=[AppId]&nonce=defaultNonce&scope=[Scopes]&response_type=code&prompt=login`,
      width: 600,
      height: 535,
      isExternal: true,
    };

    return new Promise<void>((resolve, reject) => {
      app
        .initialize()
        .then(() => {
          authentication
            .authenticate(parameters)
            .then((result) => {
              console.log("success" + result);
            })
            .catch((reason) => {
              console.log("failed" + reason);
              reject(reason);
            });
        })
        .catch((error) => {
          console.log("failed" + error);
        });
    });
  };

As you can see in Step 2 I'm stuck on this screen with no way of recovering my access token. If anyone can help me with this it would be greatly appreciated. Thank you very much.

@Nivedipa-MSFT
Copy link

@Ben-Oops - Thank you for bringing this issue to our attention. We will look into it and get back to you shortly.

@AE-MS
Copy link
Contributor

AE-MS commented Jan 8, 2025

@Ben-Oops thanks for providing a great level of detail in your question, including a code snippet!

You're right that solution1 won't work for pop-up blocking reasons, which is why the authentication.authenticate function exists, like you are using in solution2.

In solution 2, is the page that you're directing the pop-up to (url parameter in the AuthenticatePopUpParameters object) a page that runs teamsjs, redirects through the identity provider (IdP), then back to the teamsjs page? The way this typically works is that when the IdP redirects back to your page in the authentication pop-up, your page would then call authentication.notifySuccess to communicate the result to the original tab page.

You can see a visual overview of this expected flow here:
https://learn.microsoft.com/en-us/microsoftteams/platform/tabs/how-to/authentication/auth-flow-tab#use-oauth-idp-to-enable-authentication

From the code that I see, it looks like the page that is getting launched in the authentication pop-up might not be making the appropriate teamsjs calls to communicate the result back to the original tab page. What do you think?

@Ben-Oops
Copy link
Author

Ben-Oops commented Jan 8, 2025

Yes, you're absolutely right, that's the source of my problem i guess. My redirection page as you can see don't call authentication.notifySuccess. Why ? Because it's a native page which is stored inside /_layouts/15/teamslogon.aspx and i can't include custom code in it.

I don't know which let's say "native redirection address" I can set up to be able to call this authentication.notifySuccess on my pop-up. Does it mean that i need to create a proxy somewhere for example a sharepoint page in order to be able to call authentication.notifySuccess ?

@AE-MS
Copy link
Contributor

AE-MS commented Jan 8, 2025

Yes, you'll need to have some page where you can write custom code that calls authentication.notifySuccess() to serve as the IdP redirect initiator and receiver. Some developers have a route/path on their existing tab app site for this. I don't know which option might work best for you.

@Ben-Oops
Copy link
Author

Ben-Oops commented Jan 9, 2025

Thanks for your answer.

Unfortunately as it's an SPFx project based on this article i don't really know what route/path i can provide to be able to do this.

https://learn.microsoft.com/en-us/microsoftteams/platform/sbs-gs-spfx?tabs=vscode%2Cviscode

I don't really have a path of my application as it's handle by Teams. If i take a look to my Teams Manifest app, my content url is like this.

TeamsManifest

@Ben-Oops
Copy link
Author

Ben-Oops commented Jan 9, 2025

Thanks again for your help. I was able to get Teams Desktop to work, but now I can't get it to work in Teams Web mode. I have the following error after the execution of authentication.notifySuccess.

IssueCanceledUser

I saw this post https://github.com/OfficeDev/microsoft-teams-library-js/issues/1779 related to this issue but it doesn't help me. You can find below the related code i used to called authentication.notifySuccess.

export const OAuthWp = (): JSX.Element => {
  const [accessToken, setAccessToken] = React.useState<string | null>(null);

  const confirmAuthentification = async ():Promise<void> => {
    const hash = window.location.hash.replace(/^#/, '');
    const params = new URLSearchParams(hash);
    await app.initialize();
    setAccessToken(params.get('access_token'));
    authentication.notifySuccess(JSON.stringify({
      idToken: params.get('id_token'),
      accessToken: params.get('access_token'),
      tokenType: params.get('token_type'),
      expiresIn: params.get('expires_in')
    }));
  };

  React.useEffect(() => {
    // eslint-disable-next-line @typescript-eslint/no-floating-promises
    confirmAuthentification();
  }, []);


  return (
    <div>
      <div>ACCESS TOKEN: {accessToken}</div>
      <div>IsNotifySuccessCall: {isNotifyCalled}</div>
    </div>
  );
};

@AE-MS
Copy link
Contributor

AE-MS commented Jan 10, 2025

Hmm, I'm not sure what might be causing that. Is that "TEAMS AUTH Error" string somewhere in your code or is it coming from Teams? Is authentication.js a Teams file or one of yours? I did a quick search of the Teams codebase and I didn't see the string "TEAMS AUTH" anywhere but that doesn't necessarily mean its not constructed somewhere.

Enabling TeamsJS client logging may help track down what's going on after you call authentication.notifySuccess. Can you try enabling logging and share the logs from the failure case here (redact any sensitive information, e.g., tokens)

BTW you may only be doing this for testing but you shouldn't send tokens back via authentication.notifySuccess since an attacker may have triggered your authentication window to be launched (instead of your own app) and now the tokens will go to the attacker. We don't do a good job of documenting that so I will get the documentation updated (although we do sort of call it out on this page by telling people to use a random number).

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