okta / okta-oidc-android

OIDC SDK for Android
https://github.com/okta/okta-oidc-android
Other
60 stars 45 forks source link

Sign Out Functions Not Working #305

Closed gusuly0rum closed 2 years ago

gusuly0rum commented 2 years ago

Describe the bug?

Hello, we are having an issue with the signOut() or signOutOfOkta() functions. We are using the chrome custom tab to allow users to login to our app. But when logging out, chrome (or okta?) seems to retain the authentication cache. This creates a situation where even after logging out, when a user tries to log back in, okta doesn't ask for credentials because it thinks that the user is already logged in. Please let me know if you have any solutions thank you.

When debugging, there are no errors and AuthorizationStatus.SIGNED_OUT is being called correctly. After logging out, we tried to clear the chrome app cache and then logged back into our app and it works well (if you clear the chrome browser app's cache before logging in, okta asks for credentials instead of skipping the authentication). This makes me think that the browser session is not being correctly removed maybe.

What is expected to happen?

When signing in after signing out, okta should ask for credentials instead of skipping it.

What is the actual behavior?

When signing in after signing out, okta is not asking for credentials because it thinks the user was already authenticated.

Reproduction Steps?

class OktaAuthentication @Inject constructor(

    private val fragment: Fragment,
    private val webAuthClient: WebAuthClient,
    private val payload: AuthenticationPayload

) : DefaultLifecycleObserver,
    ResultCallback<AuthorizationStatus?, AuthorizationException?> {

    override fun onCreate(owner: LifecycleOwner) {
        super.onCreate(owner)
        webAuthClient.registerCallback(this, fragment.requireActivity())
    }

    val isAuthenticated: Boolean
        get() = webAuthClient.sessionClient.isAuthenticated

    fun login() {
        webAuthClient.signIn(fragment.requireActivity(), payload)
    }

    fun logout() {
        webAuthClient.signOutOfOkta(fragment.requireActivity())
    }

    override fun onSuccess(status: AuthorizationStatus) {
        if (status == AuthorizationStatus.SIGNED_OUT) {
            webAuthClient.sessionClient.clear()
        }
    }

    override fun onCancel() {
        Log.d("cancel", "canceled")
    }

    override fun onError(msg: String?, exception: AuthorizationException?) {
        Log.d("error", "$msg: ${exception?.errorDescription}")
    }
}

Additional Information?

No response

SDK Version

1.2.2

Build Information

No response

JayNewstrom commented 2 years ago

Hi @gusuly0rum it sounds like you have 2 issues:

  1. An unsuccessful logout
  2. Ensuring the user needs to sign in every time

For 1, I'd be happy to help, but there's not enough info in the issue for me to diagnose. If you want help, please give me steps to recreate, as I can't recreate the issue myself.

For 2, the best way is to use the AuthenticationPayload, something along the lines of this:

AuthenticationPayload payload = new AuthenticationPayload.Builder()
    .addParameter("prompt", "login")
    .build();

client.signIn(this, payload);
gusuly0rum commented 2 years ago

I am already using the payload with this code. What does .addParameter("prompt", "login") do exactly? I want to be clear that if the user did not logout I don't want to show the sign in page. I only want to show the sign in page if the user is already logged out.

fun createPayload(): AuthenticationPayload {
    return AuthenticationPayload.Builder()
        .setIdp(idp)
        .addParameter("aud", aud)
        .build()
    }
}
JayNewstrom commented 2 years ago

The payload parameters are documented here: https://developer.okta.com/docs/reference/api/oidc/#authorize

I see, so it looks like you're using an idp, and our signOut will not log you out of the IDP, so even if you log out of Okta, that IDP session may still exist.

gusuly0rum commented 2 years ago

I see, how do I clear the idp? I did not see any instructions in your README.md about clearing this.

JayNewstrom commented 2 years ago

It may be possible to configure here: https://help.okta.com/en/prod/Content/Topics/Apps/Apps_Single_Logout.htm

