Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

374 Support End Session #433

Open
wants to merge 1 commit into
base: master
from

Conversation

@smokienko
Copy link

@smokienko smokienko commented Nov 29, 2018

Implements End Session support flow.
Adds unit tests.
Updates readme.

@codecov-io
Copy link

@codecov-io codecov-io commented Nov 29, 2018

Codecov Report

Merging #433 into master will increase coverage by 0.11%.
The diff coverage is 83.91%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master     #433      +/-   ##
============================================
+ Coverage     83.71%   83.82%   +0.11%     
- Complexity      479      514      +35     
============================================
  Files            42       46       +4     
  Lines          2345     2455     +110     
  Branches        233      238       +5     
============================================
+ Hits           1963     2058      +95     
- Misses          299      310      +11     
- Partials         83       87       +4
Impacted Files Coverage Δ Complexity Δ
...y/java/net/openid/appauth/RegistrationRequest.java 93.15% <ø> (ø) 9 <0> (ø) ⬇️
library/java/net/openid/appauth/TokenRequest.java 87.82% <ø> (ø) 8 <0> (ø) ⬇️
.../java/net/openid/appauth/AuthorizationRequest.java 92.9% <100%> (-0.1%) 14 <2> (+1)
...ary/java/net/openid/appauth/EndSessionRequest.java 100% <100%> (ø) 9 <9> (?)
...nid/appauth/AuthorizationServiceConfiguration.java 89.88% <100%> (+2.98%) 12 <1> (+2) ⬆️
.../openid/appauth/AuthorizationServiceDiscovery.java 81.65% <100%> (+0.34%) 28 <1> (+1) ⬆️
...penid/appauth/AuthorizationManagementResponse.java 46.15% <46.15%> (ø) 3 <3> (?)
...java/net/openid/appauth/AuthorizationResponse.java 87.5% <50%> (-0.64%) 14 <1> (+1)
.../java/net/openid/appauth/AuthorizationService.java 72% <60%> (-0.99%) 19 <4> (+4)
...penid/appauth/AuthorizationManagementActivity.java 83.72% <76.19%> (+0.57%) 21 <2> (ø) ⬇️
... and 6 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7386114...c75d373. Read the comment docs.

@smokienko smokienko force-pushed the smokienko:end_session_support branch from 3fbb1fd to e689d62 Nov 30, 2018
Sergiy Mokiyenko
@nicholasxmode
Copy link

@nicholasxmode nicholasxmode commented Jan 6, 2019

Any idea when this will be available? End Session is an important feature for an auth library...

