diff --git a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt index d1980af03..1b0da3f63 100644 --- a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt +++ b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt @@ -56,4 +56,9 @@ object FeatureFlags { * Enabled showing site permission indicators in the toolbars. */ val permissionIndicatorsToolbar = Config.channel.isNightlyOrDebug + + /** + * Enables experimental WebAuthn support. This implementation should never reach release! + */ + val webAuthFeature = Config.channel.isNightlyOrDebug } diff --git a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt index 271a0a341..b637d664f 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt @@ -173,6 +173,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, private var fullScreenMediaSessionFeature = ViewBoundFeatureWrapper() private val searchFeature = ViewBoundFeatureWrapper() + private val webAuthnFeature = ViewBoundFeatureWrapper() private var pipFeature: PictureInPictureFeature? = null var customTabSessionId: String? = null @@ -639,6 +640,17 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, view = view ) + if (FeatureFlags.webAuthFeature) { + webAuthnFeature.set( + feature = WebAuthnFeature( + engine = requireComponents.core.engine, + activity = requireActivity() + ), + owner = this, + view = view + ) + } + context.settings().setSitePermissionSettingListener(viewLifecycleOwner) { // If the user connects to WIFI while on the BrowserFragment, this will update the // SitePermissionsRules (specifically autoplay) accordingly @@ -1024,7 +1036,10 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, * Forwards activity results to the prompt feature. */ final override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - promptsFeature.withFeature { it.onActivityResult(requestCode, resultCode, data) } + listOf( + promptsFeature, + webAuthnFeature + ).any { it.onActivityResult(requestCode, resultCode, data) } } /** diff --git a/app/src/main/java/org/mozilla/fenix/browser/WebAuthnFeature.kt b/app/src/main/java/org/mozilla/fenix/browser/WebAuthnFeature.kt new file mode 100644 index 000000000..269ca883c --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/browser/WebAuthnFeature.kt @@ -0,0 +1,61 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.browser + +import android.app.Activity +import android.content.Intent +import android.content.IntentSender +import mozilla.components.concept.engine.Engine +import mozilla.components.concept.engine.activity.ActivityDelegate +import mozilla.components.support.base.feature.ActivityResultHandler +import mozilla.components.support.base.feature.LifecycleAwareFeature +import mozilla.components.support.base.log.logger.Logger + +/** + * This implementation of the WebAuthnFeature is only for testing in a nightly signed build. + * + * ⚠️ This should always be behind the [FeatureFlags.webAuthFeature] nightly flag. + */ +class WebAuthnFeature( + private val engine: Engine, + private val activity: Activity +) : LifecycleAwareFeature, ActivityResultHandler { + val logger = Logger("WebAuthnFeature") + var requestCode = ACTIVITY_REQUEST_CODE + var resultCallback: ((Intent?) -> Unit)? = null + private val delegate = object : ActivityDelegate { + override fun startIntentSenderForResult(intent: IntentSender, onResult: (Intent?) -> Unit) { + val code = requestCode++ + logger.info("Received activity delegate request with code: $code intent: $intent") + activity.startIntentSenderForResult(intent, code, null, 0, 0, 0) + resultCallback = onResult + } + } + + override fun start() { + logger.info("Feature started.") + engine.registerActivityDelegate(delegate) + } + + override fun stop() { + logger.info("Feature stopped.") + engine.unregisterActivityDelegate() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { + logger.info("Received activity result with code: $requestCode\ndata: $data") + if (this.requestCode == requestCode) { + logger.info("Invoking callback!") + resultCallback?.invoke(data) + return true + } + + return false + } + + companion object { + const val ACTIVITY_REQUEST_CODE = 1337 + } +}