Skip to content
This repository has been archived by the owner on Feb 20, 2023. It is now read-only.

Commit

Permalink
WIP Use WebChannels for FxA integration
Browse files Browse the repository at this point in the history
  • Loading branch information
Grisha Kruglov committed Sep 10, 2019
1 parent eb26d95 commit 8f1e7e9
Show file tree
Hide file tree
Showing 13 changed files with 159 additions and 13 deletions.
55 changes: 55 additions & 0 deletions app/metrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,61 @@ sync_auth:
notification_emails:
- fenix-core@mozilla.com
expires: "2020-03-01"
sign_up:
type: event
description: >
User registered a new Firefox Account, and was signed into it
bugs:
- 4971
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300
notification_emails:
- fenix-core@mozilla.com
expires: "2020-03-01"
paired:
type: event
description: >
User signed into FxA by pairing with a different Firefox browser, using a QR code
bugs:
- 4971
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300
notification_emails:
- fenix-core@mozilla.com
expires: "2020-03-01"
auto_login:
type: event
description: >
User signed into FxA via an account shared from another locally installed Mozilla application (e.g. Fennec)
bugs:
- 4971
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300
notification_emails:
- fenix-core@mozilla.com
expires: "2020-03-01"
recovered:
type: event
description: >
Account manager automatically recovered FxA authentication state without direct user involvement
bugs:
- 4971
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300
notification_emails:
- fenix-core@mozilla.com
expires: "2020-03-01"
other_external:
type: event
description: >
User signed into FxA via an unknown mechanism
bugs:
- 4971
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300
notification_emails:
- fenix-core@mozilla.com
expires: "2020-03-01"
scan_pairing:
type: event
description: >
Expand Down
9 changes: 7 additions & 2 deletions app/src/main/java/org/mozilla/fenix/AppRequestInterceptor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,13 @@ class AppRequestInterceptor(private val context: Context) : RequestInterceptor {
}

adjustTrackingProtection(host, context, session)
// Accounts uses interception to check for a "success URL" in the sign-in flow to finalize authentication.
return context.components.services.accountsAuthFeature.interceptor.onLoadRequest(session, uri)

// WebChannel-driven authentication does not require a separate redirect interceptor.
return if (context.isInExperiment(Experiments.asFeatureWebChannelsDisabled)) {
context.components.services.accountsAuthFeature.interceptor.onLoadRequest(session, uri)
} else {
null
}
}