@@ -12,7 +12,7 @@ android {
// Make sure this is consistent with the redirect URI used in res/raw/auth_config.json,
// or specify additional redirect URIs in AndroidManifest.xml
manifestPlaceholders = [
'appAuthRedirectScheme': 'net.openid.appauthdemo'
'appAuthRedirectScheme': 'com.lohika.android.test'

This comment has been minimized.

@nicholasxmode

nicholasxmode Jan 15, 2019

this should be updated before merging to master

This comment has been minimized.

@ngallazzi

ngallazzi Jan 30, 2019

Why it hasn't been merged yet?

This comment has been minimized.

@iainmcgin

iainmcgin Feb 21, 2019
Member

Because there is no maintainer - see #444.

@Barryrowe
Copy link

@Barryrowe Barryrowe commented Feb 21, 2019

What can we in the community to do support getting this into master for a new release?

@iainmcgin
Copy link
Member

@iainmcgin iainmcgin commented Feb 21, 2019

Someone needs to step up and take on the role of maintainer - see #444.

@Ju4nD4v1d
Copy link

@Ju4nD4v1d Ju4nD4v1d commented Sep 6, 2019

This should be merge already!

@DayS
Copy link

@DayS DayS commented Nov 12, 2019

Good job 👍

For those looking to use end session support : until a new maintainer is found for this project, you can use a dependency to a binary built from this PR, by using Jitpack (the feature was released yesterday and works like a charm :) )

Juste add the following to your code.

Root build.gradle file :

allprojects {
	repositories {
		...
		maven { url 'https://jitpack.io' }
	}
}

App's build.gradle file :

dependencies {
    implementation 'com.github.openid:AppAuth-Android:PR433-SNAPSHOT'
}

Note that this is not an ideal solution, more a workaround to use this feature.

@MohamedHatemAbdu
Copy link

@MohamedHatemAbdu MohamedHatemAbdu commented Feb 18, 2020

Is there any updates regarding merging this branch into master ?

@rvplauborg
Copy link

@rvplauborg rvplauborg commented Feb 19, 2020

We too really need this functionality for proper sign-out.

@rvplauborg
Copy link

@rvplauborg rvplauborg commented Feb 24, 2020

We ended up forking, and using Jitpack to use a dependency built from our own fork with the PR changes, and end session is working for us now without having to do all kinds of workarounds ourselves. Can recommend this approach if you too are in a situation where you cannot just replace your client library with an entirely different one.

@Barryrowe
Copy link

@Barryrowe Barryrowe commented Feb 24, 2020

For anyone that is still holding out for someone to pick up support of this library, and get this PR merged, but needs to be able to support logging out now without patching in this entire PR (which does. solve it properly), here is the solution we are using:

  1. Extend AuthorizationService with a CustomAuthorizationService
  2. Add and implement fun getLogoutIntent(request: AuthorizationRequest): Intent { //... }
  3. Use request.configuration.toJson() to access the "end_session_endpoint" on your config that is not exposed through the AuthorizationServiceConfiguration class, and build an intent with the resulting URL

Here's our CustomAuthorizationService:

import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import net.openid.appauth.AppAuthConfiguration
import net.openid.appauth.AuthorizationManagementActivity
import net.openid.appauth.AuthorizationRequest
import net.openid.appauth.AuthorizationService
import net.openid.appauth.internal.Logger
import net.openid.appauth.internal.UriUtil

class CustomAuthorizationService(private val context: Context, private val appAuthConfig: AppAuthConfiguration) : AuthorizationService(context, appAuthConfig) {

    fun getLogoutIntent(
        request: AuthorizationRequest
    ): Intent {

        val logoutIntent = prepareLogoutIntent(request, createCustomTabsIntentBuilder().build())
        return AuthorizationManagementActivity.createStartForResultIntent(
            context,
            request,
            logoutIntent
        )
    }


    /**
     * This is copied directly (and then modified) from {@link net.openid.appauth.AuthorizationService.prepareAuthorizationRequestIntent} since we need
     * to build the same intent, but with the logout destination. Hopefully this can be removed once this PR or similar
     * is merged and released with the AppAuth sdk:
     *    https://github.com/openid/AppAuth-Android/pull/433
     */
    private fun prepareLogoutIntent(
        request: AuthorizationRequest,
        customTabsIntent: CustomTabsIntent
    ): Intent {

        if (browserDescriptor == null) {
            throw ActivityNotFoundException()
        }

        val requestUri = buildLogoutUri(request) // This is where our change comes in. Build Logout instead of authUri
        val intent: Intent = if (browserDescriptor.useCustomTab) {
                customTabsIntent.intent
            } else {
                Intent(Intent.ACTION_VIEW)
            }
        intent.setPackage(browserDescriptor.packageName)
        intent.data = requestUri

        Logger.debug(
            "Using %s as browser for auth, custom tab = %s",
            intent.getPackage(),
            browserDescriptor.useCustomTab.toString()
        )

        Logger.debug(
            "Initiating authorization request to %s",
            request.configuration.authorizationEndpoint
        )

        return intent
    }


    /**
     * The block below was lifted from {@link net.openid.appauth.AuthorizationRequest} since we need to build a proper
     * URI for logout, but it's not possible yet with the current AppAuth library.
     */

    internal val PARAM_CLIENT_ID = "client_id"
    internal val PARAM_CODE_CHALLENGE = "code_challenge"
    internal val PARAM_CODE_CHALLENGE_METHOD = "code_challenge_method"
    internal val PARAM_DISPLAY = "display"
    internal val PARAM_LOGIN_HINT = "login_hint"
    internal val PARAM_ID_TOKEN_HINT = "id_token_hint"
    internal val PARAM_PROMPT = "prompt"
    internal val PARAM_REDIRECT_URI = "redirect_uri"
    internal val PARAM_RESPONSE_MODE = "response_mode"
    internal val PARAM_RESPONSE_TYPE = "response_type"
    internal val PARAM_SCOPE = "scope"
    internal val PARAM_STATE = "state"

    private fun buildLogoutUri(request: AuthorizationRequest): Uri {

        // This is where we rely on the end_session_endpoint being available in the discoveyDoc
        val url = request.configuration.toJson().getJSONObject("discoveryDoc").getString("end_session_endpoint")

        val uriBuilder = Uri.parse(url).buildUpon()
            .appendQueryParameter(PARAM_REDIRECT_URI, request.redirectUri.toString())
            .appendQueryParameter(PARAM_CLIENT_ID, request.clientId)
            .appendQueryParameter(PARAM_RESPONSE_TYPE, request.responseType)

        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_DISPLAY, request.display)
        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_LOGIN_HINT, request.loginHint)
        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_PROMPT, request.prompt)
        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_STATE, request.state)
        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_SCOPE, request.scope)
        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_RESPONSE_MODE, request.responseMode)

        if (request.codeVerifier != null) {
            uriBuilder.appendQueryParameter(PARAM_CODE_CHALLENGE, request.codeVerifierChallenge)
                .appendQueryParameter(PARAM_CODE_CHALLENGE_METHOD, request.codeVerifierChallengeMethod)
        }

        for (entry in request.additionalParameters.entries) {
            uriBuilder.appendQueryParameter(entry.key, entry.value)
        }

        return uriBuilder.build()
    }
}
@Eightyplus
Copy link

