Okta-React logout not working

To preface, this used to work and to my knowledge we haven’t changed anything that should impact logout… The short of it is when calling authService.logout() the library attempts to perform a “revoke” on the server and at the same time the browser pops up a “login box” (never seen this before this issue). Known credentials when entered into this login box (which shouldn’t be there anyway), don’t dismiss it and it can only be removed via “cancel” button. Upon cancel the revoke (which was pending while the browser’s login box is up) returns with a 401. I am not sure where to start troubleshooting this as it worked a week or two ago and we are using the react library exclusively. Thanks for any help or direction

@jwilkin Can I ask how did you build your logout flow? Can you please try to refer the below docs and try it again?


It includes react library.
We also have a sample code here:

If you still see the pop up box, can you please attach a screenshot as well as your code snippets? Thanks

Sure, my implementation is rather simple… In a react component, in a useEffect callback I call:

await authService.logout(’/’);

when this call happens, the below login box pops up
image

and the following api call is made:


with POST data:

once the login box is dimissed
the revoke call returns with a 401 code:
{“error”:“invalid_client”,“error_description”:“Client authentication failed. Either the client or the client credentials are invalid.”}

In the Okta Dev Console log I see:

Again, this has worked for months, it just recently started exhibiting this behavior… it is possible we did something, but since we haven’t touched the Okta integration code I am not sure what it could be

thanks for any help

One thing I have in some digging and learning…
From:


and

it seems the revoke endpoint being called requires client authentication. After making a call to the list apps api I see that my app has “token_endpoint_auth_method=client_secret_basic” This authentication method requires:
Authorization: Basic ${Base64(<client_id>:<client_secret>)}

Yet in:


it seems only the client_id is being used for the basic auth. I could be completely missing something, but this would seem on its face to indicate why that call is getting a 401

I also just verified that reverting back to “@okta/okta-auth-js”: “3.2.1” “fixes” this issue for me in that the “revoke” endpoint is not called in this version. I didn’t notice I had experienced a version bump in this package as I was only depending on okta-react and clearing my package-lock.json to address another issue also caused the version increase for okta-auth-js.

looking at the changes, it looks like we had been the benefit of a bug on the signOut method which caused the access token to not be fetched properly leading to skipping the call to revoke

@jwilkin Thanks for the update. It sounds like we found the root cause. Can you please confirm if reverting to auth-js 3.2.1 resolved all the issues now? As for the package upgrade, it depends on how you rule your package json The versions could be upgraded accordingly, like only auto upgrade patch version or auto upgrade the minor version.

Yes reverting to 3.2.1 of the Okta-Auth-Js library got me back to a “working” state, however like I mentioned it was only “working” because of a bug in that version that has been fixed in 3.2.5. I wasn’t specifically referencing that package previously as it was just pulled in from Okta-React package and now I am referencing that exact version as its bug is masking the bug I experienced.

Keep in mind this isn’t a long-term fix as I am now version locked without the ability to update until the real problem with the revoke call is addressed…

await authService.logout(’/’);
used in okta/samples-js-react seems to me incorrect, because the logout argument is supposed to be options object not a string (maybe it was in old version, I am not sure)
okta-react (which has very poor documentation) is using okta-auth-js which has signOut actually documented.

_convertLogoutPathToOptions(redirectUri) { 
    if (typeof redirectUri !== 'string') {
      return redirectUri;
    }
    // If a relative path was passed, convert to absolute URI
    if (redirectUri.charAt(0) === '/') {
      redirectUri = window.location.origin + redirectUri;
    }
    return {
      postLogoutRedirectUri: redirectUri,
    };
  }

async logout(options={}) {
    options = this._convertLogoutPathToOptions(options);
    await this._oktaAuth.signOut(options);
    this.clearAuthState();
}

_convertLogoutPathToOptions is trying to convert the uri to options but if you use string and not whole object it does not give you ability to turn off revoking of the tokens (which is most of the time useless in my opinion, because jwt tokens are most often validated ‘offline’ using just signature without calling the identity provider to verify the revocation of tokens.)
and based on https://github.com/okta/okta-auth-js

signOut takes the following options:

  • postLogoutRedirectUri - Setting a value will override the postLogoutRedirectUri configured on the SDK.
  • state - An optional value, used along with postLogoutRedirectUri . If set, this value will be returned as a query parameter during the redirect to the postLogoutRedirectUri
  • idToken - Specifies the ID token object. By default, signOut will look for a token object named idToken within the TokenManager . If you have stored the id token object in a different location, you should retrieve it first and then pass it here.
  • revokeAccessToken - If false , the access token will not be revoked. Use this option with care: not revoking the access token may pose a security risk if the token has been leaked outside the application.
  • accessToken - Specifies the access token object. By default, signOut will look for a token object named accessToken within the TokenManager . If you have stored the access token object in a different location, you should retrieve it first and then pass it here. This options is ignored if the revokeAccessToken option is false .

it seems if revoking fails you have to set revokeAccessToken to false like:

await authService.logout({
        revokeAccessToken: false,
        postLogoutRedirectUri: 'registered logout uri'
    });

That is an interesting work-around, I will give that a try… while at this point I am ok that the token itself isn’t revoked as like you mentioned they aren’t valid for that long anyway, however should that ever be desired I hope they fix the underlying bug at work here, namely that react-auth-js is making a server call that is being rejected with a 401. @jupiik I appreciate you digging into this for me

hi @jwilkin - are you able to confirm you are using Single Page App (SPA) application type?