AzureAD / microsoft-authentication-library-for-js

Microsoft Authentication Library (MSAL) for JS
http://aka.ms/aadv2
MIT License
3.66k stars 2.65k forks source link

MSALObject.getAllAccounts() return empty after loggedin in a excel addin dialog #6485

Closed coeguru closed 11 months ago

coeguru commented 1 year ago

Core Library

MSAL.js (@azure/msal-browser)

Core Library Version

2.34.0

Wrapper Library

Not Applicable

Wrapper Library Version

None

Public or Confidential Client?

Public

Description

myMSALObject.getAllAccounts() return empty when logged in via myMSALObj.loginRedirect(loginRequest); inside Excel Addin Dialog created using Office.context.ui.displayDialogAsync. Because the getAllAccounts() is empty, we are not able to set the active account for fetching the token silently after the initial authentication.

What I see is if I open the dialog as present in the code section. The localStorage is not populated with msal accounts but If I use the myMSALObj.acquireTokenPopup(request) the localstorage is populated but I dont want to use the popup as it is blocked in the browsers by default.

MSAL Configuration

{
    auth: {
      clientId: clientId, // This is the ONLY mandatory field that you need to supply.
      authority: "https://login.microsoftonline.com/common", // Defaults to "https://login.microsoftonline.com/common"
      redirectUri: redirectUri, // You must register this URI on Azure Portal/App Registration. Defaults to window.location.href
      //postLogoutRedirectUri: "https://localhost:3000/signout", // Simply remove this line if you would like navigate to index page after logout.
      navigateToLoginRequestUrl: false, // If "true", will navigate back to the original request location before processing the auth code response.
      // response_type: "access_token"
    },
    cache: {
      cacheLocation: "localStorage", // Configures cache location. "sessionStorage" is more secure, but "localStorage" gives you SSO.
      storeAuthStateInCookie: false, // If you wish to store cache items in cookies as well as browser cache, set this to "true".
    },
    system: {
      loggerOptions: {
        loggerCallback: (level, message, containsPii) => {
          if (containsPii) {
            return;
          }
          switch (level) {
            case msal.LogLevel.Error:
              console.error(message);
              return;
            case msal.LogLevel.Info:
              console.info(message);
              return;
            case msal.LogLevel.Verbose:
              console.debug(message);
              return;
            case msal.LogLevel.Warning:
              console.warn(message);
              return;
          }
        }
      }
    }
 }

Relevant Code Snippets

getAccessTokenMSAL function.

async function getAccessTokenMSAL(request=loginRequest) {
    console.log('loginRequest', request, homeAccountId);
    // Attempt to acquire token silently if user is already signed in.
    if (homeAccountId !== null) {
        const result = await myMSALObj.acquireTokenSilent(request, homeAccountId);
        if (result !== null && result.accessToken !== null) {
            console.log('silent token', JSON.stringify(result));
            return result;
        } else return null;
    } else {
        let promise = await new Promise((resolve, reject) => {
            const url = '/dialog.html';
            var fullUrl =
                location.protocol +
                '//' +
                location.hostname +
                (location.port ? ':' + location.port : '') +
                url;
            //console.log('full url', fullUrl);
            // height and width are percentages of the size of the parent Office application, e.g., Outlook, PowerPoint, Excel, Word, etc.
            Office.context.ui.displayDialogAsync(
                fullUrl,
                { height: 60, width: 30 },
                function (result) {
                    if (result.status === Office.AsyncResultStatus.Failed) {
                        console.log(
                            (result.error.code = ': ' + result.error.message)
                        );
                        reject(result.error.message);
                    } else {
                        console.log('Dialog has initialized. Wiring up events');
                        let loginDialog = result.value;

                        // Handler for the dialog box closing unexpectedly.
                        loginDialog.addEventHandler(
                            Office.EventType.DialogEventReceived,
                            (arg) => {
                                console.log(
                                    'DialogEventReceived: ' + arg.error
                                );
                                loginDialog.close();
                                // For more dialog codes, see https://learn.microsoft.com/office/dev/add-ins/develop/dialog-handle-errors-events#errors-and-events-in-the-dialog-box
                                switch (arg.error) {
                                    case 12002:
                                        reject("The auth dialog box has been directed to a page that it cannot find or load, or the URL syntax is invalid.");
                                        break;
                                    case 12003:
                                        reject("The auth dialog box has been directed to a URL with the HTTP protocol. HTTPS is required.");          
                                        break;
                                    case 12006:
                                        reject("The auth dialog box was closed before the user signed in.");
                                        break;
                                    default:
                                        reject("Unknown error in auth dialog box.");
                                        break;
                                }
                            }
                        );
                        loginDialog.addEventHandler(
                            Office.EventType.DialogMessageReceived,
                            function processMessage2(arg) {
                                console.log(
                                    'Message received in processMessage',
                                    arg.message
                                );
                                let messageFromDialog = JSON.parse(arg.message);

                                if (messageFromDialog.status === 'success') {
                                    // We now have a valid access token.
                                    loginDialog.close();
                                    homeAccountId = messageFromDialog.accountId;
                                    console.log("All Accounts", myMSALObj.getAllAccounts());
                                    // Set the active account so future token requests can be silent.
                                    myMSALObj.setActiveAccount(
                                        myMSALObj.getAccountByHomeId(
                                            homeAccountId
                                        )
                                    );

                                    // Return the token.
                                    resolve({ accessToken: messageFromDialog.result});
                                } else if (messageFromDialog.status === 'test') {
                                    console.log("test", messageFromDialog);
                                }else {
                                    // Something went wrong with authentication or the authorization of the web application.
                                    loginDialog.close();
                                    reject(messageFromDialog.error);
                                }
                            }
                        );
                    }
                }
            );
        });
        return promise;
    }
}

AuthRedicect

// This file copied and modified from https://github.com/Azure-Samples/ms-identity-javascript-tutorial/blob/main/1-Authentication/1-sign-in/App/authRedirect.js

// Create the main myMSALObj instance
// configuration parameters are located at authConfig.js

Office.initialize = async function () {
  if (Office.context.ui.messageParent) {
    try {
      await initialize();
      const response = await myMSALObj.handleRedirectPromise();
      handleResponse(response);
    } catch (error) {
      console.error(error);
      Office.context.ui.messageParent(
        JSON.stringify({ status: "error", error: error.message }),
        { targetOrigin: window.location.origin }
      );
    }
  }
};

function handleResponse(response) {
  /**
   * To see the full list of response object properties, visit:
   * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/request-response-object.md#response
   */
  Office.context.ui.messageParent(
    JSON.stringify({ status: "test", accounts: myMSALObj.getAllAccounts(),  agent: navigator.userAgent }),
    { targetOrigin: window.location.origin });
  if (response !== null) {
    Office.context.ui.messageParent(
      JSON.stringify({ status: "success", result: response.accessToken, accountId: response.account.homeAccountId }),
      { targetOrigin: window.location.origin }
    );
  } else {
    //log in
    myMSALObj.loginRedirect(loginRequest);
  }
}

These snippets are from https://github.com/OfficeDev/Office-Add-in-samples/tree/main/Samples/auth/Office-Add-in-NodeJS-SSO



### Identity Provider

Azure AD / MSA

### Source

External (Customer)
microsoft-github-policy-service[bot] commented 1 year ago

This issue requires attention from the MSAL.js team and has not seen activity in 5 days. @hectormmg please follow up.

microsoft-github-policy-service[bot] commented 1 year ago

This issue requires attention from the MSAL.js team and has not seen activity in 5 days. @hectormmg please follow up.

coeguru commented 1 year ago

Hey we are still facing the issue, @hectormmg do you know when will you be able to get to this ?

vdeunzue commented 1 year ago

We are experiencing the same issue where some people in some browsers (firefox & safari) and mobile browsers, after the loginRedirect(); the getAllAccounts call returns an empty array. We however are using:

@azure/msal-angular": "^1.1.2 msal": "^1.4.6

cvium commented 1 year ago

I have also run into this issue

coeguru commented 1 year ago

@hectormmg , Any update on this. its been like 2 months since I posted the question. It would be nice to get some light on it.

hectormmg commented 11 months ago

@coeguru sorry for the delay, I see you shared snippets from a Node sample, have you looked at the add-in sample for msal-browser?

https://github.com/OfficeDev/Office-Add-in-samples/blob/main/Samples/auth/Office-Add-in-Microsoft-Graph-React/l

cvium commented 11 months ago

@coeguru sorry for the delay, I see you shared snippets from a Node sample, have you looked at the add-in sample for msal-browser?

https://github.com/OfficeDev/Office-Add-in-samples/blob/main/Samples/auth/Office-Add-in-Microsoft-Graph-React/l

The addin sample does not utilize msal.js in any meaningful way if you ask me. It does use msal.js to authenticate the user, but the accesstoken is simply stored in the React state without any way to refresh it again. As such, it does not save the account.

coeguru commented 11 months ago

Yeah the sample code even basic compared to the code snippets I mentioned, we ended up caching the token in localStorage and not rely on MSAL to cache.

Even this document mentions us to not rely on the local cache of any library. https://learn.microsoft.com/en-us/office/dev/add-ins/develop/auth-with-office-dialog-api

parinda296 commented 11 months ago

@hectormmg I've encountered a similar issue while using the dialog API for login. The local storage of the dialog isn't accessible to the task pane, causing getAllAccounts to return null upon add-in reload which results in the failure of acquireTokenSilent. This behavior changed due to recent storage partitioning updates in Chrome and other browsers.

Are there any upcoming plans for @azure/msal-browser to incorporate alternatives for enabling silent login without relying solely on local storage? Considering evolving browser security measures like storage partitioning and third-party cookies phase out.

@coeguru You mentioned that instead of relying on the library you are storing access tokens within local storage from your code. how are you managing silent login in case of task pane reload or token expiration similar to @azure/msal-browser library's acquireTokenSilent method?

coeguru commented 11 months ago

@parinda296 , The token has the validity of 1hr. If there a new call to get the token in that one hour, I get it from the localStorage. If the token is expired, I am currently asking the user to login again.

There are 2 ways in which we can get around the token expiry -

  1. If you are using the token as a way to identify the user and you dont need the exact microsoft auth token, then we can generate our own token and store it in the local storage. Since we are generating the token, we can have a longer token.
  2. Not sure if there is a refresh token flow for login using msal.