@Eightyplus Eightyplus commented Mar 11, 2020

I'm also looking for a logout feature.

@oahmedazab
Copy link

@oahmedazab oahmedazab commented Mar 29, 2020

@Barryrowe
can you please explain your solution more when i will call getLogoutIntent

@oahmedazab
Copy link

@oahmedazab oahmedazab commented Mar 30, 2020

@Barryrowe
can you please explain your solution more when i will call getLogoutIntent

Thanks man i got it

@Barryrowe
Copy link

@Barryrowe Barryrowe commented Mar 30, 2020

@oahmedazab Glad you found it helpful!

For anyone else, you would use getLogoutIntent just like you do getAuthorizationRequestIntent for logging in, just during your app's Logout flow.

I did forget to note explicitly, for anyone else, that the above implementation expects using configuration discovery, where the logout endpoint is returned as part of the remote discoveryDoc. The first line of buildLogoutUri() has a comment where you could provide your logout endpoint if you are not using config discovery.

@mshuf
Copy link

@mshuf mshuf commented Apr 20, 2020

@oahmedazab Glad you found it helpful!

For anyone else, you would use getLogoutIntent just like you do getAuthorizationRequestIntent for logging in, just during your app's Logout flow.

I did forget to note explicitly, for anyone else, that the above implementation expects using configuration discovery, where the logout endpoint is returned as part of the remote discoveryDoc. The first line of buildLogoutUri() has a comment where you could provide your logout endpoint if you are not using config discovery.

Thanks @Barryrowe, your comments helped me ending a session in Xamarin.Android until this is merged into master.

@Pavel-Sulimau
Copy link

@Pavel-Sulimau Pavel-Sulimau commented Apr 20, 2020

