Skip to content

Commit

Permalink
Closes mozilla-mobile#3272 and 2438: Improve FirefoxAccountsAuthFeatu…
Browse files Browse the repository at this point in the history
…re to allow apps

to decide how to open the FxA login.
  • Loading branch information
Amejia481 committed Aug 1, 2019
1 parent ba2cb9e commit a9247df
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,23 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import mozilla.components.concept.engine.EngineSession
import mozilla.components.concept.engine.request.RequestInterceptor
import mozilla.components.feature.tabs.TabsUseCases
import mozilla.components.service.fxa.manager.FxaAccountManager
import kotlin.coroutines.CoroutineContext

/**
* Ties together an account manager with a session manager/tabs implementation, facilitating an
* authentication flow.
* @property accountManager .
* @property redirectUrl This is the url that will be reached at the final authentication state.
* @property coroutineContext The context where [onBeginAuthentication] will be executed,
* if you want an different context you are in charge of switching. The default context is [Dispatchers.IO].
* @property onBeginAuthentication A lambda function that receives the authentication url.
*/
class FirefoxAccountsAuthFeature(
private val accountManager: FxaAccountManager,
private val tabsUseCases: TabsUseCases,
private val redirectUrl: String,
private val coroutineContext: CoroutineContext = Dispatchers.Main
private val coroutineContext: CoroutineContext = Dispatchers.IO,
private val onBeginAuthentication: (String) -> Unit = {}
) {
fun beginAuthentication() {
beginAuthenticationAsync {
Expand All @@ -48,7 +52,8 @@ class FirefoxAccountsAuthFeature(
// UI to the user.
// It's possible that the underlying problem will go away by the time the tab actually
// loads, resulting in a confusing experience.
tabsUseCases.addTab.invoke(authUrl)

onBeginAuthentication(authUrl)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import kotlinx.coroutines.runBlocking
import mozilla.components.concept.sync.DeviceType
import mozilla.components.concept.sync.OAuthAccount
import mozilla.components.concept.sync.Profile
import mozilla.components.feature.tabs.TabsUseCases
import mozilla.components.service.fxa.ServerConfig
import mozilla.components.service.fxa.manager.FxaAccountManager
import mozilla.components.support.test.any
Expand All @@ -21,10 +20,10 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mockito.`when`
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import androidx.test.ext.junit.runners.AndroidJUnit4
import mozilla.components.service.fxa.DeviceConfig
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue

// Same as the actual account manager, except we get to control how FirefoxAccountShaped instances
// are created. This is necessary because due to some build issues (native dependencies not available
Expand All @@ -48,63 +47,99 @@ class FirefoxAccountsAuthFeatureTest {
@Test
fun `begin authentication`() {
val manager = prepareAccountManagerForSuccessfulAuthentication()
val mockAddTab: TabsUseCases.AddNewTabUseCase = mock()
val mockTabs: TabsUseCases = mock()
`when`(mockTabs.addTab).thenReturn(mockAddTab)
var path = ""
var wasCalled = false
val onBeginAuthentication: (String) -> Unit = {
path = it
wasCalled = true
}

runBlocking {
val feature = FirefoxAccountsAuthFeature(manager, mockTabs, "somePath", this.coroutineContext)
val feature = FirefoxAccountsAuthFeature(
manager,
"somePath",
this.coroutineContext,
onBeginAuthentication
)
feature.beginAuthentication()
}

verify(mockAddTab, times(1)).invoke("auth://url")
assertTrue(wasCalled)
assertEquals("auth://url", path)
}

@Test
fun `begin pairing authentication`() {
val manager = prepareAccountManagerForSuccessfulAuthentication()
val mockAddTab: TabsUseCases.AddNewTabUseCase = mock()
val mockTabs: TabsUseCases = mock()
`when`(mockTabs.addTab).thenReturn(mockAddTab)
var path = ""
var wasCalled = false
val onBeginAuthentication: (String) -> Unit = {
path = it
wasCalled = true
}

runBlocking {
val feature = FirefoxAccountsAuthFeature(manager, mockTabs, "somePath", this.coroutineContext)
val feature = FirefoxAccountsAuthFeature(
manager,
"somePath",
this.coroutineContext,
onBeginAuthentication
)
feature.beginPairingAuthentication("auth://pair")
}

verify(mockAddTab, times(1)).invoke("auth://url")
assertTrue(wasCalled)
assertEquals("auth://url", path)
}

@Test
fun `begin authentication with errors`() {
val manager = prepareAccountManagerForFailedAuthentication()
val mockAddTab: TabsUseCases.AddNewTabUseCase = mock()
val mockTabs: TabsUseCases = mock()
`when`(mockTabs.addTab).thenReturn(mockAddTab)
var path = ""
var wasCalled = false
val onBeginAuthentication: (String) -> Unit = {
path = it
wasCalled = true
}

runBlocking {
val feature = FirefoxAccountsAuthFeature(manager, mockTabs, "somePath", this.coroutineContext)
val feature = FirefoxAccountsAuthFeature(
manager,
"somePath",
this.coroutineContext,
onBeginAuthentication
)
feature.beginAuthentication()
}

// Fallback url is invoked.
verify(mockAddTab, times(1)).invoke("https://accounts.firefox.com/signin")
assertTrue(wasCalled)
assertEquals("https://accounts.firefox.com/signin", path)
}

@Test
fun `begin pairing authentication with errors`() {
val manager = prepareAccountManagerForFailedAuthentication()
val mockAddTab: TabsUseCases.AddNewTabUseCase = mock()
val mockTabs: TabsUseCases = mock()
`when`(mockTabs.addTab).thenReturn(mockAddTab)
var path = ""
var wasCalled = false
val onBeginAuthentication: (String) -> Unit = {
path = it
wasCalled = true
}

runBlocking {
val feature = FirefoxAccountsAuthFeature(manager, mockTabs, "somePath", this.coroutineContext)
val feature = FirefoxAccountsAuthFeature(
manager,
"somePath",
this.coroutineContext,
onBeginAuthentication
)
feature.beginPairingAuthentication("auth://pair")
}

// Fallback url is invoked.
verify(mockAddTab, times(1)).invoke("https://accounts.firefox.com/signin")
assertTrue(wasCalled)
assertEquals("https://accounts.firefox.com/signin", path)
}

private fun prepareAccountManagerForSuccessfulAuthentication(): TestableFxaAccountManager {
Expand Down
14 changes: 14 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@ permalink: /changelog/
* [Gecko](https://github.com/mozilla-mobile/android-components/blob/master/buildSrc/src/main/java/Gecko.kt)
* [Configuration](https://github.com/mozilla-mobile/android-components/blob/master/buildSrc/src/main/java/Config.kt)

* **feature-accounts**
* ⚠️ **This is a breaking change**:
* The `FirefoxAccountsAuthFeature` no longer needs an `TabsUseCases` instead it takes a lambda to
allow apps to decide which action should be taken fixing [#2438](https://github.com/mozilla-mobile/android-components/issues/2438) and [#3272](https://github.com/mozilla-mobile/android-components/issues/3272).

```kotlin
val feature = FirefoxAccountsAuthFeature(
accountManager,
redirectUrl
){
tabsUseCases.addTab(authUrl)
}
```

* **browser-engine-gecko-nightly**
* Now supports window requests. A new tab will be opened for `target="_blank"` links and `window.open` calls.

Expand Down

0 comments on commit a9247df

Please sign in to comment.