Search code examples
azure-active-directoryoffice-jsoffice-addinsazure-ad-msaloutlook-web-addins

office-addin-sso with @azure/msal-browser


Is it possible to use office-addin-sso with @azure/msal-browser ?

I would like to:

  • use OfficeRuntime.auth.getAccessToken() to get the Identity Token.
  • while at the same time use @azure/msal-browser as the fallback method.

I have managed to get both the above working and can successfully get the MS Graph access token using just @azure/msal-browser.

Given that we want to use msal-browser/auth code flow with PKCE (and not msal/implicit flow) for the fallback, what would be the most effective way of getting the MS Graph access token in this context.

and given that the office-addin-sso package uses On Behalf Of Flow (requiring a secret and redirect).

Any help/suggestions or guidance would be really appreciated.


Solution

  • I use @azure/msal-browser in the office-addin-sso. My addin is for a single domain and the users are supposed to be logged in on OneDrive so I expect to never need the login via the fallback. I get the token silently from msal and then pass it to MS graph to get an access token. This is the code that does it in the ssoauthhelper.ts:

    import * as Msal from '@azure/msal-browser';
    export async function getToken_Email() {
      try {
        const msalConfig: Msal.Configuration = {
          auth: {
            clientId: "<your client id>", //This is your client ID
            authority: "https://login.microsoftonline.com/<tenant id>", //The <tenant> in the URL is the tenant ID of the Azure Active Directory (Azure AD) tenant (a GUID), or its tenant domain.
            redirectUri: "https://<your server>/office-js/fallbackauthdialog.html",
            navigateToLoginRequestUrl: false,
          },
          cache: {
            cacheLocation: "localStorage", // Needed to avoid "User login is required" error.
            storeAuthStateInCookie: true, // Recommended to avoid certain IE/Edge issues.
          },
        };
    
        const msalInstance = new Msal.PublicClientApplication(msalConfig);
        const silentRequest = {
          scopes: ["User.Read", "openid", "profile"]
        };
        let access_token: string;
        try {
          const loginResponse = await msalInstance.ssoSilent(silentRequest);
          access_token = loginResponse.accessToken;
        } catch (err) {
          if (err instanceof Msal.InteractionRequiredAuthError) {
            const loginResponse = await msalInstance.loginPopup(silentRequest).catch(error => {
              // handle error
            });
          } else {
            // handle error
          }
        }
    
        console.log('silent token response: ' + JSON.stringify(access_token));
    
    
        // makeGraphApiCall makes an AJAX call to the MS Graph endpoint. Errors are caught
        // in the .fail callback of that call
        const graph_response: any = await makeGraphApiCall(access_token);
        console.log('graph response: ' + JSON.stringify(graph_response));
      } catch (exception) {
        console.log(exception);
      }
    }
    
    export async function makeGraphApiCall(accessToken: string): Promise < any > {
      try {
        const response = await $.ajax({
          type: "GET",
          url: "https://graph.microsoft.com/oidc/userinfo/",
          headers: {
            access_token: accessToken,
            Authorization: 'Bearer ' + accessToken + ' '
          },
          cache: false,
        });
        return response;
      } catch (err) {
        console.log(`Error from Microsoft Graph. \n${err}`);
      }
    }