@oahmedazab Glad you found it helpful!
For anyone else, you would use getLogoutIntent just like you do getAuthorizationRequestIntent for logging in, just during your app's Logout flow.
I did forget to note explicitly, for anyone else, that the above implementation expects using configuration discovery, where the logout endpoint is returned as part of the remote discoveryDoc. The first line of buildLogoutUri() has a comment where you could provide your logout endpoint if you are not using config discovery.

Thanks @Barryrowe, your comments helped me ending a session in Xamarin.Android until this is merged into master.

Hey @mshuf, could you please share your Xamarin.Android solution for the issue?

@mshuf
Copy link

@mshuf mshuf commented Apr 21, 2020

var endsessionEndpointURI = Uri.Parse(string.Format("{0}?id_token_hint={1}&post_logout_redirect_uri={2}", yourBaseURL + "connect/endsession", userIdToken, yourCallBackURI));

var intentBuilder = _authService.CreateCustomTabsIntentBuilder(endsessionEndpointURI);
var customTabsIntent = intentBuilder.Build();
customTabsIntent.Intent.AddFlags(ActivityFlags.SingleTop);
customTabsIntent.Intent.SetData(endsessionEndpointURI);

Intent logoutIntent = AuthorizationManagementActivity.CreateStartForResultIntent(MainActivity.Instance, _authRequest, customTabsIntent.Intent);
MainActivity.Instance.StartActivity(logoutIntent);

@Pavel-Sulimau This is basically what I'm rolling with for now... It launches the end session URI in a custom tab and subsequently logs the user out. From there the user clicks a button which re-directs back to the app where I handle and then display the login screen again. You can have it automatically redirect depending on your auth server setup. Hope this helps.

@Eightyplus
Copy link

@Eightyplus Eightyplus commented May 10, 2020

Nice solution, add id_token_hint and post_logout_redirect_uri to be redirected after successful logout! 🚀

    fun endSession(
        startActivityForResult: StartActivityForResult,
        redirectLogoutUrl: String,
        idToken: String,
        authServiceConfiguration: AuthorizationServiceConfiguration
    ) {

        val endSessionEndpoint = authServiceConfiguration.toJson().getJSONObject("discoveryDoc")
            .getString("end_session_endpoint")
        val reqParam = "id_token_hint=$idToken&post_logout_redirect_uri=${redirectLogoutUrl}"

        val uri = Uri.parse("$endSessionEndpoint?$reqParam")
        val intent = if (isPackageInstalled(context, CUSTOM_TAB_PACKAGE_NAME)) {
            val intent = CustomTabsIntent.Builder().setShowTitle(true).build()
            intent.intent.setPackage(CUSTOM_TAB_PACKAGE_NAME)
            intent.intent.data = uri
            intent.intent
        } else {
            Intent(Intent.ACTION_VIEW, uri)
        }
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
        startActivityForResult(intent, REQUEST_CODE_LOG_OUT)
    }

startActivityForResult activity function is parameterized as activity::startActivityForResult

@keyhuihk01
Copy link

@keyhuihk01 keyhuihk01 commented Jul 10, 2020

Conflicting files
library/java/net/openid/appauth/AuthorizationRequest.java

@lsh-silpion
Copy link

@lsh-silpion lsh-silpion commented Aug 13, 2020

Any news here? I would need that feature for a Keycloak-Installation

@djdance djdance mentioned this pull request Sep 17, 2020
@kushanshah11
Copy link

@kushanshah11 kushanshah11 commented Sep 17, 2020

