-
Notifications
You must be signed in to change notification settings - Fork 8
Fail gracefully when the default browser does not support custom tabs #1039
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
Conversation
…rowser is launched
…and simplify intent management
| /** | ||
| * Given [AuthenticationActivity] with a browser that supports Custom Tabs | ||
| * When [AuthenticationActivity] starts | ||
| * Then an intent should be fired with ACTION_VIEW, simulating a Custom Tabs launch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused - looking at processAuthenticationFlow(), when an intent is handled with ACTION_VIEW, that means we are getting the redirect from Custom Tabs. It doesn't mean we are launching Custom Tabs. If this test is meant to check that the custom tabsl intent is being launched, shouldn't we be checking for something like the intent being of type CustomTabsIntent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the following intents that are captured by the test,
AuthenticationActivityTest: Intent: action=null data=null extras=KEY_INTENT_EXTRA_URL
AuthenticationActivityTest: Intent: action=android.intent.action.VIEW data=https://example.com/auth extras=android.support.customtabs.extra.SESSION, android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS, androidx.browser.customtabs.extra.SHARE_STATE, com.android.browser.headers
As you can see, the first one is the one that the test uses to launch AuthenticationActivity, simulating the OAuthUserSignInContract.
And the second one is the CustomTabs intent that AuthenticationActivity launches.
Since we are not signing in, we do not see the "redirect" intent that we would get from the browser
If this test is meant to check that the custom tabs intent is being launched, shouldn't we be checking for something like the intent being of type CustomTabsIntent?
I assume you mean some identifiable information that it will tell us that it is the CustomTabs Intent, I already check for android.support.customtabs.extra.SESSION inside the test but I could add more if you think is necessary
| scenario.close() | ||
| val result = scenario.result | ||
| assertThat(result.resultCode).isEqualTo(RESULT_CODE_SUCCESS) | ||
| assertThat(result.resultData?.getStringExtra("KEY_INTENT_EXTRA_RESPONSE_URI")).isEqualTo(redirectUri) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this test working? In other words, what closes the Custom Tabs and redirects into AuthenticationActivity with RESULT_CODE_SUCCESS?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test does not open CustomTabs, it only verifies that AuthenticationActivity handles a "redirect" URI successfully.
It does this by launching the activity with an intent that has a redirect_uri as data, simulating a browser redirecting back to the activity.
When AuthenticationActivity processes a redirect URI successfully, it calls setResult(RESULT_CODE_SUCCESS, newIntent) here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, makes sense. Would the following doc make that clearer?
/**
* Given [AuthenticationActivity] receives an intent containing a valid redirect URI
* When the activity processes the redirect intent
* Then the activity finishes with RESULT_CODE_SUCCESS and includes the redirect URI in the result data
* @since 300.0.0
*/
| * an appropriate error message. | ||
| * @since 300.0.0 | ||
| */ | ||
| private fun processAuthenticationFlow() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick - naming suggestion:
| private fun processAuthenticationFlow() { | |
| private fun promptWithCustomTabs() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'd like to give it a more generic name since this likely will be the method to launch external browser or Auth Tabs (when we support it).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought we won't support external browser? And when we move to Auth Tabs, we can rename the function to promptWithAuthTab at that point.
...authentication/src/main/java/com/arcgismaps/toolkit/authentication/AuthenticationActivity.kt
Outdated
Show resolved
Hide resolved
| val authEndpoint = getStringExtra(KEY_INTENT_EXTRA_URL) | ||
| when { | ||
| // This signals that we have been redirected back to the app from the browser | ||
| action == Intent.ACTION_VIEW && data != null -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this case only happening when we get an intent in onNewIntent? It seems we ar enow conflating tow different cases in processAuthenticationFlow:
- launching custom tabs
- handling the redirect from custom tabs
I think this function here should only handle the first case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When testing different browsers sign-in, i found that some browsers might redirect with different flags and might come through onCreate instead of onNewIntent.
This is just safe guarding this possible outcome.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is diverging quite a bit from the current implementation. At the minimum we will need test cases for the scenarios you mention. Lets discuss...
|
@gunt0001 Please have another look |
| * @since 300.0.0 | ||
| */ | ||
| @RunWith(AndroidJUnit4::class) | ||
| class AuthenticationActivityExternalBrowserTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we name it simply?
| class AuthenticationActivityExternalBrowserTest { | |
| class AuthenticationActivityTest { |
| /** | ||
| * Given [AuthenticationActivity] is launched with a default browser that does not support Custom Tabs | ||
| * When [AuthenticationActivity] starts | ||
| * Then the activity finishes with RESULT_CODE_CANCELED and includes an exception message in the result data | ||
| * @since 300.0.0 | ||
| */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would the following be a better description for this test?
| /** | |
| * Given [AuthenticationActivity] is launched with a default browser that does not support Custom Tabs | |
| * When [AuthenticationActivity] starts | |
| * Then the activity finishes with RESULT_CODE_CANCELED and includes an exception message in the result data | |
| * @since 300.0.0 | |
| */ | |
| /** | |
| * Given a device with default browser that does not support Custom Tabs | |
| * When [AuthenticationActivity] receives an intent for OAuth sign-in | |
| * Then the activity finishes with RESULT_CODE_CANCELED and includes an exception message in the result data | |
| * @since 300.0.0 | |
| */ |
|
|
||
| // Launch the AuthenticationActivity with the redirect intent | ||
| val scenario = ActivityScenario.launchActivityForResult<AuthenticationActivity>(intent) | ||
| scenario.close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the doc of close, should we be using scenario.use { ... } here or alternatively ActivityScenarioRule? The same would apply to the other tests.
| scenario.close() | ||
| val result = scenario.result | ||
| assertThat(result.resultCode).isEqualTo(RESULT_CODE_SUCCESS) | ||
| assertThat(result.resultData?.getStringExtra("KEY_INTENT_EXTRA_RESPONSE_URI")).isEqualTo(redirectUri) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, makes sense. Would the following doc make that clearer?
/**
* Given [AuthenticationActivity] receives an intent containing a valid redirect URI
* When the activity processes the redirect intent
* Then the activity finishes with RESULT_CODE_SUCCESS and includes the redirect URI in the result data
* @since 300.0.0
*/
| * @since 300.0.0 | ||
| */ | ||
| @Test | ||
| fun returnsExceptionWhenNoBrowserAvailable() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be named?
| fun returnsExceptionWhenNoBrowserAvailable() { | |
| fun returnsExceptionWhenNoCustomTabsAvailable() { |
| * an appropriate error message. | ||
| * @since 300.0.0 | ||
| */ | ||
| private fun processAuthenticationFlow() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought we won't support external browser? And when we move to Auth Tabs, we can rename the function to promptWithAuthTab at that point.
| val authEndpoint = getStringExtra(KEY_INTENT_EXTRA_URL) | ||
| when { | ||
| // This signals that we have been redirected back to the app from the browser | ||
| action == Intent.ACTION_VIEW && data != null -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is diverging quite a bit from the current implementation. At the minimum we will need test cases for the scenarios you mention. Lets discuss...
|
a simpler implementation is in PR #1044 since we want to make gradual changes |
Related to issue: https://devtopia.esri.com/runtime/kotlin/issues/6659
Summary of changes:
AuthenticationActivitybehaviorAuthenticationActivityto process incoming redirects in throughonCreate.OAuthUserSignInContractto handle errors coming back in the ResultPre-merge Checklist