okta / samples-js-react

React Auth SDK sample
Other
175 stars 260 forks source link

TypeError: okta_auth_js_1.default is not a constructor #128

Open MyShah1 opened 4 years ago

MyShah1 commented 4 years ago

React, redux with Visual studio enterprise, created default react app using Visual studio and followed step by step user guide for react application. While rendering Login Page I encountered this error:

LoginForm.tsx:18 Uncaught TypeError: okta_auth_js_1.default is not a constructor at new LoginForm (LoginForm.tsx:18) at constructClassInstance (react-dom.development.js:14185) at updateClassComponent (react-dom.development.js:18394) at beginWork$1 (react-dom.development.js:20161) at HTMLUnknownElement.callCallback (react-dom.development.js:336) at Object.invokeGuardedCallbackDev (react-dom.development.js:385) at invokeGuardedCallback (react-dom.development.js:440) at beginWork$$1 (react-dom.development.js:25738) at performUnitOfWork (react-dom.development.js:24662) at workLoopSync (react-dom.development.js:24638) at performSyncWorkOnRoot (react-dom.development.js:24237) at react-dom.development.js:12180 at unstable_runWithPriority (scheduler.development.js:818) at runWithPriority$2 (react-dom.development.js:12130) at flushSyncCallbackQueueImpl (react-dom.development.js:12175) at flushSyncCallbackQueue (react-dom.development.js:12163) at discreteUpdates$1 (react-dom.development.js:24390) at discreteUpdates (react-dom.development.js:1442) at dispatchDiscreteEvent (react-dom.development.js:5885)

MyShah1 commented 4 years ago

Tried few tweaks but no luck. New set of issues/observations.

Error | TS2580 | (TS) Cannot find name 'require'. Do you need to install type definitions for node? Try npm i @types/node. | MyApp, MyApp JavaScript Content Files | C:\Users\MSHAH\source\repos\sandbox\MyApp\ClientApp\src\Login\LoginForm.tsx | 27 | Active

aarongranick-okta commented 4 years ago

@MyShah1 Thanks for the report. Could you share here the relevant piece of code from LoginForm.tsx ?

MyShah1 commented 4 years ago

Here's the code snippet, last line thows the error. =====================================
var config = { pkce: false,

          // other config
          issuer: props.issuer,
          clientId: "123",
      };
      console.info('issuer' + props.issuer);
     // var OktaAuth = require('@okta/okta-auth-js');
    this.oktaAuth = new OktaAuth(config);
aarongranick-okta commented 4 years ago

@MyShah1 Are you using the latest version of okta-auth-js (4.0.0)? It was just released and the import style has changed to named exports. The guides in documentation need to be updated.

Use:

import { OktaAuth } from '@okta/okta-auth-js'

more information is here: https://github.com/okta/okta-auth-js#using-the-npm-module

MyShah1 commented 4 years ago

yes, I have already updated that, that is how I have coded but still I have been getting the error.

import { OktaAuth } from '@okta/okta-auth-js'; import { withOktaAuth } from '@okta/okta-react'; import * as React from 'react';

aarongranick-okta commented 4 years ago

The error

LoginForm.tsx:18 Uncaught TypeError: okta_auth_js_1.default is not a constructor

Indicates it is trying to use "default" import. This would happen by using a require() statement with Typescripts "esModuleInterop" option turned on, or by import OktaAuth from '@okta/okta-auth-js'. The way you have shown

import { OktaAuth } from '@okta/okta-auth-js' should not produce this error since it is using a named export.

The other possibility is a version conflict. The internal version of okta-auth-js used by React is at version 3. It's possible if you are using yarn/lerna that the dependency may be hoisted to the top level and the error is being emitted from within the React SDK trying to instantiate okta-auth-js@4.0. Since the React SDK is using okta-auth-js@3.2, I would recommend using that same version in your project's package.json outside the React SDK. There are no major feature changes in 4.0.0, the main change is this import style. We will have an update for React SDK soon which uses 4.0.

Alternatively you can access the React SDK's internal auth-js instance, it is available as a property named _oktaAuth

MyShah1 commented 4 years ago

Here's my import statements

import { OktaAuth } from '@okta/okta-auth-js'; import { withOktaAuth } from '@okta/okta-react'; import * as React from 'react';

require does not work as well, I'm willing to share my project and visual studio settings if required to troubleshoot the issue.

MyShah1 commented 4 years ago

can you please share some more details on how to access _oktaAuth? thanks

shuowu commented 4 years ago