For anyone that is still holding out for someone to pick up support of this library, and get this PR merged, but needs to be able to support logging out now without patching in this entire PR (which does. solve it properly), here is the solution we are using:

  1. Extend AuthorizationService with a CustomAuthorizationService
  2. Add and implement fun getLogoutIntent(request: AuthorizationRequest): Intent { //... }
  3. Use request.configuration.toJson() to access the "end_session_endpoint" on your config that is not exposed through the AuthorizationServiceConfiguration class, and build an intent with the resulting URL

Here's our CustomAuthorizationService:

import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import net.openid.appauth.AppAuthConfiguration
import net.openid.appauth.AuthorizationManagementActivity
import net.openid.appauth.AuthorizationRequest
import net.openid.appauth.AuthorizationService
import net.openid.appauth.internal.Logger
import net.openid.appauth.internal.UriUtil

class CustomAuthorizationService(private val context: Context, private val appAuthConfig: AppAuthConfiguration) : AuthorizationService(context, appAuthConfig) {

    fun getLogoutIntent(
        request: AuthorizationRequest
    ): Intent {

        val logoutIntent = prepareLogoutIntent(request, createCustomTabsIntentBuilder().build())
        return AuthorizationManagementActivity.createStartForResultIntent(
            context,
            request,
            logoutIntent
        )
    }


    /**
     * This is copied directly (and then modified) from {@link net.openid.appauth.AuthorizationService.prepareAuthorizationRequestIntent} since we need
     * to build the same intent, but with the logout destination. Hopefully this can be removed once this PR or similar
     * is merged and released with the AppAuth sdk:
     *    https://github.com/openid/AppAuth-Android/pull/433
     */
    private fun prepareLogoutIntent(
        request: AuthorizationRequest,
        customTabsIntent: CustomTabsIntent
    ): Intent {

        if (browserDescriptor == null) {
            throw ActivityNotFoundException()
        }

        val requestUri = buildLogoutUri(request) // This is where our change comes in. Build Logout instead of authUri
        val intent: Intent = if (browserDescriptor.useCustomTab) {
                customTabsIntent.intent
            } else {
                Intent(Intent.ACTION_VIEW)
            }
        intent.setPackage(browserDescriptor.packageName)
        intent.data = requestUri

        Logger.debug(
            "Using %s as browser for auth, custom tab = %s",
            intent.getPackage(),
            browserDescriptor.useCustomTab.toString()
        )

        Logger.debug(
            "Initiating authorization request to %s",
            request.configuration.authorizationEndpoint
        )

        return intent
    }


    /**
     * The block below was lifted from {@link net.openid.appauth.AuthorizationRequest} since we need to build a proper
     * URI for logout, but it's not possible yet with the current AppAuth library.
     */

    internal val PARAM_CLIENT_ID = "client_id"
    internal val PARAM_CODE_CHALLENGE = "code_challenge"
    internal val PARAM_CODE_CHALLENGE_METHOD = "code_challenge_method"
    internal val PARAM_DISPLAY = "display"
    internal val PARAM_LOGIN_HINT = "login_hint"
    internal val PARAM_ID_TOKEN_HINT = "id_token_hint"
    internal val PARAM_PROMPT = "prompt"
    internal val PARAM_REDIRECT_URI = "redirect_uri"
    internal val PARAM_RESPONSE_MODE = "response_mode"
    internal val PARAM_RESPONSE_TYPE = "response_type"
    internal val PARAM_SCOPE = "scope"
    internal val PARAM_STATE = "state"

    private fun buildLogoutUri(request: AuthorizationRequest): Uri {

        // This is where we rely on the end_session_endpoint being available in the discoveyDoc
        val url = request.configuration.toJson().getJSONObject("discoveryDoc").getString("end_session_endpoint")

        val uriBuilder = Uri.parse(url).buildUpon()
            .appendQueryParameter(PARAM_REDIRECT_URI, request.redirectUri.toString())
            .appendQueryParameter(PARAM_CLIENT_ID, request.clientId)
            .appendQueryParameter(PARAM_RESPONSE_TYPE, request.responseType)

        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_DISPLAY, request.display)
        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_LOGIN_HINT, request.loginHint)
        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_PROMPT, request.prompt)
        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_STATE, request.state)
        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_SCOPE, request.scope)
        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_RESPONSE_MODE, request.responseMode)

        if (request.codeVerifier != null) {
            uriBuilder.appendQueryParameter(PARAM_CODE_CHALLENGE, request.codeVerifierChallenge)
                .appendQueryParameter(PARAM_CODE_CHALLENGE_METHOD, request.codeVerifierChallengeMethod)
        }

        for (entry in request.additionalParameters.entries) {
            uriBuilder.appendQueryParameter(entry.key, entry.value)
        }

        return uriBuilder.build()
    }
}