But this is outside the scope of the SDK. Feel free to reach out to https://support.okta.com/ for more information.

JayNewstrom commented 2 years ago

Feel free to open a new issue with more questions.

emathew80 commented 1 year ago

@JayNewstrom this remains to be a problem for us. Adding .addParameter("prompt", "login") was working for us until we changed clients. Now we have the same problem again.

It really feels like the chrome client doesn't get cleaned up properly. For example, when I login and use my user name and password, I am authenticated into the app. Next, I log out and I am taken back to our initial screen. When I open up our login screen again, I am fast forwarded to the app without asking for my username and password. We are not prompted for username or password even if we uninstall the app and install a clean new build (as long as the tokens are still valid). The only way that I have found to truly clear the chrome session is to go into the chrome app and clear all data and storage. Any ideas on how we can enforce the chrome custom tab to fully clear everything to do with the user through the SDK?

JayNewstrom commented 1 year ago

Hi @emathew80 are you sure you're not clearing the the session client client.getSessionClient().clear() before attempting to log out? You shouldn't call clear until the log out was successful. Without the data stored in the session client, the logout will fail.

Also, if you're using an external IdP to login (it seems like you are based on your code snippet earlier), signing out of Okta won't sign out of your IdP. Due to being signed in to the IdP, the Okta session will be resumed from the IdP session when attempting to reauthenticate.

emathew80 commented 1 year ago

@JayNewstrom we are calling client.sessionClient.clear() after we get a success with a AuthorizationStatus of AuthorizationStatus.SIGNED_OUT.

With this new client, we are not longer passing an idp

@Provides
@Singleton
fun providePayload(okta: Okta): AuthenticationPayload {
    return AuthenticationPayload.Builder()
        .addParameter("prompt", "login")
        .addParameter("aud", okta.aud)
        .build()
}
JayNewstrom commented 1 year ago

I'm still unable to reproduce, but if you have steps to recreate, or org settings, or a failing test case, I'd be happy to fix it. Another option would be to reach out to support to have them take a look at your configuration.

Another option would be to add a max_age when logging in, which will required the user to reauthenticate.

 return AuthenticationPayload.Builder()
        .addParameter("prompt", "login")
        .addParameter("aud", okta.aud)
        .addParameter("max_age", "10") // The time since the last authentication in seconds.
        .build()

You can read more about it here: https://developer.okta.com/docs/reference/api/oidc/#authorize

From the docs:

Allowable elapsed time, in seconds, since the last time the end user was actively authenticated by Okta.

emathew80 commented 1 year ago

Looks like the

.addParameter("max_age", "10")

partially works, when I logout after 10 seconds from logging in, I am asked to enter my password again, but we want the user to have to enter UN/PW (in case they want to change users).

The other issue is I dont have direct control of our organizations settings. We have a whole team that manages that side. The only thing I can control is anything to do with the Android application. That is why I as wondering if there is a "clear the cache" option for the session on the sdk.

I know this is an odd question, do you know what organization setting controls functionality for

    .addParameter("prompt", "login")

If I can identify that setting, I might be able to ask them a more pointed question to allow them to enable it for our new client.

gusuly0rum commented 1 year ago

@JayNewstrom Our app behaves as if some unknown entity still retains the session even when we are not using an idp anymore. Is this a possible scenario even after calling client.getSessionClient().clear()?

JayNewstrom commented 1 year ago

client.getSessionClient().clear() doesn't affect the browser at all.

Please reach out to support, they can better assist with org configuration issues.

If this is a bug, please open a new issue with steps to recreate with a new organization.

emathew80 commented 1 year ago

@JayNewstrom Thank you for all your help! We believe it is a configuration issue like you said. The discoveryUri that we were using is giving us back the wrong end session url. When I bypass the discovery url and hardcode authorize, token and end session with a CustomConfiguration, I am able to logout without issue. Were working with our company's okta team to get the discoveryUri updated.

Thank you for being patient with us and troubleshooting our issues. You rock!