@MyShah1 I notice you imported both okta-auth-js and okta-react in your code, which may cause tokens inconsistency issue if you have autoRenew turned on (it's on by default), since more than one autoRenew process will run together in the background. I would suggest to stay with only @okta/okta-react v3.x (which is depends on auth-js v3.x) for now. We will have auth-js v4.x included in the react SDK in the next major release.

Also wondering is there any specific reason that you want to use those two SDKs together?

mraible commented 3 years ago

@shuowu I believe both okta-auth-js and okta-react are required by Okta React v4.0.0 now.

I have this in my src/App.js:

import React, { Component } from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import { OktaAuth } from '@okta/okta-auth-js';
import { LoginCallback, Security } from '@okta/okta-react';
import Home from './Home';

const oktaAuth = new OktaAuth({
  issuer: 'https://dev-133320.okta.com/oauth2/default',
  clientId: '0oa5nak5fmUbfT3O3357',
  redirectUri: window.location.origin + '/callback'
});

class App extends Component {

  render() {
    return (
      <Router>
        <Security oktaAuth={oktaAuth}>
          <Route path="/" exact={true} component={Home}/>
          <Route path="/callback" component={LoginCallback}/>
        </Security>
      </Router>
    );
  }
}

export default App;

In my src/App.test.ts, I have:

import React from 'react';
import ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
import App from './App';

let container;

beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
});

afterEach(() => {
  document.body.removeChild(container);
  container = null;
});

test('renders learn react link', async () => {
  await act(async () => {
    ReactDOM.render(<App/>, container);
  });

  const linkElement = container.querySelector('a');
  expect(linkElement.textContent).toBe('Learn React');
});

