okta-oidc-js icon indicating copy to clipboard operation
okta-oidc-js copied to clipboard

authState.isPending is true after logout

Open patkovskyi opened this issue 5 years ago • 17 comments

I'm submitting this issue for the package(s):

  • [x] okta-react

I'm submitting a:

  • [x] Bug report

Current behavior

authState.isPending is true after calling authService.logout() Screenshot 2020-09-01 at 18 49 56

This seems wrong to me since it breaks this official example:

const { authState, authService } = useOktaAuth();
 if (authState.isPending) {
   return <div>Loading...</div>;
 }

Expected behavior

All fields of authState are set to null after logout.

Minimal reproduction of the problem with instructions

On https://github.com/okta/samples-js-react (custom-login)

@shuowu: go to login page, then click back button directly without login.

Not the scenario I initially described, but probably has the same root cause (isPending == true when it should not be).

Extra information about the use case/user story you are trying to implement

Essentially, this is what happens: when a user initiates logout, the first authState change the app is receiving looks like the screenshot (all fields are null, isPending is true). After that, most of the time there is another authState update with isPending == false (I don't know what triggers it). But sometimes it does not happen and users observe "Loading..." indefinitely. In any case, I can verify with a debugger that the first update to authState after logout always contains isPending == true which seems to be a bug.

Environment

  • Package Version: 3.0.4
  • Browser: Chrome 86.0.4240.8
  • OS: MacOS Catalina 10.15.6
  • Node version (node -v): v12.18.3

patkovskyi avatar Sep 01 '20 16:09 patkovskyi

@patkovskyi isPending is set to true (the default state) by design when logout happens, as a success logout will do a hard reload to refresh the memory, then the client will re-evaluate the authState to set isPending to either true or false.

But sometimes it does not happen and users observe "Loading..." indefinitely.

This may be caused by a logout failure, then the authState failed to be updated properly.

I think it would make sense to update authState.error if logout fails.

Internal Ref: OKTA-326953

shuowu avatar Sep 01 '20 16:09 shuowu

@shuowu thanks for taking a look!

I find the meaning of isPending quite confusing. My initial expectation was that it means: authentication flow is currently running. But there's no ongoing authentication flow after logout.

I encountered two cases when having isPending set to true by design consistently breaks a React app:

  1. onbeforeunload event was intercepted and a user chose not to leave the page after authService.logout() was executed. I guess hard reload you're mentioning does not happen in this case.
  2. when I'm not logged in and navigate via back button to the /login route which contains this code:
const { authState, authService } = useOktaAuth();
if (authState.isPending) {
  return <div>Loading...</div>;
}

isPending is true but the authentication process does not trigger so the page just stays in "Loading..."

patkovskyi avatar Sep 01 '20 17:09 patkovskyi

My initial expectation was that it means: authentication flow is currently running

@patkovskyi - Close, but not quite. isPending means "We are currently figuring out what your authorization is". This can be waiting on an async currently-running authorization flow, but it could also be waiting on an async retrieval of tokens (such as IndexedDB).

Authorization state is decided by checking tokens, tokens MAY be stored in an async way, so knowing if you are authorized is async until it is decided, and until that is decided you are 'pending'.

That's why on initial load, the auth state is pending, and if you aren't authenticated, it will stop being pending and become false - the async token check returned, and you weren't authenticated.

swiftone avatar Sep 01 '20 17:09 swiftone

@swiftone thanks for clarification!

So, is there anything I can do to make okta-react figure out the real authentication state after I use back button (more details in my previous message)?

patkovskyi avatar Sep 01 '20 17:09 patkovskyi

@patkovskyi

onbeforeunload event was intercepted and a user chose not to leave the page after authService.logout() was executed.

The hard reload is not only to clear the memory, but also redirect to logout uri to clear user session from backend, then redirect user back based on the logoutRedirectUrl, which means this process should not be interrupted with onbeforeunload listener.

when I'm not logged in and navigate via back button to the /login route which contains this code

The sdk evaluated the authState based on the tokens state when the route changes in app. So I don't think any action is needed from the app code. Or do you see issues with navigate back by the back button?

shuowu avatar Sep 01 '20 17:09 shuowu

@shuowu

When I click Back after a full successful logout, it brings me to /login route where isAuthenticated == false, isPending == true, tokens are undefined and nothing else happens.

patkovskyi avatar Sep 01 '20 18:09 patkovskyi

Here's a video repro for the Back button issue: https://www.dropbox.com/s/t6xrm5adozxpswp/Screen%20Recording%202020-09-01%20at%2021.00.03.mov I show the whole flow: Login -> Logout -> clicking Back (around 00:55)

patkovskyi avatar Sep 02 '20 10:09 patkovskyi

@patkovskyi Thanks for the video, I am able to reproduce the issue locally now.

Internal Ref: OKTA-327283

shuowu avatar Sep 02 '20 18:09 shuowu

@shuowu

I was unable to reproduce this issue on samples-js-react custom-login, even after I changed okta-react version from 3.0.0 to 3.0.4. I'm curious how you reproduced it, maybe it points to some specific thing in my code that triggers this issue.

Meanwhile, I think I found a dirty workaround: adding authState.isAuthenticated === null condition fixes the problem in my repro video.

const { authState, authService } = useOktaAuth();
if (authState.isPending && authState.isAuthenticated === null) {
  return <div>Loading...</div>;
}

return authState.isAuthenticated
    ? <Redirect to={{ pathname: Routes.root }} />
    : <SignInWidget />;

patkovskyi avatar Sep 02 '20 19:09 patkovskyi

@patkovskyi go to login page, then click back button directly without login.

shuowu avatar Sep 02 '20 19:09 shuowu

Having the same issue. Any fixes in the works?

sero323 avatar Sep 22 '20 21:09 sero323

I'm also having this issue. At 3.0.8, isPending is true after having logged out, and stays true. Downgrading to 3.0.0 produces expected results, where isPending is true until isAuthenticated is false.

lee-bennie avatar Oct 09 '20 01:10 lee-bennie

@sero323 & @lee-bennie: We do not have a solution at this time, but the team is reviewing priorities today and I'll see if we can get this bumped up.

Meanwhile, can you clarify if this is happening every time on logout, most times, or occasionally? (original report was "most of the time it works")

Also, can you detail what steps your app takes on logout to see if anything is different from the other report?

swiftone avatar Oct 09 '20 16:10 swiftone

@sero323 & @lee-bennie: We do not have a solution at this time, but the team is reviewing priorities today and I'll see if we can get this bumped up.

Meanwhile, can you clarify if this is happening every time on logout, most times, or occasionally? (original report was "most of the time it works")

Also, can you detail what steps your app takes on logout to see if anything is different from the other report?

In my case I'm really just treating "after logout" as a blank slate, so if my issue doesn't belong here, let me know. But I've narrowed the change down to v3.0.4.

So at <= 3.0.3, when with when I look at the state of Security, I can see: { _authState: { isAuthenticated: false, idToken: undefined, accessToken: undefined } }

and then at >= 3.0.4:

{ _authState: { isAuthenticated: false, idToken: undefined, accessToken: undefined, isPending: true } }

These are with clean application storage, so no cookies, no local or session storage. Clean install of node modules.

authParams: { responseType: ['id_token', 'token'], scopes: ['openid', 'email', 'profile'], pkce: true, }

lee-bennie avatar Oct 09 '20 18:10 lee-bennie

Any progress on this issue?

sero323 avatar Jan 13 '21 18:01 sero323

@sero323 - This should be corrected in okta-react 3.0.9+, see https://github.com/okta/okta-react/releases/tag/okta-react-3.0.9.

Let us know if you continue to see this problem.

swiftone avatar Jan 13 '21 18:01 swiftone

@sero323 I can confirm that 3.0.9 fixed it for me.

lee-bennie avatar Jan 13 '21 19:01 lee-bennie