summaryrefslogtreecommitdiffstats
path: root/mobile/android/android-components/components/feature/webauthn
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/android-components/components/feature/webauthn')
-rw-r--r--mobile/android/android-components/components/feature/webauthn/README.md59
-rw-r--r--mobile/android/android-components/components/feature/webauthn/build.gradle36
-rw-r--r--mobile/android/android-components/components/feature/webauthn/proguard-rules.pro21
-rw-r--r--mobile/android/android-components/components/feature/webauthn/src/main/AndroidManifest.xml4
-rw-r--r--mobile/android/android-components/components/feature/webauthn/src/main/java/mozilla/components/feature/webauthn/WebAuthnFeature.kt66
-rw-r--r--mobile/android/android-components/components/feature/webauthn/src/test/java/mozilla/components/feature/webauthn/WebAuthnFeatureTest.kt92
-rw-r--r--mobile/android/android-components/components/feature/webauthn/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker2
-rw-r--r--mobile/android/android-components/components/feature/webauthn/src/test/resources/robolectric.properties1
8 files changed, 281 insertions, 0 deletions
diff --git a/mobile/android/android-components/components/feature/webauthn/README.md b/mobile/android/android-components/components/feature/webauthn/README.md
new file mode 100644
index 0000000000..ba8f1c8c66
--- /dev/null
+++ b/mobile/android/android-components/components/feature/webauthn/README.md
@@ -0,0 +1,59 @@
+# [Android Components](../../../README.md) > Feature > WebAuthn
+
+A feature that provides WebAuthn functionality for supported engines.
+
+## Usage
+
+Add the feature to the Activity/Fragment:
+
+```kotlin
+val webAuthnFeature = WebAuthnFeature(
+ engine = GeckoEngine,
+ activity = requireActivity()
+)
+```
+
+**Note:** If the feature is on the fragment, ensure that `onActivityResult` calls from the activity are forwarded to the fragment.
+
+Allow the feature to consume the `onActivityResult` data:
+
+```kotlin
+override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ webAuthFeature.onActiviyResult(requestCode, data, resultCode)
+}
+```
+
+As with other features in Android Components, `WebAuthnFeature` implements `LifecycleAwareFeature`, so it's recommended to use `ViewBoundFeatureWrapper` to handle the lifecycle events of the feature:
+
+```kotlin
+private val webAuthnFeature = ViewBoundFeatureWrapper<WebAuthnFeature>()
+
+override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ webAuthnFeature.set(
+ feature = WebAuthnFeature(
+ engine = GeckoEngine,
+ activity = requireActivity()
+ ),
+ owner = this,
+ view = view
+ )
+}
+
+override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ webAuthnFeature.onActivityResult(requestCode, data, resultCode) }
+}
+```
+
+### Setting up the dependency
+
+Use Gradle to download the library from [maven.mozilla.org](https://maven.mozilla.org/) ([Setup repository](../../../README.md#maven-repository)):
+
+```Groovy
+implementation "org.mozilla.components:feature-webauthn:{latest-version}"
+```
+
+## License
+
+ 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/
diff --git a/mobile/android/android-components/components/feature/webauthn/build.gradle b/mobile/android/android-components/components/feature/webauthn/build.gradle
new file mode 100644
index 0000000000..519b717dcf
--- /dev/null
+++ b/mobile/android/android-components/components/feature/webauthn/build.gradle
@@ -0,0 +1,36 @@
+/* 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/. */
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+ defaultConfig {
+ minSdkVersion config.minSdkVersion
+ compileSdk config.compileSdkVersion
+ targetSdkVersion config.targetSdkVersion
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ namespace 'mozilla.components.feature.webauthn'
+}
+
+dependencies {
+ implementation project(':concept-engine')
+
+ testImplementation project(':support-test')
+
+ testImplementation ComponentsDependencies.androidx_test_core
+ testImplementation ComponentsDependencies.androidx_test_junit
+}
+
+apply from: '../../../android-lint.gradle'
+apply from: '../../../publish.gradle'
+ext.configurePublish(config.componentsGroupId, archivesBaseName, project.ext.description)
diff --git a/mobile/android/android-components/components/feature/webauthn/proguard-rules.pro b/mobile/android/android-components/components/feature/webauthn/proguard-rules.pro
new file mode 100644
index 0000000000..f1b424510d
--- /dev/null
+++ b/mobile/android/android-components/components/feature/webauthn/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/mobile/android/android-components/components/feature/webauthn/src/main/AndroidManifest.xml b/mobile/android/android-components/components/feature/webauthn/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..e16cda1d34
--- /dev/null
+++ b/mobile/android/android-components/components/feature/webauthn/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<!-- 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/. -->
+<manifest />
diff --git a/mobile/android/android-components/components/feature/webauthn/src/main/java/mozilla/components/feature/webauthn/WebAuthnFeature.kt b/mobile/android/android-components/components/feature/webauthn/src/main/java/mozilla/components/feature/webauthn/WebAuthnFeature.kt
new file mode 100644
index 0000000000..c896421af5
--- /dev/null
+++ b/mobile/android/android-components/components/feature/webauthn/src/main/java/mozilla/components/feature/webauthn/WebAuthnFeature.kt
@@ -0,0 +1,66 @@
+/* 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 mozilla.components.feature.webauthn
+
+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
+
+/**
+ * A feature that implementing the [ActivityDelegate] to adds support
+ * for [WebAuthn](https://tools.ietf.org/html/rfc8809).
+ */
+class WebAuthnFeature(
+ private val engine: Engine,
+ private val activity: Activity,
+ private val exitFullScreen: (String?) -> Unit,
+ private val currentTab: () -> String?,
+) : LifecycleAwareFeature, ActivityResultHandler, ActivityDelegate {
+ private val logger = Logger("WebAuthnFeature")
+ private var requestCodeCounter = ACTIVITY_REQUEST_CODE
+ private var callbackRef: ((Intent?) -> Unit)? = null
+
+ override fun start() {
+ engine.registerActivityDelegate(this)
+ }
+
+ override fun stop() {
+ engine.unregisterActivityDelegate()
+ }
+
+ override fun onActivityResult(requestCode: Int, data: Intent?, resultCode: Int): Boolean {
+ logger.info(
+ "Received activity result with " +
+ "code: $requestCode " +
+ "and original request code: $requestCodeCounter",
+ )
+
+ if (requestCode != requestCodeCounter) {
+ return false
+ }
+
+ requestCodeCounter++
+
+ callbackRef?.invoke(data)
+
+ return true
+ }
+
+ override fun startIntentSenderForResult(intent: IntentSender, onResult: (Intent?) -> Unit) {
+ logger.info("Received activity delegate request with code: $requestCodeCounter")
+ exitFullScreen(currentTab())
+ activity.startIntentSenderForResult(intent, requestCodeCounter, null, 0, 0, 0)
+ callbackRef = onResult
+ }
+
+ companion object {
+ const val ACTIVITY_REQUEST_CODE = 10
+ }
+}
diff --git a/mobile/android/android-components/components/feature/webauthn/src/test/java/mozilla/components/feature/webauthn/WebAuthnFeatureTest.kt b/mobile/android/android-components/components/feature/webauthn/src/test/java/mozilla/components/feature/webauthn/WebAuthnFeatureTest.kt
new file mode 100644
index 0000000000..1714d44c17
--- /dev/null
+++ b/mobile/android/android-components/components/feature/webauthn/src/test/java/mozilla/components/feature/webauthn/WebAuthnFeatureTest.kt
@@ -0,0 +1,92 @@
+/* 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 mozilla.components.feature.webauthn
+
+import android.app.Activity
+import android.content.Intent
+import android.content.IntentSender
+import mozilla.components.concept.engine.Engine
+import mozilla.components.feature.webauthn.WebAuthnFeature.Companion.ACTIVITY_REQUEST_CODE
+import mozilla.components.support.test.eq
+import mozilla.components.support.test.mock
+import mozilla.components.support.test.nullable
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.verify
+
+class WebAuthnFeatureTest {
+ private lateinit var engine: Engine
+ private lateinit var activity: Activity
+ private val exitFullScreen: (String?) -> Unit = { _ -> exitFullScreenUseCaseCalled = true }
+ private var exitFullScreenUseCaseCalled = false
+
+ @Before
+ fun setup() {
+ engine = mock()
+ activity = mock()
+ }
+
+ @Test
+ fun `feature registers itself on start`() {
+ val feature = webAuthnFeature()
+
+ feature.start()
+
+ verify(engine).registerActivityDelegate(feature)
+ }
+
+ @Test
+ fun `feature unregisters itself on stop`() {
+ val feature = webAuthnFeature()
+
+ feature.stop()
+
+ verify(engine).unregisterActivityDelegate()
+ }
+
+ @Test
+ fun `activity delegate starts intent sender`() {
+ val feature = webAuthnFeature()
+ val callback: ((Intent?) -> Unit) = { }
+ val intentSender: IntentSender = mock()
+
+ feature.startIntentSenderForResult(intentSender, callback)
+
+ verify(activity).startIntentSenderForResult(eq(intentSender), anyInt(), nullable(), eq(0), eq(0), eq(0))
+ }
+
+ @Test
+ fun `callback is invoked`() {
+ val feature = webAuthnFeature()
+ var callbackInvoked = false
+ val callback: ((Intent?) -> Unit) = { callbackInvoked = true }
+ val intentSender: IntentSender = mock()
+
+ feature.onActivityResult(ACTIVITY_REQUEST_CODE, Intent(), 0)
+
+ assertFalse(callbackInvoked)
+
+ feature.startIntentSenderForResult(intentSender, callback)
+ feature.onActivityResult(ACTIVITY_REQUEST_CODE + 1, Intent(), 0)
+
+ assertTrue(callbackInvoked)
+ }
+
+ @Test
+ fun `feature won't process results with the wrong request code`() {
+ val feature = webAuthnFeature()
+
+ val result = feature.onActivityResult(ACTIVITY_REQUEST_CODE - 5, Intent(), 0)
+
+ assertFalse(result)
+ }
+
+ private fun webAuthnFeature(): WebAuthnFeature {
+ return WebAuthnFeature(engine, activity, { exitFullScreen("") }) { "" }
+ }
+}
diff --git a/mobile/android/android-components/components/feature/webauthn/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/mobile/android/android-components/components/feature/webauthn/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000000..cf1c399ea8
--- /dev/null
+++ b/mobile/android/android-components/components/feature/webauthn/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1,2 @@
+mock-maker-inline
+// This allows mocking final classes (classes are final by default in Kotlin)
diff --git a/mobile/android/android-components/components/feature/webauthn/src/test/resources/robolectric.properties b/mobile/android/android-components/components/feature/webauthn/src/test/resources/robolectric.properties
new file mode 100644
index 0000000000..932b01b9eb
--- /dev/null
+++ b/mobile/android/android-components/components/feature/webauthn/src/test/resources/robolectric.properties
@@ -0,0 +1 @@
+sdk=28