Skip to content

[Msal 2.0.0-beta.3] ClientAuthError: state_mismatch: State mismatch error. Please check your network. Continued requests may cause cache overflow. #1846

@qszhuan

Description

@qszhuan

Please follow the issue template below. Failure to do so will result in a delay in answering your question.

Library

  • [ x] "@azure/msal-browser": "^2.0.0-beta.3"

Framework

Description

This error happened during I test the Auth code flow with the latest msal2 beta library. sometimes I can login without issue, but when I logout & re-login, the error happen.

Error Message

Security

  • Is this issue security related?

Regression

  • Did this behavior work before?
    Version:

MSAL Configuration

const config = {
  auth: {
    authority: process.env.REACT_APP_APP_AUTHORITY,
    clientId: process.env.REACT_APP_APP_ID,
    redirectUri: window.location.origin,
  },
  cache: {
    cacheLocation: 'sessionStorage',
    storeAuthStateInCookie: false,
  },
};

const loginRequest = {
  scopes: ['User.Read'],
};

const tokenRequest = {
  scopes: ['User.Read'],
};

other codes:

this is my AuthProvider class

// import {MsalAuthProvider, LoginType} from 'react-aad-msal';
import * as msal from '@azure/msal-browser';

const config = {
  auth: {
    authority: process.env.REACT_APP_APP_AUTHORITY,
    clientId: process.env.REACT_APP_APP_ID,
    redirectUri: window.location.origin,
  },
  cache: {
    cacheLocation: 'sessionStorage',
    storeAuthStateInCookie: false,
  },
};

const loginRequest = {
  scopes: ['User.Read'],
};

const tokenRequest = {
  scopes: ['User.Read'],
};

const LoginType = {
  LoginRedirect: 0,
  LoginPopup: 1,
};

class AuthProvider extends msal.PublicClientApplication {
  constructor(logintype, config) {
    super(config);
    this.loginType = logintype;
  }

  async getToken(request, account) {
    let requestAccount = account;
    if (account == null) {
      const accounts = await super.getAllAccounts();
      if (accounts && accounts.length === 1) {
        requestAccount = accounts[0];
      }
    }
    request = {...request, account: requestAccount};

    return super.acquireTokenSilent(request).catch((error) => {
      if (error instanceof msal.InteractionRequiredAuthError) {
        if (this.loginType === LoginType.LoginRedirect) {
          return super.acquireTokenRedirect(request);
        } 
        else {
          return super.acquireTokenPopup(request);
        }
      }
      throw error;
    });
  }

  _handleResponse(tokenResponse) {
    if (tokenResponse) {
      return tokenResponse;
    }
    const accounts = super.getAllAccounts();
    if (accounts && accounts.length === 1) {
      return this.getToken(loginRequest, accounts[0]).then((response) =>
        this._handleResponse(response)
      )
      .catch(error =>{
        if(error instanceof msal.AuthError){
          console.error('Error occured during getToken', error);
        }
        throw error;
      });
    
    }else if(accounts === null){
      // this.login();
    }
    
  }
  // use this promise to handle the response and error.
  async handleRedirectPromise() {
    return super.handleRedirectPromise()
    .then((response) => this._handleResponse(response))
    ;
  }

  async login() {
    if (this.loginType === LoginType.LoginPopup) {
      return super.loginPopup(loginRequest);
    } else if (this.loginType === LoginType.LoginRedirect) {
      return super.loginRedirect(loginRequest);
    }
  }

  async logout() {
    sessionStorage.clear();
    super.logout();
  }
}

const authProvider = new AuthProvider(LoginType.LoginRedirect, config);

export {authProvider, tokenRequest, loginRequest};

this is my AzureAD component:

import React, { useState, useEffect, useCallback } from "react";
import {
    authProvider
} from "../../utils/AuthProvider";


const AuthenticationState = {
    Authenticated : "Authenticated",
    UnAuthenticated: "UnAuthenticated",
    InProgress: "InProgress"
};

const AzureADApp = (Component) => {
    const AzureAdAppComponent = ({forceLogin, ...otherProps}) => {

        const [authenticationState, setAuthenticationState] = useState(AuthenticationState.UnAuthenticated);
        const [account, setAccount] = useState(null);
        const [accessToken, setAccessToken] = useState(null);
        const [roles, setRoles] = useState(null);
        const [error, setError] = useState(null);

        async function login() {
            if(authenticationState !== AuthenticationState.UnAuthenticated){
                return;
            }
            setAuthenticationState(AuthenticationState.InProgress);
            authProvider.login();
        }

        const loginCallBack = useCallback(
            () => {
                if(authenticationState !== AuthenticationState.UnAuthenticated){
                    return;
                }
                setAuthenticationState(AuthenticationState.InProgress);
                authProvider.login();
            },
            [authenticationState],
        )

        async function logout(){
            localStorage.clear();
            authProvider.logout();
        }
        
        useEffect(() =>{
            function handleResponse(tokenResponse){
                if(tokenResponse){
                    setAccount(tokenResponse.account);
                    setAuthenticationState(AuthenticationState.Authenticated);
                    if(tokenResponse.idTokenClaims){
                        setRoles(tokenResponse.idTokenClaims.roles)
                    }
                    setAccessToken(tokenResponse.accessToken)
                }else if(forceLogin && !error && authenticationState === AuthenticationState.UnAuthenticated){
                    loginCallBack();
                }
            }

            authProvider.handleRedirectPromise()
            .then(handleResponse)
            .catch(error => {
                setAuthenticationState(AuthenticationState.UnAuthenticated);
                console.log("error happened", error);
                setError(error.errorMessage);
            });
        }, [authenticationState, error, forceLogin, loginCallBack]);

      return <Component 
      {...otherProps}
      forceLogin={forceLogin}
      account={account}
      roles={roles}
      accessToken={accessToken}
      authenticationState={authenticationState}
      error={error}
      login={login}
      logout={logout}
      />;
    }
    return AzureAdAppComponent;
  }
export {AuthenticationState}
export default AzureADApp;

this is my App.js component:

import React, { useState, useEffect, useRef } from 'react';
import AzureADApp, { AuthenticationState } from './components/auth/AuthADApp';

function App(props) {
  if(props.error){
  return <div>error: {props.error} <button onClick={props.login}>login</button></div>
  }
  if(props.authenticationState === AuthenticationState.Authenticated){
    return <div>hello, <button onClick={props.logout}>logout</button></div>
  }
  if(props.authenticationState === AuthenticationState.UnAuthenticated && !props.forceLogin)
  return (<div>
      <button onClick={props.login}>Login</button>
  </div>);

  return <div>xxxxxxxxxxx</div>
}
export default AzureADApp(App);

this is the index.js:

ReactDOM.render(
  <React.StrictMode>
     <App forceLogin={false} />
  </React.StrictMode>,
  document.getElementById('root')
);

Reproduction steps

  1. start app.
  2. page will show a login button.
  3. click login, there is an error happen, it will execute this code and show the error message:
 if(props.error){
  return <div>error: {props.error} <button onClick={props.login}>login</button></div>
  }

when I check the sessionStorage, I got :
msal.xxxxx.urlHash: "#code=......&state=.....&session_state=......", and xxxxx is my app id.

  1. click the login button on the error page, then I can login successfully and see the hello message with logout button.

when executing the previous step, at the first time actually step 2 works, but after I logged out, it will always fail.

Expected behavior

Should login without error.

Browsers/Environment

  • [x ] Chrome
  • [x ] Edge

Metadata

Metadata

Assignees

Labels

bugA problem that needs to be fixed for the feature to function as intended.msal-browserRelated to msal-browser package

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions