React jest and MSAL getting BrowserAuthError : crypto

Viewed 2150

I'm trying to test a few components that are using MSAL for authentication.

Thus far, I have a simple test, which test if my component can render, as follows:

// <MsalInstanceSnippet>
const msalInstance = new PublicClientApplication({
  auth: {
    clientId: config.appId,
    redirectUri: config.redirectUri
  },
  cache: {
    cacheLocation: 'sessionStorage',
    storeAuthStateInCookie: true
  }
});

When I run the test, I'm getting the following error:

 BrowserAuthError: crypto_nonexistent: The crypto object or function is not available. Detail:Browser crypto or msCrypto object not available.

      10 |
      11 | // <MsalInstanceSnippet>
    > 12 | const msalInstance = new PublicClientApplication({
         |                      ^
      13 |   auth: {
      14 |     clientId: config.appId,
      15 |     redirectUri: config.redirectUri

      at BrowserAuthError.AuthError [as constructor] (node_modules/@azure/msal-common/dist/error/AuthError.js:27:24)
      at new BrowserAuthError (node_modules/@azure/msal-browser/src/error/BrowserAuthError.ts:152:9)
      at Function.Object.<anonymous>.BrowserAuthError.createCryptoNotAvailableError (node_modules/@azure/msal-browser/src/error/BrowserAuthError.ts:172:16)
      at new BrowserCrypto (node_modules/@azure/msal-browser/src/crypto/BrowserCrypto.ts:31:36)
      at new CryptoOps (node_modules/@azure/msal-browser/src/crypto/CryptoOps.ts:45:30)
      at PublicClientApplication.ClientApplication (node_modules/@azure/msal-browser/src/app/ClientApplication.ts:108:58)
      at new PublicClientApplication (node_modules/@azure/msal-browser/src/app/PublicClientApplication.ts:49:9)
      at Object.<anonymous> (src/App.test.tsx:12:22)

I'm unsure what the above means, but as far as I can understand, this error is occurring because the session is not authenticated.

My question can therefore be divided into the following:

What does this error mean? How can I solve this error? (Can we bypass MSAL by any chance for testing purposes?)

3 Answers

You need to add crypto to your Jest config in jest.config.js:

module.exports = {
    // ...
    globals: {
        // ...
        crypto: require("crypto")
    }
};

For eslint issue try this way

import crypto from 'crypto';

module.exports = {
    // ...
    globals: {
        // ...
        crypto,
    }
};

I tried adding crypto to my jest.config.js, but it didn't work. Then I tried adding it package.json. It was also pointless giving this error.

Out of the box, Create React App only supports overriding these Jest options:

  • clearMocks
  • collectCoverageFrom
  • coveragePathIgnorePatterns
  • coverageReporters
  • coverageThreshold
  • displayName
  • extraGlobals
  • globalSetup
  • globalTeardown
  • moduleNameMapper
  • resetMocks
  • resetModules
  • restoreMocks
  • snapshotSerializers
  • testMatch
  • transform
  • transformIgnorePatterns
  • watchPathIgnorePatterns.

These options in your package.json Jest configuration are not currently supported by Create React App:

In my case, I have a custom hook that has a dependency with msalInstance

I can prevent the above error by mocking my hook as said here

But still, this wasn't a good solution because if I have many hooks like this. So what I did was mock msalInstance in setupTests.ts file

jest.mock('./msal-instance', () => ({
  getActiveAccount: () => ({}),
  acquireTokenSilent: () => Promise.resolve({ accessToken: '' }),
}));

This is my msal-instance.ts

import {
  PublicClientApplication,
  EventType,
  EventMessage,
  AuthenticationResult,
} from '@azure/msal-browser';
import { msalConfig } from './authConfig';

const msalInstance = new PublicClientApplication(msalConfig);

// Account selection logic is app dependent. Adjust as needed for different use cases.
const accounts = msalInstance.getAllAccounts();
if (accounts.length > 0) {
  msalInstance.setActiveAccount(accounts[0]);
}

msalInstance.addEventCallback((event: EventMessage) => {
  if (event.eventType === EventType.LOGIN_SUCCESS && event.payload) {
    const payload = event.payload as AuthenticationResult;
    const { account } = payload;
    msalInstance.setActiveAccount(account);
  }
});

export default msalInstance;
Related