Skip to content

Commit 09d87ca

Browse files
committed
Enable passkey support in browser for internal builds
1 parent 8880843 commit 09d87ca

File tree

5 files changed

+87
-0
lines changed

5 files changed

+87
-0
lines changed

app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ import com.duckduckgo.app.browser.viewstate.LoadingViewState
176176
import com.duckduckgo.app.browser.viewstate.OmnibarViewState
177177
import com.duckduckgo.app.browser.viewstate.PrivacyShieldViewState
178178
import com.duckduckgo.app.browser.viewstate.SavedSiteChangedViewState
179+
import com.duckduckgo.app.browser.webauthn.WebViewPasskeyInitializer
179180
import com.duckduckgo.app.browser.webshare.WebShareChooser
180181
import com.duckduckgo.app.browser.webview.WebContentDebugging
181182
import com.duckduckgo.app.browser.webview.WebViewBlobDownloadFeature
@@ -583,6 +584,9 @@ class BrowserTabFragment :
583584
@Inject
584585
lateinit var androidBrowserConfigFeature: AndroidBrowserConfigFeature
585586

587+
@Inject
588+
lateinit var passkeyInitializer: WebViewPasskeyInitializer
589+
586590
/**
587591
* We use this to monitor whether the user was seeing the in-context Email Protection signup prompt
588592
* This is needed because the activity stack will be cleared if an external link is opened in our browser
@@ -3111,6 +3115,10 @@ class BrowserTabFragment :
31113115
}
31123116

31133117
WebView.setWebContentsDebuggingEnabled(webContentDebugging.isEnabled())
3118+
3119+
lifecycleScope.launch(dispatchers.main()) {
3120+
webView?.let { passkeyInitializer.configurePasskeySupport(it) }
3121+
}
31143122
}
31153123

31163124
private fun screenLock(data: JsCallbackData) {

app/src/main/java/com/duckduckgo/app/browser/DuckDuckGoWebView.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,10 @@ class DuckDuckGoWebView : WebView, NestedScrollingChild3 {
412412
}
413413
}
414414

415+
fun isDestroyed(): Boolean {
416+
return isDestroyed
417+
}
418+
415419
@SuppressLint("RequiresFeature", "AddWebMessageListenerUsage")
416420
suspend fun safeAddWebMessageListener(
417421
webViewCapabilityChecker: WebViewCapabilityChecker,
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) 2025 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.app.browser.webauthn
18+
19+
import android.annotation.SuppressLint
20+
import androidx.webkit.WebSettingsCompat
21+
import androidx.webkit.WebSettingsCompat.WEB_AUTHENTICATION_SUPPORT_FOR_BROWSER
22+
import androidx.webkit.WebViewFeature
23+
import androidx.webkit.WebViewFeature.WEB_AUTHENTICATION
24+
import com.duckduckgo.app.browser.DuckDuckGoWebView
25+
import com.duckduckgo.autofill.api.AutofillFeature
26+
import com.duckduckgo.common.utils.DispatcherProvider
27+
import com.duckduckgo.di.scopes.AppScope
28+
import com.squareup.anvil.annotations.ContributesBinding
29+
import javax.inject.Inject
30+
import kotlinx.coroutines.withContext
31+
import logcat.logcat
32+
33+
interface WebViewPasskeyInitializer {
34+
suspend fun configurePasskeySupport(webView: DuckDuckGoWebView)
35+
}
36+
37+
@ContributesBinding(AppScope::class)
38+
class RealWebViewPasskeyInitializer @Inject constructor(
39+
private val autofillFeature: AutofillFeature,
40+
private val dispatchers: DispatcherProvider,
41+
) : WebViewPasskeyInitializer {
42+
43+
override suspend fun configurePasskeySupport(webView: DuckDuckGoWebView) {
44+
if (featureFlagEnabled() && webViewCapable()) {
45+
enablePasskeySupport(webView)
46+
}
47+
}
48+
49+
@SuppressLint("RequiresFeature")
50+
private suspend fun enablePasskeySupport(webView: DuckDuckGoWebView) {
51+
withContext(dispatchers.main()) {
52+
if (!webView.isDestroyed()) {
53+
WebSettingsCompat.setWebAuthenticationSupport(webView.settings, WEB_AUTHENTICATION_SUPPORT_FOR_BROWSER)
54+
logcat { "Autofill-passkey: WebView passkey support (WebAuthn) enabled" }
55+
}
56+
}
57+
}
58+
59+
private suspend fun featureFlagEnabled(): Boolean {
60+
return withContext(dispatchers.io()) {
61+
autofillFeature.passkeySupport().isEnabled()
62+
}
63+
}
64+
65+
private suspend fun webViewCapable(): Boolean {
66+
return withContext(dispatchers.main()) {
67+
WebViewFeature.isFeatureSupported(WEB_AUTHENTICATION)
68+
}
69+
}
70+
}

autofill/autofill-api/src/main/java/com/duckduckgo/autofill/api/AutofillFeature.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,7 @@ interface AutofillFeature {
154154

155155
@Toggle.DefaultValue(defaultValue = DefaultFeatureValue.TRUE)
156156
fun canShowImportOptionInAppSettings(): Toggle
157+
158+
@Toggle.DefaultValue(defaultValue = DefaultFeatureValue.INTERNAL)
159+
fun passkeySupport(): Toggle
157160
}

autofill/autofill-impl/src/main/AndroidManifest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
package="com.duckduckgo.autofill.impl">
44

5+
<uses-permission android:name="android.permission.CREDENTIAL_MANAGER_SET_ORIGIN" />
6+
57
<application>
68
<activity
79
android:name=".ui.credential.management.importpassword.ImportPasswordsActivity"

0 commit comments

Comments
 (0)