private fun adjustTrackingProtection(host: String, context: Context, session: EngineSession) {
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/org/mozilla/fenix/Experiments.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ object Experiments {
val asFeatureSyncDisabled = ExperimentDescriptor("asFeatureSyncDisabled")
// application services flag to disable Firefox Accounts pairing button.
val asFeatureFxAPairingDisabled = ExperimentDescriptor("asFeatureFxAPairingDisabled")
// application services flag to disable Firefox Accounts WebChannel integration.
val asFeatureWebChannelsDisabled = ExperimentDescriptor("asFeatureWebChannelsDisabled")
}

val Context.app: FenixApplication
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class IntentReceiverActivity : Activity() {
private fun setIntentActivity(intent: Intent) {
val openToBrowser = when {
components.utils.customTabIntentProcessor.matches(intent) -> {
// TODO This is potentially unsafe. Any app on the system can set the auth extra.
val activityClass = if (intent.hasExtra(getString(R.string.intent_extra_auth))) {
AuthCustomTabActivity::class
} else {
Expand Down
30 changes: 30 additions & 0 deletions app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import mozilla.appservices.places.BookmarkRoot
import mozilla.components.browser.session.Session
import mozilla.components.feature.accounts.FxaWebChannelFeature
import mozilla.components.feature.readerview.ReaderViewFeature
import mozilla.components.feature.session.ThumbnailsFeature
import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.support.base.feature.BackHandler
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import org.mozilla.fenix.Experiments
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.readermode.DefaultReaderModeController
Expand All @@ -45,6 +48,7 @@ import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.home.sessioncontrol.SessionControlChange
import org.mozilla.fenix.home.sessioncontrol.TabCollection
import org.mozilla.fenix.isInExperiment
import org.mozilla.fenix.mvi.getManagedEmitter
import org.mozilla.fenix.quickactionsheet.DefaultQuickActionSheetController
import org.mozilla.fenix.quickactionsheet.QuickActionSheetSessionObserver
Expand All @@ -61,6 +65,8 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
private var quickActionSheetSessionObserver: QuickActionSheetSessionObserver? = null

private val readerViewFeature = ViewBoundFeatureWrapper<ReaderViewFeature>()
private val thumbnailsFeature = ViewBoundFeatureWrapper<ThumbnailsFeature>()
private val webchannelIntegration = ViewBoundFeatureWrapper<FxaWebChannelFeature>()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down Expand Up @@ -90,6 +96,30 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {

return super.initializeUI(view)?.also {

thumbnailsFeature.set(
feature = ThumbnailsFeature(
requireContext(),
view.engineView,
requireComponents.core.sessionManager
),
owner = this,
view = view
)

if (!requireContext().isInExperiment(Experiments.asFeatureWebChannelsDisabled)) {
webchannelIntegration.set(
feature = FxaWebChannelFeature(
requireContext(),
customTabSessionId,
requireComponents.core.engine,
requireComponents.core.sessionManager,
requireComponents.backgroundServices.accountManager
),
owner = this,
view = view
)
}

readerViewFeature.set(
feature = ReaderViewFeature(
requireContext(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,12 @@ class BackgroundServices(
) {
companion object {
const val CLIENT_ID = "a2270f727f45f648"
const val REDIRECT_URL = "https://accounts.firefox.com/oauth/success/$CLIENT_ID"

fun redirectUrl(context: Context) = if (context.isInExperiment(Experiments.asFeatureWebChannelsDisabled)) {
"https://accounts.firefox.com/oauth/success/$CLIENT_ID"
} else {
"urn:ietf:wg:oauth:2.0:oob:oauth-redirect-webchannel"
}
}

fun defaultDeviceName(context: Context): String = context.getString(
Expand All @@ -65,7 +70,7 @@ class BackgroundServices(
Build.MODEL
)

private val serverConfig = ServerConfig.release(CLIENT_ID, REDIRECT_URL)
private val serverConfig = ServerConfig.release(CLIENT_ID, redirectUrl(context))
private val deviceConfig = DeviceConfig(
name = defaultDeviceName(context),
type = DeviceType.MOBILE,
Expand Down Expand Up @@ -150,7 +155,20 @@ class BackgroundServices(
push.subscribeForType(PushType.Services)
}

context.components.analytics.metrics.track(Event.SyncAuthSignIn)
when (authType) {
// User signed-in into an existing FxA account.
AuthType.Signin -> context.components.analytics.metrics.track(Event.SyncAuthSignIn)
// User created a new FxA account.
AuthType.Signup -> context.components.analytics.metrics.track(Event.SyncAuthSignUp)
// User paired to an existing account via QR code scanning.
AuthType.Pairing -> context.components.analytics.metrics.track(Event.SyncAuthPaired)
// User signed-in into an FxA account shared from another locally installed app (e.g. Fennec).
AuthType.Shared -> context.components.analytics.metrics.track(Event.SyncAuthFromShared)
// Account Manager recovered a broken FxA auth state, without direct user involvement.
AuthType.Recovered -> context.components.analytics.metrics.track(Event.SyncAuthRecovered)
// User signed-in into an FxA account via unknown means. Exact mechanism identified by the 'action' param.
is AuthType.OtherExternal -> context.components.analytics.metrics.track(Event.SyncAuthOtherExternal)
}

Settings.getInstance(context).fxaSignedIn = true
}
Expand All @@ -174,6 +192,7 @@ class BackgroundServices(
// See https://github.com/mozilla-mobile/android-components/issues/3732
setOf("https://identity.mozilla.com/apps/oldsync")
).also {
// TODO this needs to change once we have a SyncManager
Settings.getInstance(context).fxaHasSyncedItems = syncConfig?.supportedEngines?.isNotEmpty() ?: false

it.registerForDeviceEvents(deviceEventObserver, ProcessLifecycleOwner.get(), false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Components(private val context: Context) {
val backgroundServices by lazy {
BackgroundServices(context, core.historyStorage, core.bookmarksStorage)
}
val services by lazy { Services(backgroundServices.accountManager) }
val services by lazy { Services(context, backgroundServices.accountManager) }
val core by lazy { Core(context) }
val search by lazy { Search(context) }
val useCases by lazy {
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/org/mozilla/fenix/components/Services.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ import org.mozilla.fenix.test.Mockable
*/
@Mockable
class Services(
private val context: Context,
private val accountManager: FxaAccountManager
) {
val accountsAuthFeature by lazy {
FirefoxAccountsAuthFeature(
accountManager,
redirectUrl = BackgroundServices.REDIRECT_URL
redirectUrl = BackgroundServices.redirectUrl(context)
) { context, authUrl ->
CoroutineScope(Dispatchers.Main).launch {
val intent = SupportUtils.createAuthCustomTabIntent(context, authUrl)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,15 +224,27 @@ private val Event.wrapper: EventWrapper<*>?
is Event.SyncAuthSignIn -> EventWrapper<NoExtraKeys>(
{ SyncAuth.signIn.record(it) }
)
is Event.SyncAuthSignUp -> EventWrapper<NoExtraKeys>(
{ SyncAuth.signUp.record(it) }
)
is Event.SyncAuthPaired -> EventWrapper<NoExtraKeys>(
{ SyncAuth.paired.record(it) }
)
is Event.SyncAuthOtherExternal -> EventWrapper<NoExtraKeys>(
{ SyncAuth.otherExternal.record(it) }
)
is Event.SyncAuthFromShared -> EventWrapper<NoExtraKeys>(
{ SyncAuth.autoLogin.record(it) }
)
is Event.SyncAuthRecovered -> EventWrapper<NoExtraKeys>(
{ SyncAuth.recovered.record(it) }
)
is Event.SyncAuthSignOut -> EventWrapper<NoExtraKeys>(
{ SyncAuth.signOut.record(it) }
)
is Event.SyncAuthScanPairing -> EventWrapper<NoExtraKeys>(
{ SyncAuth.scanPairing.record(it) }
)
is Event.SyncAuthCreateAccount -> EventWrapper<NoExtraKeys>(
{ SyncAuth.createAccount.record(it) }
)
is Event.SyncAccountOpened -> EventWrapper<NoExtraKeys>(
{ SyncAccount.opened.record(it) }
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,14 @@ sealed class Event {
object LibraryClosed : Event()
object SyncAuthOpened : Event()
object SyncAuthClosed : Event()
object SyncAuthSignUp: Event()
object SyncAuthSignIn : Event()
object SyncAuthSignOut : Event()
object SyncAuthScanPairing : Event()
object SyncAuthCreateAccount : Event()
object SyncAuthPaired : Event()
object SyncAuthRecovered : Event()
object SyncAuthOtherExternal : Event()
object SyncAuthFromShared : Event()
object SyncAccountOpened : Event()
object SyncAccountClosed : Event()
object SyncAccountSyncNow : Event()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import mozilla.components.concept.sync.AccountObserver
import mozilla.components.concept.sync.AuthType
import mozilla.components.concept.sync.OAuthAccount
import org.mozilla.fenix.ext.components

/**
* A special custom tab for signing into a Firefox Account. The activity is closed once the user is signed in.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,19 @@ import kotlinx.android.synthetic.main.fragment_browser.view.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ObsoleteCoroutinesApi
import mozilla.components.browser.session.Session
import mozilla.components.feature.accounts.FxaWebChannelFeature
import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.support.base.feature.BackHandler
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import org.mozilla.fenix.Experiments
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.BaseBrowserFragment
import org.mozilla.fenix.components.toolbar.BrowserToolbarController
import org.mozilla.fenix.components.toolbar.BrowserToolbarInteractor
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.isInExperiment

/**
* Fragment used for browsing the web within external apps.
Expand All @@ -30,6 +33,7 @@ import org.mozilla.fenix.ext.requireComponents
class ExternalAppBrowserFragment : BaseBrowserFragment(), BackHandler {

private val customTabsIntegration = ViewBoundFeatureWrapper<CustomTabsIntegration>()
private val webchannelIntegration = ViewBoundFeatureWrapper<FxaWebChannelFeature>()

override fun initializeUI(view: View): Session? {
return super.initializeUI(view)?.also {
Expand All @@ -50,6 +54,20 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), BackHandler {
view = view)
}

if (!requireContext().isInExperiment(Experiments.asFeatureWebChannelsDisabled)) {
webchannelIntegration.set(
feature = FxaWebChannelFeature(
requireContext(),
customTabSessionId,
requireComponents.core.engine,
requireComponents.core.sessionManager,
requireComponents.backgroundServices.accountManager
),
owner = this,
view = view
)
}

consumeFrom(browserStore) {
browserToolbarView.update(it)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class TestComponents(private val context: Context) : Components(context) {
override val backgroundServices by lazy {
mockk<BackgroundServices>(relaxed = true)
}
override val services by lazy { Services(backgroundServices.accountManager) }
override val services by lazy { Services(context, backgroundServices.accountManager) }
override val core by lazy { TestCore(context) }
override val search by lazy { Search(context) }
override val useCases by lazy {
Expand Down

0 comments on commit 8f1e7e9

Please sign in to comment.