okta / okta-react

Okta OIDC SDK for React
https://github.com/okta/okta-react
Other
114 stars 78 forks source link

When silently renewing token using @okta/okta-react 3.0.7 it refreshes the whole page #13

Open fetters5 opened 4 years ago

fetters5 commented 4 years ago

For the life of me I dont know how to change it so that the octa react package can switch to using iframes when renewing tokens. The current way the tokens are renewed it redirects to the okta server and then back causing a full page reload, is there a way to override this? Currently I pass the following service to the Security component AuthService

export const authService: AuthService = new AuthService({
  issuer: config.issuer,
  clientId: config.clientId,
  redirectUri: config.redirectUri,
  scopes: config.scopes,
  pkce: true,
  disableHttpsCheck: config.disableHttpsCheck,
  tokenManager: {
    expireEarlySeconds: 270,
  },
  isAuthenticated: async () => {
    const idToken = await authService.getTokenManager().get('idToken');
    const accessToken = await authService.getTokenManager().get('accessToken');
    return !!(idToken && accessToken);
  },
});

App.tsx

export const App = () => {
  return (
    <React.StrictMode>
      <Router>
        <Security authService={authService}>
          <GlobalContexts>
            <Suspense fallback={<LoadingIndicator />}>
              <Routes />
            </Suspense>
          </GlobalContexts>
        </Security>
      </Router>
    </React.StrictMode>
  );
};

Also I am a little unsure can I have an access token with a lifetime of say 2 hours, that can be refreshed for up to say 7 days with the pkce method or does it rely on the lifetime of the okta session?

Salinn commented 3 years ago

Bumping this as we are seeing the same issue and are on 4.1.0, we did not see this issue on version 3.0.1. Any help would be appreciated

swiftone commented 3 years ago

Catching up on this issue (thanks for the bump) -

@fetters5 -

can I have an access token with a lifetime of say 2 hours, that can be refreshed for up to say 7 days with the pkce method or does it rely on the lifetime of the okta session

