Skip to content

Commit 8c4c091

Browse files
committed
feat(android): expose allowedBrowsers option for web authentication
Adds an allowedBrowsers option to NativeAuthorizeOptions that restricts which browsers can handle the web authentication flow on Android. This works around a known issue with Firefox where App Link redirects are not correctly handled, causing login flows to fail. The underlying Auth0.Android SDK supports browser filtering via BrowserPicker and CustomTabsOptions. This change exposes that capability through the React Native bridge. When set, the behaviour is: - Default browser in the list → use it - Default browser not in the list, another allowed browser installed → use that - No allowed browser installed → a0.browser_not_available error This brings parity with the auth0-flutter SDK which already exposes this option (see auth0/auth0-flutter#392). Ref: https://bugzilla.mozilla.org/show_bug.cgi?id=1976809
1 parent aa82e3b commit 8c4c091

5 files changed

Lines changed: 45 additions & 4 deletions

File tree

android/src/main/java/com/auth0/react/A0Auth0Module.kt

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ import com.auth0.android.authentication.storage.SecureCredentialsManager
1313
import com.auth0.android.authentication.storage.SharedPreferencesStorage
1414
import com.auth0.android.dpop.DPoP
1515
import com.auth0.android.dpop.DPoPException
16+
import com.auth0.android.provider.BrowserPicker
17+
import com.auth0.android.provider.CustomTabsOptions
1618
import com.auth0.android.provider.WebAuthProvider
1719
import com.auth0.android.result.Credentials
1820
import com.facebook.react.bridge.ActivityEventListener
1921
import com.facebook.react.bridge.Promise
2022
import com.facebook.react.bridge.ReactApplicationContext
2123
import com.facebook.react.bridge.ReactMethod
24+
import com.facebook.react.bridge.ReadableArray
2225
import com.facebook.react.bridge.ReadableMap
2326
import com.facebook.react.bridge.UiThreadUtil
2427
import com.facebook.react.bridge.WritableNativeMap
@@ -111,22 +114,23 @@ class A0Auth0Module(private val reactContext: ReactApplicationContext) : A0Auth0
111114
ephemeralSession: Boolean?,
112115
safariViewControllerPresentationStyle: Double?,
113116
additionalParameters: ReadableMap?,
117+
allowedBrowserPackages: ReadableArray?,
114118
promise: Promise
115119
) {
116120
if(this.useDPoP) {
117121
WebAuthProvider.useDPoP(reactContext)
118122
}
119123
webAuthPromise = promise
120124
val cleanedParameters = mutableMapOf<String, String>()
121-
125+
122126
additionalParameters?.let { params ->
123127
params.toHashMap().forEach { (key, value) ->
124128
value?.let { cleanedParameters[key] = it.toString() }
125129
}
126130
}
127131

128132
val builder = WebAuthProvider.login(auth0!!).withScheme(scheme)
129-
133+
130134
builder.apply {
131135
state?.let { withState(it) }
132136
nonce?.let { withNonce(it) }
@@ -138,6 +142,17 @@ class A0Auth0Module(private val reactContext: ReactApplicationContext) : A0Auth0
138142
invitationUrl?.let { withInvitationUrl(it) }
139143
leeway?.let { if (it.toInt() != 0) withIdTokenVerificationLeeway(it.toInt()) }
140144
redirectUri?.let { withRedirectUri(it) }
145+
allowedBrowserPackages?.let { packages ->
146+
val packageList = (0 until packages.size()).mapNotNull { packages.getString(it) }
147+
val browserPicker = BrowserPicker.newBuilder()
148+
.withAllowedPackages(packageList)
149+
.build()
150+
withCustomTabsOptions(
151+
CustomTabsOptions.newBuilder()
152+
.withBrowserPicker(browserPicker)
153+
.build()
154+
)
155+
}
141156
}
142157

143158
builder.withParameters(cleanedParameters)

android/src/main/oldarch/com/auth0/react/A0Auth0Spec.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.facebook.react.bridge.ReactApplicationContext
55
import com.facebook.react.bridge.ReactContextBaseJavaModule
66
import com.facebook.react.bridge.Promise
77
import com.facebook.react.bridge.ReactMethod
8+
import com.facebook.react.bridge.ReadableArray
89
import com.facebook.react.bridge.ReadableMap
910

1011
abstract class A0Auth0Spec(context: ReactApplicationContext) : ReactContextBaseJavaModule(context) {
@@ -83,6 +84,7 @@ abstract class A0Auth0Spec(context: ReactApplicationContext) : ReactContextBaseJ
8384
ephemeralSession: Boolean?,
8485
safariViewControllerPresentationStyle: Double?,
8586
additionalParameters: ReadableMap?,
87+
allowedBrowserPackages: ReadableArray?,
8688
promise: Promise
8789
)
8890

src/platforms/native/bridge/NativeBridgeManager.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ export class NativeBridgeManager implements INativeBridge {
104104
options.ephemeralSession ?? false,
105105
presentationStyle ?? 99, // Since we can't pass null to the native layer, and we need a value to represent this parameter is not set, we are using 99.
106106
// //The native layer will check for this and ignore if the value is 99
107-
parameters.additionalParameters ?? {}
107+
parameters.additionalParameters ?? {},
108+
options.allowedBrowsers
108109
);
109110
return new CredentialsModel(credential);
110111
}

src/specs/NativeA0Auth0.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ export interface Spec extends TurboModule {
9393
leeway: Int32 | undefined,
9494
ephemeralSession: boolean | undefined,
9595
safariViewControllerPresentationStyle: Int32 | undefined,
96-
additionalParameters: { [key: string]: string } | undefined
96+
additionalParameters: { [key: string]: string } | undefined,
97+
allowedBrowsers: string[] | undefined
9798
): Promise<Credentials>;
9899

99100
/**

src/types/platform-specific.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,28 @@ export interface NativeAuthorizeOptions {
144144
presentationStyle?: SafariViewControllerPresentationStyle;
145145
}
146146
| boolean;
147+
/**
148+
* **Android only:** List of browser package names allowed to handle the web authentication flow.
149+
* When set, only browsers whose package names appear in this list will be used. This is useful
150+
* for excluding browsers that do not correctly handle App Link redirects (e.g. Firefox).
151+
*
152+
* - When the user's default browser is in the list, it is used.
153+
* - When the user's default browser is not in the list but another allowed browser is installed, that browser is used instead.
154+
* - When no allowed browser is installed, an `a0.browser_not_available` error is returned.
155+
*
156+
* @example
157+
* ```typescript
158+
* await authorize({}, {
159+
* allowedBrowsers: [
160+
* 'com.android.chrome',
161+
* 'com.chrome.beta',
162+
* 'com.microsoft.emmx', // Edge
163+
* 'com.brave.browser',
164+
* ]
165+
* });
166+
* ```
167+
*/
168+
allowedBrowsers?: string[];
147169
}
148170

149171
/**

0 commit comments

Comments
 (0)