syncweek-react-aad / react-aad

A React wrapper for Azure AD using the Microsoft Authentication Library (MSAL). The easiest way to integrate AzureAD with your React for authentication.
MIT License
345 stars 94 forks source link

"Uncaught (in promise) TypeError: Cannot read property 'tokenType' of undefined" when trying .getAccessToken() after initial login #285

Closed cheslijones closed 3 years ago

cheslijones commented 3 years ago

Library versions

Describe the bug Using an Incognito Chrome window, I navigate to my running web application, login to Azure AD, and then click a button that does a .getAccessToken(). The first time I do this, it fails and reloads the web app. The second time I do it, it performs it successfully.

Expected behavior I expected it to get the access token the first time I clicked the button.

To Reproduce I can provide my code:

// App.js

import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { AzureAD, AuthenticationState } from 'react-aad-msal';

// Component Imports
import Authentication from './authentication';
import Dashboard from './dashboard';
import Navbar from './navbar';

// Utilities Imports
import { authProvider } from '../utils/authProvider';

const App = () => {
  return (
    <AzureAD provider={authProvider} forceLogin={true}>
      {({ logout, authenticationState }) => {
        switch (authenticationState) {
          case AuthenticationState.Authenticated:
            return (
              <Router basename={process.env.PUBLIC_URL}>
                <Navbar logout={logout} />
                <Switch>
                  <Route path='/dashboard/' component={Dashboard} />
                  <Route path='/' component={Authentication} />
                </Switch>
              </Router>
            );
          default:
            return <div>Authenticating...</div>;
        }
      }}
    </AzureAD>
  );
};

export default App;
# authProvider.js

import { MsalAuthProvider, LoginType } from 'react-aad-msal';
import { Logger, LogLevel } from "msal";

// The auth provider should be a singleton. Best practice is to only have it ever instantiated once.
// Avoid creating an instance inside the component it will be recreated on each render.
// If two providers are created on the same page it will cause authentication errors.
export const authProvider = new MsalAuthProvider(
  {
    auth: {
      authority:
        'https://login.microsoftonline.com/<tenant_id>/',
      clientId: '<client_id>,
      postLogoutRedirectUri: window.location.origin + '/admin/',
      redirectUri: window.location.origin + '/admin/',
      validateAuthority: true,
    },
    // Enable logging of MSAL events for easier troubleshooting.
    // This should be disabled in production builds.
    system: {
      logger: new Logger(
        (logLevel, message, containsPii) => {
          console.log("[MSAL]", message);
        },
        {
          level: LogLevel.Verbose,
          piiLoggingEnabled: false
        }
      )
    },
    cache: {
      cacheLocation: 'sessionStorage',
      storeAuthStateInCookie: true,
    },
  },
  {
    scopes: ['user.read'],
  },
  {
    loginType: LoginType.Redirect,
    // When a token is refreshed it will be done by loading a page in an iframe.
    // Rather than reloading the same page, we can point to an empty html file which will prevent
    // site resources from being loaded twice.
    tokenRefreshUri: window.location.origin + '/admin/auth.html',
  }
);
// Authentication.js
...
  // This is where the .getAccessToken is that is triggered by a button click
  const testApiAuthentication = async () => {
    let token = await authProvider.getAccessToken();
    setAccessToken(token.accessToken);
    if (token.accessToken) {
      console.log(token.accessToken);
      setAuthenticatingToken(true);
      axios({
        method: 'post',
        url: '/api/user/',
        data: { access_token: token.accessToken },
        // headers: {
        //   'Accept': 'application/json',
        //   'Authorization': 'Bearer ' + token.accessToken
        // }
      })
        .then((response) => {
          console.log(response);
        })
        .catch((error) => {
          console.log(error);
        });
    }
  };
...

            <button onClick={testApiAuthentication}>
              Test API Authentication
            </button>

Desktop (please complete the following information):

loveneetsaini commented 3 years ago

Did you able to resolve it @cheslijones ?

janto commented 3 years ago

Anyone find a fix for this? Not sure if the same, but I get this error when I try to call authProvider.getAccessToken( ... forceRefresh : true) periodically with a setInterval(). This always causes a page reload which breaks page's state.

loveneetsaini commented 3 years ago

Any update?

sebastian-budka-mir commented 3 years ago

@cheslijones have you been able to resolve this issue? I run into the same problem and cannot find the way to resolve it.