microsoft-authentication-library-for-js: Hash value cannot be processed because it is empty

Core Library

MSAL.js v2 (@azure/msal-browser)

Core Library Version

2.22.0

Wrapper Library

MSAL React (@azure/msal-react)

Wrapper Library Version

1.3.0

Description

The user cannot log in after entering email & password. The login popup gets closed and nothing happens.

After getting the error, that does not happen all times, it’s possible to login after clicking the login button again (sometimes, it is needed to click it several times). This intermittence makes it difficult to debug.

I basically followed this tutorial: https://docs.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-react

The Azure AD app in portal.azure.com (Microsoft_AAD_RegisteredApps) was configured with Single Page Application redirect URIs having the URI set matching the one configured in the msal configuration (auth.redirectUri property)

Screenshot 2022-03-04 at 17 17 12

Error Message

error BrowserAuthError: hash_empty_error: Hash value cannot be processed because it is empty. Please verify that your redirectUri is not clearing the hash. Given Url: https://myUrl.com/

Msal Logs

No response

MSAL Configuration

const msalConfig = {
  auth: {
    clientId: '930b1k1b-...',
    authority: 'https://login.microsoftonline.com/l2cba3e8-...',
    redirectUri: 'https://myUrl.com/'
  },
  cache: {
    cacheLocation: 'sessionStorage',
    storeAuthStateInCookie: false,
  },
}

Relevant Code Snippets

ClientApp instantiation:
---------------------------------------------------------------------
import { PublicClientApplication } from '@azure/msal-browser';
import { MsalProvider } from '@azure/msal-react';
import { msalConfig } from './config';

const msaInstance = new PublicClientApplication(msalConfig);

ReactDOM.render(
  <React.StrictMode>
    <MsalProvider instance={msaInstance}>
      <App />
    </MsalProvider>
  </React.StrictMode>,
  document.getElementById('root')
);
---------------------------------------------------------------------
Signing in:

const { instance } = useMsal();
const navigate = useNavigate();

let signin = () => {
    return instance
      .loginPopup({
       scopes: ['User.Read'],
      })
      .then((response) => {
          setUserData(response);
          navigate('welcomPagePath');
      })
      .catch((e) => {
        console.log('error', e);
      });
};

Reproduction Steps

  1. Enter Microsoft email
  2. Enter password

Expected Behavior

The user can log in

Identity Provider

Azure AD / MSA

Browsers Affected (Select all that apply)

Chrome

Regression

No response

Source

External (Customer)

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 42 (7 by maintainers)

Most upvoted comments

For me, when the login popup is opened in a popup window, then it succeeds. However, if the login popup is opened in a tab - then it fails with the same error as above (hash_empty_error). I find Chrome, running in fullscreen mode on a Mac (rather than just expanded to the display) will open the popup as a tab, rather than a new window - triggering the error.

