diff --git a/README.md b/README.md
index 1c283f44..b2688e62 100644
--- a/README.md
+++ b/README.md
@@ -543,12 +543,12 @@ provided, such as:
- [Browsers](https://github.com/openid/AppAuth-Android/blob/master/library/java/net/openid/appauth/browser/Browsers.java):
contains a set of constants for the official package names and signatures
- of Chrome, Firefox and Samsung SBrowser.
+ of Chrome, Firefox, Microsoft Edge and Samsung SBrowser.
- [VersionedBrowserMatcher](https://github.com/openid/AppAuth-Android/blob/master/library/java/net/openid/appauth/browser/VersionedBrowserMatcher.java):
will match a browser if it has a matching package name and signature, and
a version number within a defined
[VersionRange](https://github.com/openid/AppAuth-Android/blob/master/library/java/net/openid/appauth/browser/VersionRange.java). This class also provides some static instances for matching
- Chrome, Firefox and Samsung SBrowser.
+ Chrome, Firefox, Microsoft Edge and Samsung SBrowser.
- [BrowserAllowList](https://github.com/openid/AppAuth-Android/blob/master/library/java/net/openid/appauth/browser/BrowserAllowList.java):
takes a list of BrowserMatcher instances, and will match a browser if any
of these child BrowserMatcher instances signals a match.
@@ -587,6 +587,40 @@ AuthorizationService authService =
new AuthorizationService(context, appAuthConfig);
```
+### Excluding Microsoft Edge to avoid redirect interception
+
+Microsoft Edge on Android may intercept the authorization redirect and show
+a "Stay in Microsoft Edge" prompt instead of seamlessly returning control to
+your app ([#1151](https://github.com/openid/AppAuth-Android/issues/1151)).
+This can leave users stuck on the login page if they tap the primary button.
+
+To work around this, you can exclude Edge from the authorization flow using
+a `BrowserDenyList`:
+
+```java
+AppAuthConfiguration appAuthConfig = new AppAuthConfiguration.Builder()
+ .setBrowserMatcher(new BrowserDenyList(
+ VersionedBrowserMatcher.EDGE_CUSTOM_TAB,
+ VersionedBrowserMatcher.EDGE_BROWSER))
+ .build();
+AuthorizationService authService =
+ new AuthorizationService(context, appAuthConfig);
+```
+
+Alternatively, you can use a `BrowserAllowList` to explicitly permit only
+browsers known to handle redirects correctly:
+
+```java
+AppAuthConfiguration appAuthConfig = new AppAuthConfiguration.Builder()
+ .setBrowserMatcher(new BrowserAllowList(
+ VersionedBrowserMatcher.CHROME_CUSTOM_TAB,
+ VersionedBrowserMatcher.FIREFOX_CUSTOM_TAB,
+ VersionedBrowserMatcher.SAMSUNG_CUSTOM_TAB))
+ .build();
+AuthorizationService authService =
+ new AuthorizationService(context, appAuthConfig);
+```
+
### Customizing the connection builder for HTTP requests
It can be desirable to customize how HTTP connections are made when performing
diff --git a/edgetest/AndroidManifest.xml b/edgetest/AndroidManifest.xml
new file mode 100644
index 00000000..0b1e8333
--- /dev/null
+++ b/edgetest/AndroidManifest.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/edgetest/build.gradle b/edgetest/build.gradle
new file mode 100644
index 00000000..398f1676
--- /dev/null
+++ b/edgetest/build.gradle
@@ -0,0 +1,39 @@
+apply plugin: 'com.android.application'
+apply from: '../config/android-common.gradle'
+apply from: '../config/keystore.gradle'
+
+android {
+ namespace 'net.openid.edgetest'
+ defaultConfig {
+ applicationId 'net.openid.edgetest'
+ project.archivesBaseName = 'appauth-edgetest'
+
+ manifestPlaceholders = [
+ 'appAuthRedirectScheme': 'net.openid.edgetest'
+ ]
+ }
+
+ signingConfigs {
+ debugAndRelease {
+ storeFile file("${rootDir}/appauth.keystore")
+ storePassword "appauth"
+ keyAlias "appauth"
+ keyPassword "appauth"
+ }
+ }
+
+ buildTypes {
+ debug {
+ signingConfig signingConfigs.debugAndRelease
+ }
+ release {
+ signingConfig signingConfigs.debugAndRelease
+ }
+ }
+}
+
+dependencies {
+ implementation project(':library')
+ implementation "androidx.appcompat:appcompat:${project.androidXVersions.appcompat}"
+ implementation "androidx.annotation:annotation:${project.androidXVersions.annotation}"
+}
diff --git a/edgetest/java/net/openid/edgetest/BrowserTestActivity.java b/edgetest/java/net/openid/edgetest/BrowserTestActivity.java
new file mode 100644
index 00000000..b887cdbe
--- /dev/null
+++ b/edgetest/java/net/openid/edgetest/BrowserTestActivity.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2024 The AppAuth for Android Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.openid.edgetest;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.os.Bundle;
+import android.util.Base64;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import net.openid.appauth.browser.BrowserDenyList;
+import net.openid.appauth.browser.BrowserDescriptor;
+import net.openid.appauth.browser.BrowserSelector;
+import net.openid.appauth.browser.Browsers;
+import net.openid.appauth.browser.VersionedBrowserMatcher;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+
+/**
+ * Test activity for verifying the Edge browser deny list workaround.
+ *
+ *
This app performs three functions:
+ *
+ *
Lists all installed browsers with their package names, versions, custom tab support,
+ * and SHA-512 signature hashes (in the format used by AppAuth's BrowserDescriptor).
+ *
Tests the BrowserDenyList with Edge exclusion and reports whether Edge is correctly
+ * filtered out of the browser selection.
+ *
Extracts the exact Edge signature hash that should be used in Browsers.Edge.SIGNATURE_HASH
+ * for production use.
+ *
+ *
+ *
To use: install this app on a device that has Microsoft Edge installed, then tap each button.
+ */
+public class BrowserTestActivity extends AppCompatActivity {
+
+ private static final String EDGE_PACKAGE = "com.microsoft.emmx";
+
+ private TextView mOutput;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_browser_test);
+
+ mOutput = findViewById(R.id.output);
+
+ Button btnListBrowsers = findViewById(R.id.btn_list_browsers);
+ btnListBrowsers.setOnClickListener(v -> listAllBrowsers());
+
+ Button btnTestDenyList = findViewById(R.id.btn_test_deny_list);
+ btnTestDenyList.setOnClickListener(v -> testEdgeDenyList());
+
+ Button btnExtractEdgeHash = findViewById(R.id.btn_extract_edge_hash);
+ btnExtractEdgeHash.setOnClickListener(v -> extractEdgeSignatureHash());
+ }
+
+ /**
+ * Lists all browsers detected by AppAuth's BrowserSelector, showing their package names,
+ * versions, custom tab support, and signature hashes.
+ */
+ private void listAllBrowsers() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("=== ALL DETECTED BROWSERS ===\n\n");
+
+ List browsers = BrowserSelector.getAllBrowsers(this);
+
+ if (browsers.isEmpty()) {
+ sb.append("No browsers detected!\n");
+ } else {
+ sb.append("Found ").append(browsers.size()).append(" browser entries:\n\n");
+
+ for (int i = 0; i < browsers.size(); i++) {
+ BrowserDescriptor browser = browsers.get(i);
+ sb.append("--- Browser #").append(i + 1).append(" ---\n");
+ sb.append("Package: ").append(browser.packageName).append("\n");
+ sb.append("Version: ").append(browser.version).append("\n");
+ sb.append("Custom Tab: ").append(browser.useCustomTab).append("\n");
+ sb.append("Signature Hashes:\n");
+ for (String hash : browser.signatureHashes) {
+ sb.append(" ").append(hash).append("\n");
+ }
+
+ // Check if this is Edge
+ if (EDGE_PACKAGE.equals(browser.packageName)) {
+ sb.append(" >>> THIS IS MICROSOFT EDGE <<<\n");
+ }
+ sb.append("\n");
+ }
+ }
+
+ mOutput.setText(sb.toString());
+ }
+
+ /**
+ * Tests the BrowserDenyList with Edge exclusion.
+ * Shows which browser would be selected with and without the deny list.
+ */
+ private void testEdgeDenyList() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("=== EDGE DENY LIST TEST ===\n\n");
+
+ // Get all browsers first
+ List allBrowsers = BrowserSelector.getAllBrowsers(this);
+
+ // Check if Edge is in the list
+ boolean edgeFound = false;
+ for (BrowserDescriptor browser : allBrowsers) {
+ if (EDGE_PACKAGE.equals(browser.packageName)) {
+ edgeFound = true;
+ break;
+ }
+ }
+
+ sb.append("Edge installed: ").append(edgeFound ? "YES" : "NO").append("\n\n");
+
+ // Test without deny list (default behavior)
+ BrowserDescriptor defaultSelection = BrowserSelector.select(
+ this, descriptor -> true);
+ sb.append("Default selection (no filter):\n");
+ if (defaultSelection != null) {
+ sb.append(" Package: ").append(defaultSelection.packageName).append("\n");
+ sb.append(" Custom Tab: ").append(defaultSelection.useCustomTab).append("\n");
+ sb.append(" Is Edge: ").append(
+ EDGE_PACKAGE.equals(defaultSelection.packageName)).append("\n");
+ } else {
+ sb.append(" (none)\n");
+ }
+ sb.append("\n");
+
+ // Test with Edge deny list
+ BrowserDenyList denyList = new BrowserDenyList(
+ VersionedBrowserMatcher.EDGE_CUSTOM_TAB,
+ VersionedBrowserMatcher.EDGE_BROWSER);
+
+ BrowserDescriptor filteredSelection = BrowserSelector.select(this, denyList);
+ sb.append("Selection with Edge DenyList:\n");
+ if (filteredSelection != null) {
+ sb.append(" Package: ").append(filteredSelection.packageName).append("\n");
+ sb.append(" Custom Tab: ").append(filteredSelection.useCustomTab).append("\n");
+ sb.append(" Is Edge: ").append(
+ EDGE_PACKAGE.equals(filteredSelection.packageName)).append("\n");
+ } else {
+ sb.append(" (none - no other browser available)\n");
+ }
+ sb.append("\n");
+
+ // Verify the deny list works correctly
+ sb.append("=== VERIFICATION ===\n");
+ if (!edgeFound) {
+ sb.append("SKIP: Edge is not installed on this device.\n");
+ sb.append("Install Microsoft Edge to test the deny list.\n");
+ } else if (filteredSelection != null
+ && !EDGE_PACKAGE.equals(filteredSelection.packageName)) {
+ sb.append("PASS: Edge was successfully excluded!\n");
+ sb.append("Alternative browser selected: ")
+ .append(filteredSelection.packageName).append("\n");
+ } else if (filteredSelection == null) {
+ sb.append("PASS: Edge was excluded (no other browser available).\n");
+ } else {
+ sb.append("FAIL: Edge was NOT excluded by the deny list.\n");
+ sb.append("This likely means the signature hash doesn't match.\n");
+ sb.append("Run 'Extract Edge Signature Hash' to get the correct value.\n");
+ }
+
+ mOutput.setText(sb.toString());
+ }
+
+ /**
+ * Extracts the exact SHA-512 signature hash for Microsoft Edge.
+ * This is the value that should be used in Browsers.Edge.SIGNATURE_HASH.
+ */
+ private void extractEdgeSignatureHash() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("=== EDGE SIGNATURE HASH EXTRACTION ===\n\n");
+
+ try {
+ PackageInfo packageInfo = getPackageManager().getPackageInfo(
+ EDGE_PACKAGE, PackageManager.GET_SIGNATURES);
+
+ sb.append("Edge package found!\n");
+ sb.append("Package: ").append(packageInfo.packageName).append("\n");
+ sb.append("Version: ").append(packageInfo.versionName).append("\n");
+ sb.append("Version Code: ").append(packageInfo.versionCode).append("\n\n");
+
+ if (packageInfo.signatures != null && packageInfo.signatures.length > 0) {
+ sb.append("Number of signatures: ")
+ .append(packageInfo.signatures.length).append("\n\n");
+
+ for (int i = 0; i < packageInfo.signatures.length; i++) {
+ Signature sig = packageInfo.signatures[i];
+ String hash = generateSignatureHash(sig);
+
+ sb.append("Signature #").append(i + 1).append(":\n");
+ sb.append(" SHA-512 (Base64 URL-safe):\n");
+ sb.append(" ").append(hash).append("\n\n");
+
+ // Check if it matches our current constant
+ sb.append(" Matches Browsers.Edge.SIGNATURE_HASH: ");
+ sb.append(Browsers.Edge.SIGNATURE_HASH.equals(hash) ? "YES" : "NO");
+ sb.append("\n\n");
+
+ if (!Browsers.Edge.SIGNATURE_HASH.equals(hash)) {
+ sb.append(" *** UPDATE NEEDED ***\n");
+ sb.append(" Replace the SIGNATURE_HASH in Browsers.Edge with:\n");
+ sb.append(" \"").append(hash).append("\"\n\n");
+ }
+ }
+ } else {
+ sb.append("ERROR: No signatures found in package info.\n");
+ sb.append("This shouldn't happen for a properly signed app.\n");
+ }
+
+ } catch (PackageManager.NameNotFoundException e) {
+ sb.append("Microsoft Edge is NOT installed on this device.\n");
+ sb.append("Package '").append(EDGE_PACKAGE).append("' not found.\n\n");
+ sb.append("Please install Microsoft Edge from the Play Store\n");
+ sb.append("and run this test again.\n");
+ }
+
+ // Also show the current constant value for reference
+ sb.append("\n=== CURRENT CONSTANT VALUE ===\n");
+ sb.append("Browsers.Edge.SIGNATURE_HASH:\n");
+ sb.append(" ").append(Browsers.Edge.SIGNATURE_HASH).append("\n");
+
+ mOutput.setText(sb.toString());
+ }
+
+ /**
+ * Generates a SHA-512 hash, Base64 url-safe encoded, from a Signature.
+ * This replicates the logic in BrowserDescriptor.generateSignatureHash().
+ */
+ private static String generateSignatureHash(Signature signature) {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-512");
+ byte[] hashBytes = digest.digest(signature.toByteArray());
+ return Base64.encodeToString(hashBytes, Base64.URL_SAFE | Base64.NO_WRAP);
+ } catch (NoSuchAlgorithmException e) {
+ return "ERROR: SHA-512 not available";
+ }
+ }
+}
diff --git a/edgetest/res/layout/activity_browser_test.xml b/edgetest/res/layout/activity_browser_test.xml
new file mode 100644
index 00000000..08282075
--- /dev/null
+++ b/edgetest/res/layout/activity_browser_test.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/edgetest/res/values/strings.xml b/edgetest/res/values/strings.xml
new file mode 100644
index 00000000..163ac83a
--- /dev/null
+++ b/edgetest/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ AppAuth Edge Test
+
diff --git a/library/java/net/openid/appauth/browser/Browsers.java b/library/java/net/openid/appauth/browser/Browsers.java
index 3f70101e..155e9103 100644
--- a/library/java/net/openid/appauth/browser/Browsers.java
+++ b/library/java/net/openid/appauth/browser/Browsers.java
@@ -124,6 +124,59 @@ private Firefox() {
}
}
+ /**
+ * Constants related to
+ * [Microsoft Edge](https://play.google.com/store/apps/details?id=com.microsoft.emmx).
+ */
+ public static final class Edge {
+
+ /**
+ * The package name for Microsoft Edge.
+ */
+ public static final String PACKAGE_NAME = "com.microsoft.emmx";
+
+ /**
+ * The SHA-512 hash (Base64 url-safe encoded) of the public key for Microsoft Edge.
+ * This value should be verified against the installed version on the target device,
+ * as it may change with Play App Signing updates.
+ */
+ public static final String SIGNATURE_HASH =
+ "Ivy-Rk6ztai_IudfbyUrSHugzRqAtHWslFvHT0PTvLMsEKLUIgv7ZZbVxygWy_M5m"
+ + "OPpfjZrd3vOx3t-cA6fVQ==";
+
+ /**
+ * The set of signature hashes for Microsoft Edge.
+ */
+ public static final Set SIGNATURE_SET =
+ Collections.singleton(SIGNATURE_HASH);
+
+ /**
+ * The version in which Custom Tabs were introduced in Microsoft Edge.
+ */
+ public static final DelimitedVersion MINIMUM_VERSION_FOR_CUSTOM_TAB =
+ DelimitedVersion.parse("45");
+
+ /**
+ * Creates a browser descriptor for the specified version of Edge, when used as a
+ * standalone browser.
+ */
+ public static BrowserDescriptor standaloneBrowser(@NonNull String version) {
+ return new BrowserDescriptor(PACKAGE_NAME, SIGNATURE_SET, version, false);
+ }
+
+ /**
+ * Creates a browser descriptor for the specified version of Edge, when used as
+ * a custom tab.
+ */
+ public static BrowserDescriptor customTab(@NonNull String version) {
+ return new BrowserDescriptor(PACKAGE_NAME, SIGNATURE_SET, version, true);
+ }
+
+ private Edge() {
+ // no need to construct this class
+ }
+ }
+
/**
* Constants related to
* [SBrowser](https://play.google.com/store/apps/details?id=com.sec.android.app.sbrowser),
diff --git a/library/java/net/openid/appauth/browser/VersionedBrowserMatcher.java b/library/java/net/openid/appauth/browser/VersionedBrowserMatcher.java
index f4b09ffe..b3b2aa70 100644
--- a/library/java/net/openid/appauth/browser/VersionedBrowserMatcher.java
+++ b/library/java/net/openid/appauth/browser/VersionedBrowserMatcher.java
@@ -61,6 +61,24 @@ public class VersionedBrowserMatcher implements BrowserMatcher {
false,
VersionRange.ANY_VERSION);
+ /**
+ * Matches any version of Microsoft Edge for use as a custom tab.
+ */
+ public static final VersionedBrowserMatcher EDGE_CUSTOM_TAB = new VersionedBrowserMatcher(
+ Browsers.Edge.PACKAGE_NAME,
+ Browsers.Edge.SIGNATURE_SET,
+ true,
+ VersionRange.atLeast(Browsers.Edge.MINIMUM_VERSION_FOR_CUSTOM_TAB));
+
+ /**
+ * Matches any version of Microsoft Edge for use as a standalone browser.
+ */
+ public static final VersionedBrowserMatcher EDGE_BROWSER = new VersionedBrowserMatcher(
+ Browsers.Edge.PACKAGE_NAME,
+ Browsers.Edge.SIGNATURE_SET,
+ false,
+ VersionRange.ANY_VERSION);
+
/**
* Matches any version of SBrowser for use as a standalone browser.
*/
diff --git a/library/javatests/net/openid/appauth/browser/EdgeBrowserTest.java b/library/javatests/net/openid/appauth/browser/EdgeBrowserTest.java
new file mode 100644
index 00000000..8be5fd81
--- /dev/null
+++ b/library/javatests/net/openid/appauth/browser/EdgeBrowserTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2016 The AppAuth for Android Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.openid.appauth.browser;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Collections;
+import java.util.HashSet;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = 16)
+public class EdgeBrowserTest {
+
+ @Test
+ public void testEdgePackageName() {
+ assertThat(Browsers.Edge.PACKAGE_NAME).isEqualTo("com.microsoft.emmx");
+ }
+
+ @Test
+ public void testEdgeSignatureSetNotEmpty() {
+ assertThat(Browsers.Edge.SIGNATURE_SET).isNotEmpty();
+ assertThat(Browsers.Edge.SIGNATURE_SET).hasSize(1);
+ }
+
+ @Test
+ public void testEdgeMinimumVersionForCustomTab() {
+ assertThat(Browsers.Edge.MINIMUM_VERSION_FOR_CUSTOM_TAB)
+ .isEqualTo(DelimitedVersion.parse("45"));
+ }
+
+ @Test
+ public void testEdgeCustomTabDescriptor() {
+ BrowserDescriptor descriptor = Browsers.Edge.customTab("50");
+ assertThat(descriptor.packageName).isEqualTo(Browsers.Edge.PACKAGE_NAME);
+ assertThat(descriptor.signatureHashes).isEqualTo(Browsers.Edge.SIGNATURE_SET);
+ assertThat(descriptor.version).isEqualTo("50");
+ assertThat(descriptor.useCustomTab).isTrue();
+ }
+
+ @Test
+ public void testEdgeStandaloneBrowserDescriptor() {
+ BrowserDescriptor descriptor = Browsers.Edge.standaloneBrowser("50");
+ assertThat(descriptor.packageName).isEqualTo(Browsers.Edge.PACKAGE_NAME);
+ assertThat(descriptor.signatureHashes).isEqualTo(Browsers.Edge.SIGNATURE_SET);
+ assertThat(descriptor.version).isEqualTo("50");
+ assertThat(descriptor.useCustomTab).isFalse();
+ }
+
+ @Test
+ public void testEdgeCustomTabMatcher_matchesEdgeCustomTab() {
+ BrowserDescriptor edgeCustomTab = Browsers.Edge.customTab("50");
+ assertThat(VersionedBrowserMatcher.EDGE_CUSTOM_TAB.matches(edgeCustomTab)).isTrue();
+ }
+
+ @Test
+ public void testEdgeCustomTabMatcher_doesNotMatchOldVersion() {
+ BrowserDescriptor oldEdge = Browsers.Edge.customTab("44");
+ assertThat(VersionedBrowserMatcher.EDGE_CUSTOM_TAB.matches(oldEdge)).isFalse();
+ }
+
+ @Test
+ public void testEdgeCustomTabMatcher_doesNotMatchStandalone() {
+ BrowserDescriptor edgeStandalone = Browsers.Edge.standaloneBrowser("50");
+ assertThat(VersionedBrowserMatcher.EDGE_CUSTOM_TAB.matches(edgeStandalone)).isFalse();
+ }
+
+ @Test
+ public void testEdgeCustomTabMatcher_doesNotMatchChrome() {
+ BrowserDescriptor chromeCustomTab = Browsers.Chrome.customTab("50");
+ assertThat(VersionedBrowserMatcher.EDGE_CUSTOM_TAB.matches(chromeCustomTab)).isFalse();
+ }
+
+ @Test
+ public void testEdgeBrowserMatcher_matchesEdgeStandalone() {
+ BrowserDescriptor edgeStandalone = Browsers.Edge.standaloneBrowser("50");
+ assertThat(VersionedBrowserMatcher.EDGE_BROWSER.matches(edgeStandalone)).isTrue();
+ }
+
+ @Test
+ public void testEdgeBrowserMatcher_doesNotMatchEdgeCustomTab() {
+ BrowserDescriptor edgeCustomTab = Browsers.Edge.customTab("50");
+ assertThat(VersionedBrowserMatcher.EDGE_BROWSER.matches(edgeCustomTab)).isFalse();
+ }
+
+ @Test
+ public void testEdgeBrowserMatcher_doesNotMatchDifferentPackage() {
+ BrowserDescriptor chrome = Browsers.Chrome.standaloneBrowser("50");
+ assertThat(VersionedBrowserMatcher.EDGE_BROWSER.matches(chrome)).isFalse();
+ }
+
+ @Test
+ public void testEdgeBrowserMatcher_doesNotMatchDifferentSignature() {
+ BrowserDescriptor fakeEdge = new BrowserDescriptor(
+ Browsers.Edge.PACKAGE_NAME,
+ Collections.singleton("FAKE_SIGNATURE_HASH"),
+ "50",
+ false);
+ assertThat(VersionedBrowserMatcher.EDGE_BROWSER.matches(fakeEdge)).isFalse();
+ }
+
+ @Test
+ public void testDenyList_excludeEdgeCustomTab() {
+ BrowserDenyList denyList = new BrowserDenyList(
+ VersionedBrowserMatcher.EDGE_CUSTOM_TAB);
+
+ // Edge custom tab should be denied
+ assertThat(denyList.matches(Browsers.Edge.customTab("50"))).isFalse();
+
+ // Edge standalone should still be allowed
+ assertThat(denyList.matches(Browsers.Edge.standaloneBrowser("50"))).isTrue();
+
+ // Other browsers should not be affected
+ assertThat(denyList.matches(Browsers.Chrome.customTab("50"))).isTrue();
+ assertThat(denyList.matches(Browsers.Firefox.customTab("60"))).isTrue();
+ assertThat(denyList.matches(Browsers.SBrowser.customTab("5"))).isTrue();
+ }
+
+ @Test
+ public void testDenyList_excludeEdgeBoth() {
+ BrowserDenyList denyList = new BrowserDenyList(
+ VersionedBrowserMatcher.EDGE_CUSTOM_TAB,
+ VersionedBrowserMatcher.EDGE_BROWSER);
+
+ // Both Edge modes should be denied
+ assertThat(denyList.matches(Browsers.Edge.customTab("50"))).isFalse();
+ assertThat(denyList.matches(Browsers.Edge.standaloneBrowser("50"))).isFalse();
+
+ // Other browsers should not be affected
+ assertThat(denyList.matches(Browsers.Chrome.customTab("50"))).isTrue();
+ assertThat(denyList.matches(Browsers.Chrome.standaloneBrowser("50"))).isTrue();
+ assertThat(denyList.matches(Browsers.Firefox.standaloneBrowser("60"))).isTrue();
+ }
+
+ @Test
+ public void testAllowList_onlyChromeAndFirefox() {
+ // Simulates the recommended workaround: only allow Chrome and Firefox
+ BrowserAllowList allowList = new BrowserAllowList(
+ VersionedBrowserMatcher.CHROME_CUSTOM_TAB,
+ VersionedBrowserMatcher.FIREFOX_CUSTOM_TAB);
+
+ assertThat(allowList.matches(Browsers.Chrome.customTab("50"))).isTrue();
+ assertThat(allowList.matches(Browsers.Firefox.customTab("60"))).isTrue();
+ assertThat(allowList.matches(Browsers.Edge.customTab("50"))).isFalse();
+ assertThat(allowList.matches(Browsers.Edge.standaloneBrowser("50"))).isFalse();
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index 33069973..040f1eba 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app', ':library'
+include ':app', ':library', ':edgetest'