Hi @Barryrowe can you please help me to integrate this?
how can i integrate this in Android Code?

means how to call on Logout and redirect to another activity?

Thanks in advance.

@kushanshah11
Copy link

@kushanshah11 kushanshah11 commented Sep 17, 2020

Nice solution, add id_token_hint and post_logout_redirect_uri to be redirected after successful logout!

    fun endSession(
        startActivityForResult: StartActivityForResult,
        redirectLogoutUrl: String,
        idToken: String,
        authServiceConfiguration: AuthorizationServiceConfiguration
    ) {

        val endSessionEndpoint = authServiceConfiguration.toJson().getJSONObject("discoveryDoc")
            .getString("end_session_endpoint")
        val reqParam = "id_token_hint=$idToken&post_logout_redirect_uri=${redirectLogoutUrl}"

        val uri = Uri.parse("$endSessionEndpoint?$reqParam")
        val intent = if (isPackageInstalled(context, CUSTOM_TAB_PACKAGE_NAME)) {
            val intent = CustomTabsIntent.Builder().setShowTitle(true).build()
            intent.intent.setPackage(CUSTOM_TAB_PACKAGE_NAME)
            intent.intent.data = uri
            intent.intent
        } else {
            Intent(Intent.ACTION_VIEW, uri)
        }
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
        startActivityForResult(intent, REQUEST_CODE_LOG_OUT)
    }

startActivityForResult activity function is parameterized as activity::startActivityForResult

Hi @Eightyplus

can you please let me know below code usage?

if (isPackageInstalled(context, CUSTOM_TAB_PACKAGE_NAME)) {
val intent = CustomTabsIntent.Builder().setShowTitle(true).build()
intent.intent.setPackage(CUSTOM_TAB_PACKAGE_NAME)
intent.intent.data = uri
intent.intent
}

What should i pass CUSTOM_TAB_PACKAGE_NAME ?

what should be isPackageInstalled?

@Barryrowe
Copy link

@Barryrowe Barryrowe commented Sep 17, 2020

@kushanshah11

For anyone else, you would use getLogoutIntent just like you do getAuthorizationRequestIntent for logging in, just during your app's Logout flow.

I did forget to note explicitly, for anyone else, that the above implementation expects using configuration discovery, where the logout endpoint is returned as part of the remote discoveryDoc. The first line of buildLogoutUri() has a comment where you could provide your logout endpoint if you are not using config discovery.

To expand slighlty, you would use the getLogoutIntent() function to build the logout intent, then pass that to startActivityForResult(), and handle the result in onActivityResult(), at which point you can navigate to your desired post-logout activity as you see fit.

You could optionally use authService.performAuthorizationRequest() to automatically go to your desired activity on completed logout.

@Eightyplus
Copy link

@Eightyplus Eightyplus commented Sep 18, 2020

@kushanshah11 , ah I left out some code.

const pointing at package for chrome
const val CUSTOM_TAB_PACKAGE_NAME = "com.android.chrome"

one way to check if Chrome is installed

    fun isPackageInstalled(context: Context, packageName: String): Boolean {
        return try {
            context.packageManager.getPackageInfo(packageName, 0)
            true
        } catch (e: PackageManager.NameNotFoundException) {
            false
        }
    }
@kushanshah11
Copy link

@kushanshah11 kushanshah11 commented Sep 21, 2020

@kushanshah11 , ah I left out some code.

const pointing at package for chrome
const val CUSTOM_TAB_PACKAGE_NAME = "com.android.chrome"

