openid / AppAuth-Android

Android client SDK for communicating with OAuth 2.0 and OpenID Connect providers.
https://openid.github.io/AppAuth-Android
Apache License 2.0
2.79k stars 877 forks source link

Problem with redirect Intent handling when app is started via App Link #996

Open SPodjasek opened 11 months ago

SPodjasek commented 11 months ago

Configuration

Description

I've run into a very specific edge case problem inside my app which probably involves intent handling by Android.

So, the app offers authorization with proprietary OIDC compatible provider - and this works flawlessly. But besides that it offers registration flow and password recovery flow. Both rely on user clicking a one-time link in email message they receive - and the problem is with that two flows. Those links are handled by the app as App Links. App verifies tokens it receives in link, asks user for confirmation and starts authentication flow with AppAuth supplying tokens in additional parameters. Everything runs fine in CustomTab until provider finishes authentication - and after that things get strange....

When you use adb am start to simulate click on App Link everything is fine, Intent is delivered to RedirectUriReceiverActivity in running application instance and properly forwarded to AuthorizationManagementActivity, then integration handles response and everything finishes as expected.

But when you click the link from GMail, after finishing OIDC flow a new App instance is started every time. It doesn't have access to saved state and logs following error No stored state - unable to handle response - so the flow couldn't finish properly. Although on OIDC side everything finishes properly, user is registered properly on his password is properly reset. This from user perspective is very confusing.

From what I've found by now the problem is with flags that are used to start the App via App Links. When you use adb it defaults to only using FLAG_ACTIVITY_NEW_TASK 0x10000000, but when you click on a link it uses following:

When I've tried to set similar flags with adb it seems that only FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET is causing problem to occur. When I start Intent without it (0x90400) everything works.