It relies on the lifetime of the okta session to do renewals. Okta has just released one-time refresh tokens for SPAs, which will allow you to do renewals without relying on the okta session, speak to our support team (developers@okta.com) if you are interested in trying that feature in our Early Access program. ( https://help.okta.com/en/prod/Content/Topics/ReleaseNotes/early-access.htm ).

PKCE has no connection to the lifetime of a renewal, it is simply used to ensure that the recipient of the token is the same application that requested them.

@fetters5 and @Salinn -

A full page redirect should not be required to renew tokens, particularly in recent versions of our libraries. I'm happy to work to figure out what is going wrong, but the only code sample above is for a previous major version of the library. Can someone share a simple reproduction case?

Salinn commented 3 years ago

@swiftone tried reproducing this in a simple CRA and think it might actually be due to a useEffect that we were dependent on. Will respond back to this thread if the useEffect is not the problem.

Salinn commented 3 years ago

@swiftone after getting some more time to investigate I have figured out what is causing the issue for our team, it was due to us passing the component the function that returned a component to the component prop instead of the render prop. Once I switched that it worked as expected. Added an example in case anyone runs into this same issue.

TLDR: passing a function to the component to render on the secure route is what is causing the issue.

WRONG

function App() {
  const { userDispatch } = useUserState();
  useGetUser({ types, dispatch: userDispatch });
  return (
    <Switch>
        <SecureRoute
          path="/"
          exact
          component={() => ComponentWithHeaderFooter(Home)}
        />
        <Route path="/implicit/callback" component={LoginCallback} />
      </Switch>
  );
}

const ComponentWithHeaderFooter = (Component) => {
  return (
    <UserAuthenticationCheck>
      <Header />
      <main role="main" className="container">
        <Component />
      </main>
      <Footer />
    </UserAuthenticationCheck>
  );
};

Correct

function App() {
  const { userDispatch } = useUserState();
  useGetUser({ types, dispatch: userDispatch });
  return (
    <Switch>
        <SecureRoute
          path="/"
          exact
          render={() => ComponentWithHeaderFooter(Home)}
        />
        <Route path="/implicit/callback" component={LoginCallback} />
      </Switch>
  );
}
swiftone commented 3 years ago

@Salinn - thanks for reporting back! Glad you're working.

That's interesting - <SecureRoute> is just a thin layer around <Route>, so it accepts render/component/children, but we may want to do some checking/reporting if it turns out using component (or not using render) is always a bad idea.

Salinn commented 3 years ago

@swiftone please report back if you did that as I would be interested in those results. I found an easy way to reproduce and check this was with adding a counter that incremented on the home page for each render and watched it keep resetting to 0 with the componet={()=>....} and worked perfectly with children or render

rohit-khanna commented 3 years ago

I am currently using @okta/okta-react v4.0.1 and found a similar behavior where the auto-renew token, refreshes the whole page. Though, this behavior is only observed while using private browsing or incognito mode in browser. In normal mode, the token refresh happens silently. Does anyone else faced similar issue?

amit70 commented 3 years ago

@Salinn I tried your above approach and still no luck. Im using okta-react:3.0.0 & okta-signin-widget:3.9.0. Any help/suggestion?

const HasAccessToRouter = (securityConfig) => {
  const history = useHistory(); // example from react-router
  const [localSecurityConfig] = useState(securityConfig.securityConfig.config);

  const customAuthHandler = () => {
    history.push('/login');
  };

  return (
    <Security
      {...localSecurityConfig}
      onAuthRequired={customAuthHandler}>
      <div className="container-full-bg">
      <SecureRoute path="/" exact component={Home} />
        <Route path='/implicit/callback' component={LoginCallback} />
        {/* <Route path="/login" component={LoginPage} /> */}
        <Route path='/login' render={(props) => (<LoginPage {...props} oktaConfig={securityConfig.securityConfig.config} />)}/>
      </div>
    </Security>
  );
};

const App = (config) => {

  return(
  <div>
    <Router>
      <HasAccessToRouter securityConfig={config} />
    </Router>
  </div>);
};
amit70 commented 3 years ago

@swiftone any thoughts on the above observation?

swiftone commented 3 years ago

@amit70 - I'm no longer at Okta, so I can't give any insights from the SDK team there anymore, but per the suggestion above, have you tried replacing

<SecureRoute path="/" exact component={Home} />

with

<SecureRoute path="/" exact render={ () => <Home/> } />

?

ey52fpv commented 3 years ago

@swiftone Thanks for your response. I tried and still no luck. Also I have updated the okta plugins to latest.

"@okta/okta-react": "^5.1.2" "@okta/okta-signin-widget": "^5.6.3"

image

Any OKTA SDK team member can provide help/suggestion?

ey52fpv commented 3 years ago

I am currently using @okta/okta-react v4.0.1 and found a similar behavior where the auto-renew token, refreshes the whole page. Though, this behavior is only observed while using private browsing or incognito mode in browser. In normal mode, the token refresh happens silently. Does anyone else faced similar issue?

I tried upgrading the okta to v4.0.1 and okta-auth-js to v4.0.0, still facing the issue. Any lead?

rohit-khanna commented 3 years ago

@ey52fpv ,what I observed was- in case of incognito mode, if you allow third-party cookies (in Chrome, there is an "eye" on right side of address bar), the thing is solved. It Seems by allowing that, okta-sdk can use previous set cookies and does not treat it as a fresh session.

ey52fpv commented 3 years ago

@rohit-khanna Thank you for your response. Right now Im facing the issue in the normal browser session. As a workaround, I degraded to okta-react: 3.0.0 and okta-sign-widget: 3.9.0 and the app seems not to refresh when it tries to get a token.

alfredvaa commented 2 years ago

I had the same issue. I found out two different issues that caused this. As mentioned above by @Salinn one should not write

component={() => <Component />}

but instead:

render={() => <Component />}

This is actually mentioned in the docs for react-router, here:

When you use component (instead of render or children, below) the router uses React.createElement to create a new React element from the given component. That means if you provide an inline function to the component prop, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component. When using an inline function for inline rendering, use the render or the children prop (below). see react-router docs here

I am not sure but I can not see any upsides in using the component prop instead of the render prop if the intention is to use a function within, so just changing to render should be fine.

The above solves the issue when parts of the SPA is re-rendering. There are also cases when the complete page is reloaded. The cause of this is that third party cookies are disallowed in the browser. I got around this issue by enabling these cookies on the site (for firefox see here) but that is not a real end user solution to the problem.

Is there any solution to make it work with third party cookies disabled as well?

jaredperreault-okta commented 2 years ago

@alfredvaa I recommend upgrading to the latest @okta/okta-auth-js and @okta/react, the versions listed in this thread are out-of-date.

Routing samples: https://github.com/okta/okta-react/tree/master/samples/routing

Third Party Cookie Guidance: https://support.okta.com/help/s/article/FAQ-How-Blocking-Third-Party-Cookies-Can-Potentially-Impact-Your-Okta-Environment?language=en_US