summaryrefslogtreecommitdiffstats
path: root/mobile/android/android-components/components/feature/accounts/src
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/android-components/components/feature/accounts/src')
-rw-r--r--mobile/android/android-components/components/feature/accounts/src/main/assets/extensions/fxawebchannel/background.js10
-rw-r--r--mobile/android/android-components/components/feature/accounts/src/main/assets/extensions/fxawebchannel/fxawebchannel.js12
-rw-r--r--mobile/android/android-components/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeature.kt13
-rw-r--r--mobile/android/android-components/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FxaWebChannelFeature.kt36
-rw-r--r--mobile/android/android-components/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeatureTest.kt14
-rw-r--r--mobile/android/android-components/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FxaWebChannelFeatureTest.kt86
6 files changed, 151 insertions, 20 deletions
diff --git a/mobile/android/android-components/components/feature/accounts/src/main/assets/extensions/fxawebchannel/background.js b/mobile/android/android-components/components/feature/accounts/src/main/assets/extensions/fxawebchannel/background.js
index b90f57154a..e8cf40ba8d 100644
--- a/mobile/android/android-components/components/feature/accounts/src/main/assets/extensions/fxawebchannel/background.js
+++ b/mobile/android/android-components/components/feature/accounts/src/main/assets/extensions/fxawebchannel/background.js
@@ -10,12 +10,12 @@ let port = browser.runtime.connectNative(WEB_CHANNEL_BACKGROUND_MESSAGING_ID);
/*
Handle messages from native application, register content script for specific url.
*/
-port.onMessage.addListener( event => {
- if(event.type == "overrideFxAServer"){
+port.onMessage.addListener(event => {
+ if (event.type == "overrideFxAServer") {
browser.contentScripts.register({
- "matches": [ event.url+"/*" ],
- "js": [{file: "fxawebchannel.js"}],
- "runAt": "document_start"
+ matches: [event.url + "/*"],
+ js: [{ file: "fxawebchannel.js" }],
+ runAt: "document_start",
});
port.disconnect();
}
diff --git a/mobile/android/android-components/components/feature/accounts/src/main/assets/extensions/fxawebchannel/fxawebchannel.js b/mobile/android/android-components/components/feature/accounts/src/main/assets/extensions/fxawebchannel/fxawebchannel.js
index 2f5934dff1..16614d3069 100644
--- a/mobile/android/android-components/components/feature/accounts/src/main/assets/extensions/fxawebchannel/fxawebchannel.js
+++ b/mobile/android/android-components/components/feature/accounts/src/main/assets/extensions/fxawebchannel/fxawebchannel.js
@@ -10,16 +10,18 @@ let port = browser.runtime.connectNative("mozacWebchannel");
/*
Handle messages from native application, dispatch them to FxA via an event.
*/
-port.onMessage.addListener((event) => {
- window.dispatchEvent(new CustomEvent('WebChannelMessageToContent', {
- detail: JSON.stringify(event)
- }));
+port.onMessage.addListener(event => {
+ window.dispatchEvent(
+ new CustomEvent("WebChannelMessageToContent", {
+ detail: JSON.stringify(event),
+ })
+ );
});
/*
Handle messages from FxA. Messages are posted to the native application for processing.
*/
-window.addEventListener('WebChannelMessageToChrome', function (e) {
+window.addEventListener("WebChannelMessageToChrome", function (e) {
const detail = JSON.parse(e.detail);
port.postMessage(detail);
});
diff --git a/mobile/android/android-components/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeature.kt b/mobile/android/android-components/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeature.kt
index 60913282b7..b244eb0de8 100644
--- a/mobile/android/android-components/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeature.kt
+++ b/mobile/android/android-components/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeature.kt
@@ -38,10 +38,15 @@ class FirefoxAccountsAuthFeature(
* @param context [Context] The application context
* @param entrypoint [FxAEntryPoint] The Firefox Accounts feature/entrypoint that is launching
* authentication
+ * @param scopes [Set<String>] The oAuth scopes being requested
*/
- fun beginAuthentication(context: Context, entrypoint: FxAEntryPoint) {
+ fun beginAuthentication(
+ context: Context,
+ entrypoint: FxAEntryPoint,
+ scopes: Set<String> = emptySet(),
+ ) {
beginAuthenticationAsync(context) {
- accountManager.beginAuthentication(entrypoint = entrypoint)
+ accountManager.beginAuthentication(entrypoint = entrypoint, authScopes = scopes)
}
}
@@ -50,15 +55,17 @@ class FirefoxAccountsAuthFeature(
* @param context [Context] The application context
* @param pairingUrl [String] The pairing URL retrieved from the QR scanner
* @param entrypoint [FxAEntryPoint] The Firefox Accounts feature/entrypoint that is launching
+ * @param scopes [Set<String>] The oAuth scopes being requested
* authentication
*/
fun beginPairingAuthentication(
context: Context,
pairingUrl: String,
entrypoint: FxAEntryPoint,
+ scopes: Set<String> = emptySet(),
) {
beginAuthenticationAsync(context) {
- accountManager.beginAuthentication(pairingUrl, entrypoint = entrypoint)
+ accountManager.beginAuthentication(pairingUrl, entrypoint = entrypoint, scopes)
}
}
diff --git a/mobile/android/android-components/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FxaWebChannelFeature.kt b/mobile/android/android-components/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FxaWebChannelFeature.kt
index f5554c99e4..377d6b0d93 100644
--- a/mobile/android/android-components/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FxaWebChannelFeature.kt
+++ b/mobile/android/android-components/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FxaWebChannelFeature.kt
@@ -20,6 +20,7 @@ import mozilla.components.concept.engine.webextension.MessageHandler
import mozilla.components.concept.engine.webextension.Port
import mozilla.components.concept.engine.webextension.WebExtensionRuntime
import mozilla.components.concept.sync.AuthType
+import mozilla.components.concept.sync.UserData
import mozilla.components.lib.state.ext.flowScoped
import mozilla.components.service.fxa.FxaAuthData
import mozilla.components.service.fxa.ServerConfig
@@ -157,6 +158,7 @@ class FxaWebChannelFeature(
WebChannelCommand.CAN_LINK_ACCOUNT -> processCanLinkAccountCommand(messageId)
WebChannelCommand.FXA_STATUS -> processFxaStatusCommand(accountManager, messageId, fxaCapabilities)
WebChannelCommand.OAUTH_LOGIN -> processOauthLoginCommand(accountManager, payload)
+ WebChannelCommand.LOGIN -> processLoginCommand(accountManager, payload)
}
response?.let { port.postMessage(it) }
}
@@ -195,6 +197,7 @@ class FxaWebChannelFeature(
enum class WebChannelCommand {
CAN_LINK_ACCOUNT,
+ LOGIN,
OAUTH_LOGIN,
FXA_STATUS,
}
@@ -222,6 +225,12 @@ class FxaWebChannelFeature(
private const val COMMAND_STATUS = "fxaccounts:fxa_status"
/**
+ * Gets triggered when the web content is signed in/up, but not necessarily verified
+ * it passes in its payload the session token the web content is holding on to
+ */
+ private const val COMMAND_LOGIN = "fxaccounts:login"
+
+ /**
* Handles the [COMMAND_CAN_LINK_ACCOUNT] event from the web-channel.
* Currently this always response with 'ok=true'.
* On Fx Desktop, this event prompts a possible "another user was previously logged in on
@@ -329,6 +338,32 @@ class FxaWebChannelFeature(
}
/**
+ * Handles the [COMMAND_LOGIN] event from the web-channel
+ */
+ private fun processLoginCommand(accountManager: FxaAccountManager, payload: JSONObject): JSONObject? {
+ val sessionToken: String
+ val email: String
+ val uid: String
+ val verified: Boolean
+
+ try {
+ val data = payload.getJSONObject("data")
+ sessionToken = data.getString("sessionToken")
+ email = data.getString("email")
+ uid = data.getString("uid")
+ verified = data.getBoolean("verified")
+ } catch (e: JSONException) {
+ logger.error("Error while processing WebChannel login command", e)
+ return null
+ }
+ val userData = UserData(sessionToken, email, uid, verified)
+ CoroutineScope(Dispatchers.Main).launch {
+ accountManager.setUserData(userData)
+ }
+ return null
+ }
+
+ /**
* Handles the [COMMAND_OAUTH_LOGIN] event from the web-channel.
*/
private fun processOauthLoginCommand(accountManager: FxaAccountManager, payload: JSONObject): JSONObject? {
@@ -368,6 +403,7 @@ class FxaWebChannelFeature(
COMMAND_CAN_LINK_ACCOUNT -> WebChannelCommand.CAN_LINK_ACCOUNT
COMMAND_OAUTH_LOGIN -> WebChannelCommand.OAUTH_LOGIN
COMMAND_STATUS -> WebChannelCommand.FXA_STATUS
+ COMMAND_LOGIN -> WebChannelCommand.LOGIN
else -> {
logger.warn("Unrecognized WebChannel command: $this")
null
diff --git a/mobile/android/android-components/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeatureTest.kt b/mobile/android/android-components/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeatureTest.kt
index a32681e8fe..ef7f78b336 100644
--- a/mobile/android/android-components/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeatureTest.kt
+++ b/mobile/android/android-components/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FirefoxAccountsAuthFeatureTest.kt
@@ -17,8 +17,8 @@ import mozilla.components.concept.sync.AuthType
import mozilla.components.concept.sync.DeviceConfig
import mozilla.components.concept.sync.DeviceType
import mozilla.components.concept.sync.FxAEntryPoint
-import mozilla.components.concept.sync.OAuthAccount
import mozilla.components.concept.sync.Profile
+import mozilla.components.service.fxa.FirefoxAccount
import mozilla.components.service.fxa.FxaAuthData
import mozilla.components.service.fxa.ServerConfig
import mozilla.components.service.fxa.StorageWrapper
@@ -45,13 +45,13 @@ internal class TestableStorageWrapper(
manager: FxaAccountManager,
accountEventObserverRegistry: ObserverRegistry<AccountEventsObserver>,
serverConfig: ServerConfig,
- private val block: () -> OAuthAccount = {
- val account: OAuthAccount = mock()
+ private val block: () -> FirefoxAccount = {
+ val account: FirefoxAccount = mock()
`when`(account.deviceConstellation()).thenReturn(mock())
account
},
) : StorageWrapper(manager, accountEventObserverRegistry, serverConfig) {
- override fun obtainAccount(): OAuthAccount = block()
+ override fun obtainAccount(): FirefoxAccount = block()
}
// Same as the actual account manager, except we get to control how FirefoxAccountShaped instances
@@ -63,7 +63,7 @@ class TestableFxaAccountManager(
config: ServerConfig,
scopes: Set<String>,
coroutineContext: CoroutineContext,
- block: () -> OAuthAccount = { mock() },
+ block: () -> FirefoxAccount = { mock() },
) : FxaAccountManager(context, config, DeviceConfig("test", DeviceType.MOBILE, setOf()), null, scopes, null, coroutineContext) {
private val testableStorageWrapper = TestableStorageWrapper(this, accountEventObserverRegistry, serverConfig, block)
override fun getStorageWrapper(): StorageWrapper {
@@ -252,7 +252,7 @@ class FirefoxAccountsAuthFeatureTest {
private suspend fun prepareAccountManagerForSuccessfulAuthentication(
coroutineContext: CoroutineContext,
): TestableFxaAccountManager {
- val mockAccount: OAuthAccount = mock()
+ val mockAccount: FirefoxAccount = mock()
val profile = Profile(uid = "testUID", avatar = null, email = "test@example.com", displayName = "test profile")
`when`(mockAccount.deviceConstellation()).thenReturn(mock())
@@ -279,7 +279,7 @@ class FirefoxAccountsAuthFeatureTest {
private suspend fun prepareAccountManagerForFailedAuthentication(
coroutineContext: CoroutineContext,
): TestableFxaAccountManager {
- val mockAccount: OAuthAccount = mock()
+ val mockAccount: FirefoxAccount = mock()
val profile = Profile(uid = "testUID", avatar = null, email = "test@example.com", displayName = "test profile")
`when`(mockAccount.getProfile(anyBoolean())).thenReturn(profile)
diff --git a/mobile/android/android-components/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FxaWebChannelFeatureTest.kt b/mobile/android/android-components/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FxaWebChannelFeatureTest.kt
index 809ed7a703..3a49633f61 100644
--- a/mobile/android/android-components/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FxaWebChannelFeatureTest.kt
+++ b/mobile/android/android-components/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FxaWebChannelFeatureTest.kt
@@ -19,6 +19,7 @@ import mozilla.components.concept.engine.webextension.WebExtension
import mozilla.components.concept.sync.AuthType
import mozilla.components.concept.sync.OAuthAccount
import mozilla.components.concept.sync.Profile
+import mozilla.components.concept.sync.UserData
import mozilla.components.service.fxa.FxaAuthData
import mozilla.components.service.fxa.ServerConfig
import mozilla.components.service.fxa.SyncEngine
@@ -701,6 +702,56 @@ class FxaWebChannelFeatureTest {
assertTrue(FxaWebChannelFeature.isCommunicationAllowed("http://localhost", "http://localhost"))
}
+ @Test
+ fun `COMMAND_LOGIN must be processed and sets the user's data`() = runTest {
+ val accountManager: FxaAccountManager = mock() // syncConfig is null by default (is not configured)
+ val engineSession: EngineSession = mock()
+ val ext: WebExtension = mock()
+ val port: Port = mock()
+ val messageHandler = argumentCaptor<MessageHandler>()
+
+ WebExtensionController.installedExtensions[FxaWebChannelFeature.WEB_CHANNEL_EXTENSION_ID] = ext
+
+ val webchannelFeature = prepareFeatureForTest(ext, port, engineSession, null, emptySet(), accountManager)
+ webchannelFeature.start()
+ shadowOf(getMainLooper()).idle()
+
+ verify(ext).registerContentMessageHandler(
+ eq(engineSession),
+ eq(FxaWebChannelFeature.WEB_CHANNEL_MESSAGING_ID),
+ messageHandler.capture(),
+ )
+ messageHandler.value.onPortConnected(port)
+
+ // Action: signin
+ verifyLogin("sessiontoken123", "foo@bar.com", "uid123", false, messageHandler.value, accountManager)
+ }
+
+ @Test
+ fun `COMMAND_LOGIN invalid json sends back`() = runTest {
+ val accountManager: FxaAccountManager = mock() // syncConfig is null by default (is not configured)
+ val engineSession: EngineSession = mock()
+ val ext: WebExtension = mock()
+ val port: Port = mock()
+ val messageHandler = argumentCaptor<MessageHandler>()
+
+ WebExtensionController.installedExtensions[FxaWebChannelFeature.WEB_CHANNEL_EXTENSION_ID] = ext
+
+ val webchannelFeature = prepareFeatureForTest(ext, port, engineSession, null, emptySet(), accountManager)
+ webchannelFeature.start()
+ shadowOf(getMainLooper()).idle()
+
+ verify(ext).registerContentMessageHandler(
+ eq(engineSession),
+ eq(FxaWebChannelFeature.WEB_CHANNEL_MESSAGING_ID),
+ messageHandler.capture(),
+ )
+ messageHandler.value.onPortConnected(port)
+
+ // Action: signin
+ verifyLogin("sessiontoken123", "foo@bar.com", "uid123", false, messageHandler.value, accountManager)
+ }
+
private fun JSONObject.getSupportedEngines(): List<String> {
val engines = this.getJSONObject("message")
.getJSONObject("data")
@@ -798,6 +849,41 @@ class FxaWebChannelFeatureTest {
)
}
+ private suspend fun verifyLogin(sessionToken: String, email: String, uid: String, verified: Boolean, messageHandler: MessageHandler, accountManager: FxaAccountManager) {
+ val jsonToWebChannel = jsonLogin(sessionToken, email, uid, verified)
+ val port = mock<Port>()
+ whenever(port.senderUrl()).thenReturn("https://foo.bar/email")
+ messageHandler.onPortMessage(jsonToWebChannel, port)
+
+ val expectedUserData = UserData(
+ sessionToken = sessionToken,
+ email = email,
+ uid = uid,
+ verified = verified,
+ )
+ shadowOf(getMainLooper()).idle()
+
+ verify(accountManager).setUserData(expectedUserData)
+ }
+
+ private fun jsonLogin(sessionToken: String, email: String, uid: String, verified: Boolean): JSONObject {
+ return JSONObject(
+ """{
+ "message":{
+ "command": "fxaccounts:login",
+ "messageId":123,
+ "data":{
+ "email":"$email",
+ "sessionToken":"$sessionToken",
+ "uid":"$uid",
+ "verified":$verified
+ }
+ }
+ }
+ """.trimIndent(),
+ )
+ }
+
private fun prepareFeatureForTest(
ext: WebExtension = mock(),
port: Port = mock(),