Digging further into that I've tried experimenting with documentLaunchMode, launchMode and allowTaskReparenting with various combinations. I've also tried setting redirect URI to HTTPS (I've used custom scheme as default) and the results are always the same.

Anyone here run into similar problem?

This issue is critical to this App and as I've wasted few days already debugging this I'll probably implement those not working flows without AppAuth to make App usable - but in future I'll like to use original design (BTW application is multi-platform and with iOS it works properly).

Below you can find logcat entries from tests. Those starting with A are from success tests. Those with B are from failed ones.

  1. Starting App from App Link - notice different result code

    A: START u0 {act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=https://<domain>/... flg=0x10000000 pkg=<appid> cmp=<appid>/.MainActivity} with LAUNCH_SINGLE_TOP from uid 2000 (BAL_ALLOW_PERMISSION) result code=3
      START u0 {act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=https://<domain>/... flg=0x10000000 cmp=<appid>/.MainActivity} with LAUNCH_SINGLE_TOP from uid 2000 (BAL_ALLOW_PERMISSION) result code=3
      START u0 {act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=https://<domain>/... flg=0x10010400 cmp=<appid>/.MainActivity} with LAUNCH_SINGLE_TOP from uid 2000 (BAL_ALLOW_PERMISSION) result code=3
    B: START u0 {act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=https://<domain>/... flg=0x90400 cmp=<appid>/.MainActivity (has extras)} with LAUNCH_SINGLE_TOP from uid 10224 (BAL_ALLOW_VISIBLE_WINDOW) result code=0
      START u0 {act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=https://<domain>/... flg=0x10090400 cmp=<appid>/.MainActivity} with LAUNCH_SINGLE_TOP from uid 2000 (BAL_ALLOW_PERMISSION) result code=0
      START u0 {act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=https://<domain>/... flg=0x90400 cmp=<appid>/.MainActivity (has extras)} with LAUNCH_SINGLE_TOP from uid 10224 (BAL_ALLOW_VISIBLE_WINDOW) result code=0
      START u0 {act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=https://<domain>/... flg=0x90400 cmp=<appid>/.MainActivity (has extras)} with LAUNCH_SINGLE_INSTANCE from uid 10224 (BAL_ALLOW_VISIBLE_WINDOW) result code=2
  2. After CustomTab finishes - same as above, result code for AuthorizationManagementActivity differs

    A: START u0 {act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=<appid>:/... flg=0x14000000 cmp=<appid>/net.openid.appauth.RedirectUriReceiverActivity (has extras)} with LAUNCH_MULTIPLE from uid 10204 (BAL_ALLOW_VISIBLE_WINDOW) result code=0
    B: START u0 {act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=<appid>:/... flg=0x14000000 cmp=<appid>/net.openid.appauth.RedirectUriReceiverActivity (has extras)} with LAUNCH_MULTIPLE from uid 10204 (BAL_ALLOW_VISIBLE_WINDOW) result code=0
      START u0 {act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=<appid>:/... flg=0x14000000 cmp=<appid>/net.openid.appauth.RedirectUriReceiverActivity (has extras)} with LAUNCH_MULTIPLE from uid 10204 (BAL_ALLOW_VISIBLE_WINDOW) result code=0
      START u0 {act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=<appid>:/... flg=0x14000000 cmp=<appid>/net.openid.appauth.RedirectUriReceiverActivity (has extras)} with LAUNCH_SINGLE_INSTANCE from uid 10204 (BAL_ALLOW_VISIBLE_WINDOW) result code=0
      START u0 {act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=https://<domain>/... flg=0x14000000 cmp=<appid>/net.openid.appauth.RedirectUriReceiverActivity (has extras)} with LAUNCH_SINGLE_INSTANCE from uid 10204 (BAL_ALLOW_VISIBLE_WINDOW) result code=0
      START u0 {act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=https://<domain>/... flg=0x14000000 cmp=<appid>/net.openid.appauth.RedirectUriReceiverActivity (has extras)} with LAUNCH_MULTIPLE from uid 10204 (BAL_ALLOW_VISIBLE_WINDOW) result code=0
    
    A: START u0 {dat=<appid>:/... flg=0x24000000 cmp=<appid>/net.openid.appauth.AuthorizationManagementActivity} with LAUNCH_SINGLE_TASK from uid 10553 (BAL_ALLOW_VISIBLE_WINDOW) result code=3
    B: START u0 {dat=<appid>:/... flg=0x24000000 cmp=<appid>/net.openid.appauth.AuthorizationManagementActivity} with LAUNCH_SINGLE_TASK from uid 10553 (BAL_ALLOW_VISIBLE_WINDOW) result code=0 
      START u0 {dat=https://<domain>/... flg=0x24000000 cmp=<appid>/net.openid.appauth.AuthorizationManagementActivity} with LAUNCH_SINGLE_TASK from uid 10553 (BAL_ALLOW_VISIBLE_WINDOW) result code=0
John-59 commented 9 months ago

Yes, I run into with very similar problem. I create application which needs authorization with OIDC provider (Keycloak) and I have 3 situation:

  1. If the user is already authorized in the application and then clicks on a link (from mail, for example) to a specific application screen - all works fine and the application starts and shows this screen.
  2. If the user is not previously authorized and simulates to click on a link by command "adb shell am start -d "link.to.concrete.application.screen" -a android.intent.action.VIEW" - all works fine and the application starts, redirects to Chrome Custom Tab for authorization and then shows the screen according to the link.
  3. But if the user not previously authorized and clicks on a link - the application starts, redirects to Chrome Custom Tab for authorization, but after authorization nothing happens (not redirect back to the application, Chrome Custom Tab remains open).

AppAuth version: 0.11.1 Language: Kotlin OIDC provider: Keycloak

tnache commented 5 months ago

Hi all, I've observed the same issue with my app (native Kotlin Android). I am using version 0.11.1 of the library. When the app gets started with an app link (clicking on a link in an email) the authorization flow ends with No stored state - unable to handle response. If the app gets started from the launcher, the authorazation flow completes normally and the result is delivered to the calling activity. Based on the research from @SPodjasek I think that the same flag is causing my problem too. I would appreciate any ideas how this can be worked around. Thank you in advance.