We use the popup login methods. The popup first directs the user to Microsoft, they sign in (or are already signed in) and are then redirected to our redirectUri that we’ve set in both the PublicClientApplication config and Azure portal. The URL they’re redirected to has a hash appended (http://your.redirect.url#some-hash-from-microsoft). The parent window (the one where you created the popup from) reads the hash from the popup’s URL, and then closes the popup.

We had our redirect URL set to our application’s homepage (i.e. /). This meant that, when the popup loaded the page after being redirected back from Microsoft, our application was loaded - including the code that checks if the user is logged in. If the parent window didn’t read the hash in time before the application in the popup checked the user’s login status, then the hash would be cleared - and our parent application would error, as it couldn’t read the hash from the popup. So we had a race between the parent window reading the URL and our application loading - not good!

The solution, as given in docs and issue comments, was to redirect the popup to a blank page.

Could you let me know what this blank.html should consist of

Not very much, ideally. 😄 It’s literally just a page to redirect the popup to; it just needs to be on the same domain as your application, so the parent window can read from it. Here’s a (slightly redacted) version of ours:

<html>

<head>
  <title>Blank Page | [Our Application Name]</title>
</head>

<body>
  <!-- This is an intentionally blank page -->
</body>

</html>

This page should never be seen by your users. It will flash up quickly just before the popup closes, so having it as minimal as possible will help with performance. It doesn’t need to be styled - if anything, having content will confuse users who’ll see a momentary flash of content in the popup before its closed for them.

How will this redirect back to my application landing page

If you’re using the popup login, then it never redirects back to your application landing page. You have MSAL in your main application window that a user sees (we’ll call this the “parent”). The parent creates the popup, the popup goes to Microsoft, Microsoft redirects back to your blank page, and the parent reads from the popup URL before closing the popup. Your parent window never redirects to Microsoft - but when you’re logged in the AuthenticatedTemplate will show rather than UnauthenticatedTemplate.

We’ve resolved this in our application. For us, we had our redirect URL as the home page of our application. This caused an issue where our router (in Next.js) took over the routing in the popup, stripping the hash and redirecting it to the login page - before the window that launched the popup had time to extract it. Redirecting to a blank page allowed consistent logins.

These comments and documentation helped: https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/4550#issuecomment-1058519256 https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/4456#issuecomment-1046328877 https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/login-user.md#redirecturi-considerations

Great - then you can set that as your redirect URL in your PublicClientApplication config and Azure portal. 🙂

Seems like its working now… Still under testing though… Thanks for your help and valuable feedback !!

We use the popup login methods. The popup first directs the user to Microsoft, they sign in (or are already signed in) and are then redirected to our redirectUri that we’ve set in both the PublicClientApplication config and Azure portal. The URL they’re redirected to has a hash appended (http://your.redirect.url#some-hash-from-microsoft). The parent window (the one where you created the popup from) reads the hash from the popup’s URL, and then closes the popup.

We had our redirect URL set to our application’s homepage (i.e. /). This meant that, when the popup loaded the page after being redirected back from Microsoft, our application was loaded - including the code that checks if the user is logged in. If the parent window didn’t read the hash in time before the application in the popup checked the user’s login status, then the hash would be cleared - and our parent application would error, as it couldn’t read the hash from the popup. So we had a race between the parent window reading the URL and our application loading - not good!

This part I did understand. Thanks for giving a detailed info.

The solution, as given in docs and issue comments, was to redirect the popup to a blank page.

React being a SPA, is it even possible to have a url to open up a static html page ? My application entire routes + App components are bound by

const rootEl = document.getElementById('root');
const msalInstance = new PublicClientApplication(MSAL_CONFIG_TEST);

const render = Component =>
  // eslint-disable-next-line react/no-render-return-value
  ReactDOM.render(
    <MsalProvider instance={msalInstance}>
      <ErrorBoundary>
        <Provider store={store}>
          <div>
            <Component />
          </div>
        </Provider>
      </ErrorBoundary>
    </MsalProvider>,
    rootEl
  );

render(AppComponent);

According to msft docs, this blank route should be outside of the <MsalProvider /> . Is my understanding correct ? If so, how do we now open a blank.html outside of the SPA 🤔

Could you let me know what this blank.html should consist of

Not very much, ideally. 😄 It’s literally just a page to redirect the popup to; it just needs to be on the same domain as your application, so the parent window can read from it. Here’s a (slightly redacted) version of ours:

<html>

<head>
  <title>Blank Page | [Our Application Name]</title>
</head>

<body>
  <!-- This is an intentionally blank page -->
</body>

</html>

This page should never be seen by your users. It will flash up quickly just before the popup closes, so having it as minimal as possible will help with performance. It doesn’t need to be styled - if anything, having content will confuse users who’ll see a momentary flash of content in the popup before its closed for them.

How will this redirect back to my application landing page

If you’re using the popup login, then it never redirects back to your application landing page. You have MSAL in your main application window that a user sees (we’ll call this the “parent”). The parent creates the popup, the popup goes to Microsoft, Microsoft redirects back to your blank page, and the parent reads from the popup URL before closing the popup. Your parent window never redirects to Microsoft - but when you’re logged in the AuthenticatedTemplate will show rather than UnauthenticatedTemplate.

Again appreciate you providing this explanation regarding how MSAL is enabling authentication !! 👍

🎉This issue was addressed in #4793, which has now been successfully released as @azure/msal-browser@v2.25.0.🎉

Handy links:

@atkinchris’s answer above should resolve this issue - thank you for your input. We will update our docs to be more clear that you must either allow MSAL to complete processing before the router, or follow the docs listed and redirect to a blank page.

Thank you for your help @atkinchris!

Great - then you can set that as your redirect URL in your PublicClientApplication config and Azure portal. 🙂