When I run npm test, it fails with this error:

 FAIL  src/App.test.js
  ✕ renders learn react link (76 ms)

  ● renders learn react link

    TypeError: Cannot read property 'getAuthState' of undefined

      18 | test('renders learn react link', async () => {
      19 |   await act(async () => {
    > 20 |     ReactDOM.render(<App/>, container);
         |              ^
      21 |   });
      22 | 
      23 |   const linkElement = container.querySelector('a');

      at node_modules/@okta/src/Security.tsx:48:24

Any idea how to fix it?

aarongranick-okta commented 3 years ago

For those seeing this issue in tests, you may need to adjust your config: https://github.com/okta/okta-react/blob/master/jest.config.js#L23

okta-auth-js is an isomorphic module so it supports both a browser and node (server-side) endpoint. Jest tests run on Node and will use the server-side endpoint BY DEFAULT. The server-side module does not support the full interface, notably it does not have a getAuthState method.

dev-dyson commented 3 years ago

Thanks @aarongranick-okta! Adding this snippet to my package.json fixed the tests:

"jest": {
    "moduleNameMapper": {
      "^@okta/okta-auth-js$": "<rootDir>/node_modules/@okta/okta-auth-js/dist/okta-auth-js.umd.js"
    }
 }
aarongranick-okta commented 3 years ago

@MyShah1 were you able to resolve your issue, or can we provide further assistance?

niteshchaudhary commented 3 years ago

@mraible - I am facing exact issue while running my app locally (dint try tests) - were you able to fix this issue?

aarongranick-okta commented 3 years ago

@niteshchaudhary This error usually means that the commonJS version of the library is loaded, but the calling code is expecting ES. This can happen easily with esModuleInterop turned on in a typescript app, because it generates some code to make it work both ways.

If you are using ES syntax (recommended), it should be:

import { OktaAuth } from '@okta/okta-auth-js

Note that { OktaAuth } is inside curly braces.

If using commonJS (not recommended unless this is a NodeJS application)

const OktaAuth = require('@okta/okta-auth-js')

niteshchaudhary commented 3 years ago

@aarongranick-okta - I am using curly braces.

aarongranick-okta commented 3 years ago

@niteshchaudhary I'm assuming you are using the latest version of okta-auth-js (4.7.x). Are you using a yarn or lerna workspace or otherwise hoisting modules such that it is possible it is using an older version of the module?

What are you using for bundling? typescript, babel, webpack?

Any of these tools should be able to find the correct "browser" or "module" entrypoint, but if the bundler is confused, you may have to point it directly to the module entry:

node_modules/@okta/okta-auth-js/dist/okta-auth-js.umd.js

(hopefully it should not come to this)

niteshchaudhary commented 3 years ago

@aarongranick-okta - I guess, I got some hold of the issue. when I try to print oktaAuth.authStateManager - it prints "undefined". Hence I get error as TypeError: Cannot read property 'getAuthState' of undefined Can you suggest, how I should define it? I am using webpack and using okta-auth-js=4.7.1 and okta-react=4.1.0

and I changed my oktaConfig as below:

const oktaConfig = {
    clientId: 'abcd,
    issuer: 'https://abcdabcd.okta.com',
    pkce: false,
    transformAuthState: async (oktaAuth, authState) => {
        if (!authState.isAuthenticated) {
            return authState;
        }
    }
};

reference: https://github.com/okta/okta-auth-js#transformauthstate

aarongranick-okta commented 3 years ago

@niteshchaudhary authStateManager will exist on the browser instance, but would not exist on the node (server) instance. My guess is somehow your bundler is choosing the server entry point instead of browser. There is a module resolution strategy option for both babel and typescript, check if it is forcing commonJS.

cmacdonnacha commented 3 years ago

Hey guys, adding this worked

"jest": {
    "moduleNameMapper": {
      "^@okta/okta-auth-js$": "<rootDir>/node_modules/@okta/okta-auth-js/dist/okta-auth-js.umd.js"
    }
 }

However, I'm curios to know if this makes a real call out to the issuer url? I'd rather not be making real network requests during tests and just mock the auth object itself instead which is passed into the <Security> component. Any ideas on how to do that?

nikhileshncyb commented 2 years ago

Is there any update on this?

I'm trying to test App.tsx file containing following code:

import { CircularProgress } from '@mui/material';
import { OktaAuth, toRelativeUrl } from '@okta/okta-auth-js';
import { LoginCallback, SecureRoute, Security } from '@okta/okta-react';
import {
  Route, useHistory, useLocation, Redirect,
} from 'react-router-dom';
import './App.css';
import Header from './components/header';
import { oktaAuthConfig } from './config/env-constants';
import LandingPage from './modules/landing-page';
import LoginScreen from './modules/login';
import ErrorCallbackComponent from './modules/login-callback-error';

const errorCallbackComponent = (propsParam: any) => <ErrorCallbackComponent error={propsParam.error} />;

const oktaAuth = new OktaAuth({ ...oktaAuthConfig });

function App() {
  const history = useHistory();
  const restoreOriginalUri = async (_oktaAuth: any, originalUri: any) => {
    history.replace(toRelativeUrl(originalUri, window.location.origin));
  };
  const location = useLocation();
  const isUserLoggedIn = () => {
    const localStorageItem = JSON.parse(localStorage.getItem('okta-token-storage') || '{ }');
    return (
      !!(localStorageItem
        && localStorageItem.accessToken
        && localStorageItem.accessToken.accessToken));
  };

  return (
      <Security oktaAuth={oktaAuth} restoreOriginalUri={restoreOriginalUri}>
        <Header />
        <div>
          <Route
            path="/"
            exact
            render={() => { return isUserLoggedIn() ? <Redirect to="/home" /> : <LoginScreen />; }}
          />
          <SecureRoute path="/home" exact component={LandingPage} />
          <Route
            path="/callback"
            render={() => (
              <LoginCallback
                errorComponent={(props) => errorCallbackComponent(props)}
                loadingElement={(
                  <div style={{
                    height: '100vh',
                    width: '100vw',
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center',
                    alignContent: 'center',
                    }}
                  >
                    <CircularProgress color="inherit" />
                  </div>
                )}
              />
            )}
          />
        </div>
      </Security>
  );
}
export default App;

And the test is

import { render, waitFor } from '@testing-library/react';
import { MemoryRouter as Router } from 'react-router-dom';

import App from './App';

test('renders app', async () => {
  render(<Router initialEntries={['/home']}><App /></Router>);
  // const linkElement = screen.getByText(/learn react/i);
  // expect(linkElement).toBeInTheDocument();
  await waitFor(() => {
    expect(true).toBeTruthy();
  });
});

I keep getting TypeError: _oktaAuthJs.OktaAuth is not a constructor. I've added following configuration in package.json.

  "jest": {
    "moduleNameMapper": {
      "^@okta/okta-auth-js$": "<rootDir>/node_modules/@okta/okta-auth-js/dist/okta-auth-js.min.js"
    }
  }

However if I change the configuration to point to <rootDir>/node_modules/@okta/okta-auth-js/dist/okta-auth-js.umd.js, I get

 FAIL  src/App.test.tsx (9.356 s)
  × renders app (989 ms)

  ● renders app

    No useable method found in ["native","idb","localstorage"]

       5 |
       6 | test('renders app', async () => {
    >  7 |   render(<Router initialEntries={['/home']}><App /></Router>);
         |   ^
       8 |   // const linkElement = screen.getByText(/learn react/i);
       9 |   // expect(linkElement).toBeInTheDocument();
      10 |   await waitFor(() => {

      at node_modules/@okta/okta-auth-js/dist/webpack:/OktaAuth/node_modules/broadcast-channel/dist/esbrowser/method-chooser.js:42:25
denysoblohin-okta commented 2 years ago

@nikhileshncyb As a workaround, please add in jest setup file:

// broadcast-channel should not detect node environment
// https://github.com/pubkey/broadcast-channel/blob/master/src/util.js#L61
process[Symbol.toStringTag] = 'Process';
dayamoraes commented 1 year ago

hey, did anyone manage to run it? I'm using jest, I've already done the mapping of the module and the solution above and still nothing

mraymond77 commented 1 year ago

+1, The workaround is not working for me as well.

I am in an non-ejected create-react-app, which requires putting jest setup code in src/setupTests.js (instead of configuring the setupFilesAfterEnv clause). Ostensibly it should be the same effect.

I have the line process[Symbol.toStringTag] = 'Process'; in my setupTests.js file, and confirm it is updating process toString to return "Process". But still seeing:

TypeError: _oktaAuthJs.OktaAuth is not a constructor

I know there have been new releases in both okta-auth-js and broadcast-channel since a year ago, any of those changes effect this?