one way to check if Chrome is installed

    fun isPackageInstalled(context: Context, packageName: String): Boolean {
        return try {
            context.packageManager.getPackageInfo(packageName, 0)
            true
        } catch (e: PackageManager.NameNotFoundException) {
            false
        }
    }

HI @Eightyplus I have implemented your code and it works but now i have one issue.
when i perform logout - > logout browser will open
and in onresultactivity i have pt\ut code for redirect to login

now issue is on logout browser remain open and display you are successfully logout and in back of browser Login activity also redirected.

question is how to close browser on logout?

Thanks in advance.

@Eightyplus
Copy link

@Eightyplus Eightyplus commented Sep 21, 2020

I guess we had the same issue. The server has to configure/enable logout. We use identity server, and have setup like this

PostLogoutRedirectUris = new[] { "com.domain.myapp:/logout" },

Replace com.domain.myapp to match the one you configured for manifestPlaceholders: [ appAuthRedirectScheme = ...

HI @Eightyplus I have implemented your code and it works but now i have one issue.
when i perform logout - > logout browser will open
and in onresultactivity i have pt\ut code for redirect to login

now issue is on logout browser remain open and display you are successfully logout and in back of browser Login activity also redirected.

question is how to close browser on logout?

Thanks in advance.

@kushanshah11
Copy link

@kushanshah11 kushanshah11 commented Sep 21, 2020

@kushanshah11

For anyone else, you would use getLogoutIntent just like you do getAuthorizationRequestIntent for logging in, just during your app's Logout flow.

I did forget to note explicitly, for anyone else, that the above implementation expects using configuration discovery, where the logout endpoint is returned as part of the remote discoveryDoc. The first line of buildLogoutUri() has a comment where you could provide your logout endpoint if you are not using config discovery.

To expand slighlty, you would use the getLogoutIntent() function to build the logout intent, then pass that to startActivityForResult(), and handle the result in onActivityResult(), at which point you can navigate to your desired post-logout activity as you see fit.

You could optionally use authService.performAuthorizationRequest() to automatically go to your desired activity on completed logout.

Hi @Barryrowe ,
i have also implemented your solution and it works but how to close logout webview?

can you please let me know solution for close logout browser?

thanks in advance.

@kushanshah11
Copy link

@kushanshah11 kushanshah11 commented Sep 21, 2020

I guess we had the same issue. The server has to configure/enable logout. We use identity server, and have setup like this

PostLogoutRedirectUris = new[] { "com.domain.myapp:/logout" },

Replace com.domain.myapp to match the one you configured for manifestPlaceholders: [ appAuthRedirectScheme = ...

HI @Eightyplus I have implemented your code and it works but now i have one issue.
when i perform logout - > logout browser will open
and in onresultactivity i have pt\ut code for redirect to login
now issue is on logout browser remain open and display you are successfully logout and in back of browser Login activity also redirected.
question is how to close browser on logout?
Thanks in advance.

so based on my understanding first server guy have to add postlogoutredirecturi in discovery document right?

after that what is our part or its automatically close?

Thanks in advance

@Eightyplus
Copy link

@Eightyplus Eightyplus commented Sep 21, 2020

If you use custom tab / chrome, it will close and redirect. If it runs on an old device with older browser, probably not, but will redirect to your app.

so based on my understanding first server guy have to add postlogoutredirecturi in discovery document right?

after that what is our part or its automatically close?

Thanks in advance

@kushanshah11
Copy link

@kushanshah11 kushanshah11 commented Sep 21, 2020

If you use custom tab / chrome, it will close and redirect. If it runs on an old device with older browser, probably not, but will redirect to your app.

so based on my understanding first server guy have to add postlogoutredirecturi in discovery document right?
after that what is our part or its automatically close?
Thanks in advance

yes by default it use chrome. so once server guy added that endpoint in discovery it will automatically works correct ?

@Barryrowe
Copy link

@Barryrowe Barryrowe commented Sep 21, 2020

@kushanshah11

For anyone else, you would use getLogoutIntent just like you do getAuthorizationRequestIntent for logging in, just during your app's Logout flow.

I did forget to note explicitly, for anyone else, that the above implementation expects using configuration discovery, where the logout endpoint is returned as part of the remote discoveryDoc. The first line of buildLogoutUri() has a comment where you could provide your logout endpoint if you are not using config discovery.

To expand slighlty, you would use the getLogoutIntent() function to build the logout intent, then pass that to startActivityForResult(), and handle the result in onActivityResult(), at which point you can navigate to your desired post-logout activity as you see fit.
You could optionally use authService.performAuthorizationRequest() to automatically go to your desired activity on completed logout.

Hi @Barryrowe ,
i have also implemented your solution and it works but how to close logout webview?

can you please let me know solution for close logout browser?

thanks in advance.

@kushanshah11 There are several pieces to understand about how the redirect works. There are a few different things that could be going on if you're not properly getting redirected to your app after the logout action occurs.

The relevant documentation is in the root of this project here: https://github.com/openid/AppAuth-Android#capturing-the-authorization-redirect

  1. Your app must have a registered URI scheme to handle login/logout redirects.
  2. Your oauth server should redirect to a url that matches that scheme for both actions, likely with an extra path or query param to differentiate login vs logout
  3. You need to add that redirect url for the actions when building the authoriaztion intents. If you use the discovery document, you can pull it from there, but if your oAuth server doesn't provide the discovery document, you can provide it directly however you see fit as a string.

If you're not getting redirected with all the proper setup, make sure you're on API 21 or higher. I believe API 16-19 has some flaky handling of the login flows if you're using startActivityForResult

@danijelt
Copy link

@danijelt danijelt commented Sep 23, 2020

@Barryrowe Something is not clear from your patch, how do you send the id_token_hint parameter (that is being sent by the code in this pull request)? Actually, under what OIDC standard/draft does your logout code work? What IdP do you work with?

@Barryrowe
Copy link

@Barryrowe Barryrowe commented Sep 23, 2020

@danijelt This is a good question. Our identity provider is Identity Server 4

I was fumbling my way through patching this when I originally wrote this, and left out that detail. That unused PARAM_ID_TOKEN_HINT was added before realizing I don't have access to the AuthState inside the intent builder function. It looks like I'm adding id_token_hint and the post_logout_redirect_uri params when building the AutorizationRequest to pass into getLogoutIntent() using the setAdditionalParameters function.

So given a valid AuthState, and the normal client configuration items (clientId/RedirectUri/scope), my Request Building looks like:

val redirectUri = Uri.parse(redirectUrl)
val authRequest = AuthorizationRequest.Builder(
        state.authorizationServiceConfiguration!!,
        clientId,
        ResponseTypeValues.CODE,
        redirectUri
    )
    .setScope(scope)
    .setAdditionalParameters(
        mapOf(
            LOGOUT_PARAM_ID_TOKEN_HINT to state.idToken,
            LOGOUT_PARAM_POST_LOGOUT_REDIRECT to "$redirectUrl/logout"
        )
    )
    .build()

val intent = (authService as CustomAuthorizationService).getLogoutIntent(authRequest)
@danijelt
Copy link

@danijelt danijelt commented Sep 23, 2020

@Barryrowe Thanks for clearing it up! So, the ID token is being sent implicitly with setAdditionalParameters? That's what confused me and I thought you went out-of-spec.

@Barryrowe
Copy link

@Barryrowe Barryrowe commented Sep 23, 2020

@danijelt The buildLogoutUri function does this at the very end of the function. This is also done in the AuthorizationRequest.toUri() function, which is where I copied the uri building logic from.

https://github.com/openid/AppAuth-Android/blob/master/library/java/net/openid/appauth/AuthorizationRequest.java#L1003

My solution has always been a patch, so it's a little ugly. I'm hoping this PR, or a similar one will get merged and released eventually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

You can’t perform that action at this time.