From 1017c1b136923974bc1326c58bc87dd02bb31c51 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Thu, 14 Jan 2021 22:34:29 +0400 Subject: [PATCH] [fenix] Add experimental WebAuthn support for Nightly only When testing out WebAuthn support with the privileged API, we need our app to be signed by an allowed signing key. We're seeing our tests fail with this error when testing locally: ``` [FidoApiImpl] updateTransaction is called for stop [FidoApiImpl] finishSecurityKeyRequestController should not be called when SecurityKeyRequestController is null. ``` Our theory is that if we try this code on our signed APK, we should see it work. --- .../java/org/mozilla/fenix/FeatureFlags.kt | 5 ++ .../fenix/browser/BaseBrowserFragment.kt | 17 +++++- .../mozilla/fenix/browser/WebAuthnFeature.kt | 61 +++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/mozilla/fenix/browser/WebAuthnFeature.kt diff --git a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt index d1980af030..1b0da3f637 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 271a0a3413..b637d664f8 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 0000000000..269ca883c8 --- /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 + } +}