Closed Bug 1178924 Opened 9 years ago Closed 9 years ago

Implement automatic login to accounts.firefox.com from a native Android Firefox Account

Categories

(Android Background Services Graveyard :: Firefox Accounts, defect)

defect
Not set
normal

Tracking

(Not tracked)

RESOLVED DUPLICATE of bug 1204930

People

(Reporter: nalexander, Unassigned)

References

Details

We would like to transition the current Fennec native sign up/sign in flow to a web based flow.  We would prefer to transition to the web flow smoothly, i.e. not require a password the first time a user navigates to accounts.firefox.com.  (And, indeed, after every time that they clear private data or similar.)

In addition, Fennec's background sync system does a good job of maintaining the account's state and should be the sole owner of that state.  For example, Android Sync can surface failures to sync or log-in to the user, even when Gecko is not running.  There's also a desire to have accounts.firefox.com be pinned to an existing native Account's user ID; and for the user *not* be signed in to accounts.firefox.com when a native Android account is not present.

With this context, I want to open a discussion about a message channel that lets Fennec log in to accounts.firefox.com automatically when the native Account can generate the tokens to do so.  This might look like a very early message (pre-"loaded") from fxa-content-server that asks the browser context for a credential of some sort early in the loading process (possibly circumventing checking {local,session}Storage for existing credentials).  Alternatively, Fennec could also include its account state as query parameters.

This inversion of control probably violates many assumptions from fxa-content-server, so let's get talking!
stomlinson: I don't see an fxa-content-server ticket for this.  Can you comment here and file if necessary?

To be clear we don't /have/ to do this for Fennec.  But I think our story is going to be quite bad if we don't have the Android account own the local representation of the state.
nalexander: Before getting into the technical approaches, I'm glad to see this happen. I think it'll be helpful to know what problem you want to solve, what goals you have, and what constraints you are under.

From your description, here is what I gather:

* You want Fennec to use the FxA web flow instead of the purely native solution.
* Some portions of the flow may be implemented natively, so you want deep integration between Fennec and FxA.
* You want a single source of truth for user data.
* You want the ability to update user data independently of the FxA content server.
* You want Fennec services to have full read/write access to the single source of truth of user, without resorting to hacks.

I'll make no more assumptions other than those.

Some questions for you that we had to ask when doing the Fx Desktop integration:

* If the user has no Fennec based FxA account, but visits pocket.com and signs in using FxA, is Fennec's state updated automatically?
  * If not, what happens when the user visits Pocket again and Fennec's data store tells FxA no user is signed in?
  * If yes, how do we handle the scenario where a user is not yet signed in to Sync?
* If the user visits FxA's settings page and signs out, does that update Fennec's state?
  * If yes, what happens to the user's Sync session?
  * Do we do the same thing as on desktop and not give Fennec based sessions the option to sign out?
* Will Fennec be able to handle multiple accounts on a single device?
* Can Fennec query FxA via some TBD mechanism to query account state and use that instead of Fennec keeping state?
Flags: needinfo?(nalexander)
(In reply to Shane Tomlinson [:stomlinson] from comment #2)
> nalexander: Before getting into the technical approaches, I'm glad to see
> this happen.

Huzzah!  It's been a long time coming, and should really unlock the FxA team to
move faster.

 I think it'll be helpful to know what problem you want to
> solve, what goals you have, and what constraints you are under.

Aye.

> From your description, here is what I gather:
> 
> * You want Fennec to use the FxA web flow instead of the purely native
> solution.

Correct.  The only native UI we want is:

* native notifications to say that Sync is busted;
* an account status display, parallel to Desktop's Sync Preferences panel.

The native notifications drive users back into Fennec to correct the issue,
which almost always means re-authenticating.

The account status display is intensely local -- it's intended to allow you to
see the health of the account *on the current device*.  Right now it includes
"Sync leftovers" like the datatype election checkboxes and the device name; over
time, I expect that configuration to move to accounts.firefox.com/settings.
This status display also includes information about service endpoints (for self
hosters) and privacy/ToS links.  The former probably never move; the latter may
eventually be removed from the top-level UI.

> * Some portions of the flow may be implemented natively, so you want deep
> integration between Fennec and FxA.

I don't think this is correct, depending on the definition of flow.  My
definition of "flow" is what I refer to as the "sign up/sign in flow", which
starts with visiting accounts.firefox.com and ends with the "login" message.

If we're talking about this sign up/sign in flow, then I really don't want
integration in the sense of "some steps happen in Fennec".  What I want to go to
accounts.firefox.com, provide some "current state data" from Fennec, have
accounts.firefox.com do some work, and then yield some data (the "login"
message) back to Fennec.  So my diagram looks like

Fennec -> accounts -> Fennec

and not really

Fennec -> (accounts <-> Fennec) -> Fennec.

> * You want a single source of truth for user data.

Yes.  Very much so.

On Android, we have a very clear state machine that advances the Account state.
A slightly out-dated version of the diagram is at
http://people.mozilla.org/~nalexander/FxA%20client%20states.pdf.  We have a
simple-yet-robust error handling approach: in the face of errors, we drive the
account to the Separated, which asks the user for the password again, and when
it gets it re-creates a healthy internal state.  This is our get out of jail
card.  The fxa-content-server equivalent is force_auth?uid=UID; the "login"
message that comes back is what we need to re-connect.

On Desktop, this gets quite blurry.  There's an internal state machine that
kinda-sorta maintains the logged in state, but accounts.firefox.com also
maintains a logged in state (with web-layer things like sessionStorage).  It's
my opinion that Desktop suffers from race conditions and generally difficult to
diagnose situations because of this divide.  For example, Sync could disconnect
due to transient token server or auth server errors and require the password,
but accounts.firefox.com could think the user was still connected.  (The
force_auth end point is an inconsistent way to address this.)  Conversely, if
the user clears local private data in some ways, Sync could be connected but
accounts.firefox.com could think the user is not signed in (since the session
cookies have been cleared).

> * You want the ability to update user data independently of the FxA content
> server.

Let's drill into user data.  Fennec Sync happens in the background, so we must
own the account state outside of the web-layer (i.e., outside of the Gecko
process).  This is non-negotiable.

That account state is completely local to the device: it's things like
sessionToken and Sync keys.  It might include oauth tokens to hand to other
Apps, or even to websites, if we would like to make that happen.  I was going to
add service endpoints, but I'd be thrilled to have the account state machinery
query the accounts server for those endpoints.

We do not want to update user data like the user's profile independent of
accounts.firefox.com.  A big motivation for this work is to move that kind of UI
and editing support out of native code and onto the web.  That's unlocking the
FxA team to be awesome.

> * You want Fennec services to have full read/write access to the single
> source of truth of user, without resorting to hacks.

Correct.

> I'll make no more assumptions other than those.

This has already been a long response, so I'm going to field your questions
in a separate comment.

I have a division-of-responsibility diagram brewing in my head that I will
prioritize.
Flags: needinfo?(nalexander)
> Some questions for you that we had to ask when doing the Fx Desktop
> integration:
> 
> * If the user has no Fennec based FxA account, but visits pocket.com and
> signs in using FxA, is Fennec's state updated automatically?

The answer almost certainly is no.  It is not a requirement that one be using
FxA + Sync in the browser to use an oauth relier, and I can't imagine that a
user flow that starts at pocket.com and ends with turning on Sync on your device
provides a good conversion experience for *either* product.

>   * If not, what happens when the user visits Pocket again and Fennec's data
> store tells FxA no user is signed in?

Fennec knows if there is a Fennec account; it can say, "I have no account"
rather than "I have an account that is in bad standing and needs to force_auth".

A trickier case is if you use one uid to connect to a relier, then you connect
to Sync and have a Fennec native account, and then try to go to
accounts.firefox.com.  I think that should always reflect your native account
(it's managing that account!) but it will be confusing as hell.  I have no idea
what Desktop does in this scenario: perhaps there's some subtle distinction
between about:accounts and accounts.firefox.com?

>   * If yes, how do we handle the scenario where a user is not yet signed in
> to Sync?

I think I addressed this above.

> * If the user visits FxA's settings page and signs out, does that update
> Fennec's state?

My desire would be that /settings and the browser communicate to do exactly
this (presumably, with prompting).  I think Desktop's situation is confusing,
but perhaps this is again finessed with about:accounts vs. accounts.firefox.com?

>   * If yes, what happens to the user's Sync session?

I really want /settings to own the local native account; explicitly
disconnecting from accounts.firefox.com should (with prompting) disconnect Sync
and remove the local native account.

>   * Do we do the same thing as on desktop and not give Fennec based sessions
> the option to sign out?

We could, if we think that sign out is sufficiently destructive that one needs
to interact with chrome UI to take that action.  On day one, we almost certainly
don't want to expose web sign out.

In the native world, one can delete the local native account from the Android UI
directly.  In this case, I guess accounts.firefox.com would remain signed in
unless it was clever enough to realize that it was signed in and Fennec says
there is no account.  (We handle re-connecting a different account specially in
the content server already; this is similar.)

> * Will Fennec be able to handle multiple accounts on a single device?

Almost certainly not.  There's a huge engineering backload that would go into
this, and it's very complicated to message in the Android world.  We'll be one
account, tied to one profile, on Android for the foresee-able future.

> * Can Fennec query FxA via some TBD mechanism to query account state and use
> that instead of Fennec keeping state?

I don't think this is sensible, since the ability to query presumably requires
authentication tokens and it's possible to get into a state where you don't have
those tokens.  There is always some local state.  More broadly, that local state
may include Sync keys derived from the password; you never get to store those
remotely for strongly-secured users.
Nick, the link to the android state machine doesn't seem to be working for me, can you please check and re-upload if necessary?
Flags: needinfo?(nalexander)
(In reply to Ryan Kelly [:rfkelly] from comment #5)
> Nick, the link to the android state machine doesn't seem to be working for
> me, can you please check and re-upload if necessary?

http://people.mozilla.org/~nalexander/FxA%20client%20states.pdf works for me, but I've also put it at https://people.mozilla.org/~nalexander/FxA_client_states.pdf so there is no character encoding.  Let me know if it's still busted.

Be aware this is slightly out-dated -- it doesn't include the "migrated from Sync 1.1" state, for example.
Flags: needinfo?(nalexander)
>  I have no idea what Desktop does in this scenario: perhaps there's some
> subtle distinction between about:accounts and accounts.firefox.com?

For the record, there's not.  Starting at about:accounts will take you through the sync preference page, to the /settings page for whatever user is currently logged in on the web.  Right now for two, sync says I'm signed in as "ryan@rfk.id.au" but the manage button takes me to settings for a test account "ryan-two@rfk.id.au".

Hopefully whatever we do here will also lead to a solution to such nonsense in the desktop case as well.
No longer blocks: androidwebfxa
(whoops, think I accidentally messed up the bug tree there, fixing...)
As a point of comparison, I just tried the following with Chrome:

  * Sign in to chrome with my gmail account "rfkelly0"
  * Go to hamburger -> settings
  * It says I'm signed in as "rfkelly0"
  * Click the link to "Manage your synced data on Google Dashboard."
  * It opens a web page of my account management dashboard on accounts.google.com
  * Click the "sign out" button at the bottom on this webpage
  * I'm signed out on the web, and it presents a normal Google "sign in" page
  * But chrome still says I'm signed into the browser as "rfkelly0"
  * On accounts.google.com, sign in as a different user "lachlan.f.kelly"
  * Restart Chrome
  * Go to hamburger -> settings
  * It still says I'm signed in as "rfkelly0"
  * Click the link to "Manage your synced data on Google Dashboard."
  * It opens accounts.google.com and shows me the management dashboard for "lachlan.f.kelly"

I'm not saying that "it's weird on Chrome as well" should be any comfort.  I'm sure we can do better!  But it's a good reminder to consider whether some small amount of relatively safe weirdness may be a worthwhile tradeoff if it makes the common case much simpler.

It's be interesting to see how a similar flow behaves on Chrome for Android.
> There's also a desire to have accounts.firefox.com be pinned to an existing native Account's user ID;

What's the underlying driver of this desire?

> and for the user *not* be signed in to accounts.firefox.com when a native Android account
> is not present.

TBH, this sounds like an anti-goal for accounts.firefox.com as a whole.  It would make logging into web-based FxA reliers difficult-to-impossible if you're not signed in to sync.  As an example, I think this would be a perfectly reasonable scenario to want to do on a Fennec device:

  * You remember an interesting article that you want to share with me.
  * I lend you my android tablet (or iPad, or whatever) on which I'm logged in to sync.
  * You visit Pocket on the web and sign in with your FxA to dig out the link.

It seems like tying the account state on the web too strongly to the account state in Fennec would make this impossible.

I propose a slighly looser coupling than what (my read of) Comment 0 suggests.  This would not be too big a change from how fxa-content-server currently operates, and I think is still inline with what Nick and Shane have described previously.  It's predicated on the fact that we have fairly sophisticated account-state-management in fxa-content-server, in particular the ability to juggle multiple previously-seen accounts in various states of authenticated-ness.

(We don't expose it in the UI much yet, but IIUC we have many of the building blocks that would be required to build a kind of account-chooser UI similar to e.g. the Google account switcher).


In three parts:


1)  Make accounts.firefox.com treat the browser as an additional source of account state.

At page load, in addition to slurping any cached account data out of local storage, we query the browser to see if there's a currently-logged-in user.  If so then we merge that account data into our local state, ensuring that it's marked to indicate that it came from the browser.  We always treat the browser's reported state as canonical for that account, so if e.g. the browser says the user is unauthenticated then we drop any cached tokens, if the browser says there's nobody there then we discard any cached from a previous user, etc.

We have a tiny version of this already with the special "sessionTokenContext" flag on account data that originated in desktop Firefox.  We would generalize that to sync with account data from the browser on page load.

This is pretty close to what's described in the existing https://github.com/mozilla/fxa-content-server/issues/2662

Importantly, this always treats Fennec as the canonical source of truth for its account data.  We might slurp it in and cache it locally to simplify our implementation, but the browser is always the owner of that state.

This also means we can continue to offer purely-web-based logins for accounts other than the one that's logged in to the browser, by implementing our own "use a different account" flow.  Maybe in the future we could have some sort of account-picker thing, https://github.com/mozilla/fxa-content-server/issues/1844


2)  Hide and/or message out actions that change the state of the Brower's account.

We do this on desktop right now, by hiding the "sign out" link if the account is owned by desktop sync.  We could either generalize this, or implement a protocol by which we can cause the "sign out" action to be propagated back to the browser.  Hiding them is almost certainly simpler to start with.

Are there actions besides "signout" that might usefully be messaged up to the browser when performed on the native account?


3)  Let the browser coerce accounts.firefox.com into displaying a particular account

When the user triggers "about:accounts" or the equivalent on Fennec, it should pass in an explicit ?uid=FOO parameter to instruct accounts.firefox.com to display that particular account.  If necessary this would transparently do the equivalent of a "use a different account" switcheroo, using the state from the browser in (1) to load directly into the correctly-authenticated screen.  It would avoid weird scenarios like Comment 7 and Comment 9, at least in simple cases.

There will still be edge-cases here, but they don't seem too bad.  I also wonder whether malicious web content could do some sort of coerction attack with the ?uid=FOO parameter to force an unsuspecting user into a particular state on accounts.firefox.com, but can't think of any scenarios where this would be a problem.


From my admittedly-very-high-level perspective, this setup would do a decent job of satisfying everyone's needs:

* Fennec (and Desktop) retain full control over the account state used for syncing
* accounts.firefox.com retains the ability to offer web-based login flows independent of the brower's state
* It's hard for the user to view state-management weirdness when we take them from chrome-ui => web-ui
* It's incremental to the way that fxa-content-server already does its state management, and could potentially be built in several smaller pieces


Nick, thoughts?

Shane, how closely does this map to what you already had in mind here?


Also, since this is blocking p11 production release, we need to stay in tight sync on timelines for getting it done.  /cc Edwin for project management context, and ni? Nick to suggest an appropriate timeline since he has the most context on that project.
Flags: needinfo?(nalexander)
(In reply to Ryan Kelly [:rfkelly] from comment #10)

> We always treat the browser's reported state as canonical for that account, so if e.g. the browser says the user is unauthenticated then we drop any cached tokens, if the browser says there's nobody there then we discard any cached from a previous user, etc.

Note: the browser only saves a subset of the accounts that might be cached on the web (i.e. Sync, Hello vs. n-third party reliers) so we might want to be conservative and only clear accounts that are marked as imported from native. If the account is subsequently used to sign-in to a non-native relier, we might flip the state so that it's not cleared.

> 2)  Hide and/or message out actions that change the state of the Brower's account.

> We do this on desktop right now, by hiding the "sign out" link if the account is owned by desktop sync.  We could either generalize this, or implement a protocol by which we can cause the "sign out" action to be propagated back to the browser.  Hiding them is almost certainly simpler to start with.

> Are there actions besides "signout" that might usefully be messaged up to the browser when performed on the native account?

We currently message back on account deletes and password changes[1], and also profile updates[2]. There's an issue open to message back on sign out[3].

[1] https://github.com/mozilla/fxa-content-server/issues/2152
[2] https://github.com/mozilla/fxa-content-server/pull/2163
[3] https://github.com/mozilla/fxa-content-server/issues/2731

> 
> 
> Nick, thoughts?
> 
> Shane, how closely does this map to what you already had in mind here?

FWIW this is pretty much how I imagined things working. We already do (3) but without access to the native account state that (1) would provide (for desktop this kind-of works already since all sign-ins go through the web, at least until the user selects "Use a different account" and clears the cache).

This feature would also be useful for non-native reliers who want to link to the FxA settings page for a specific account, e.g. Marketplace. I doubt we'd ever let an untrusted relier seed account credentials, but if there's a need for trusted reliers to reliably link to the settings page for a specific account we might explore a solution that could accommodate native and non-native, but that's probably another can of worms.
There's valuable discussion here, but I'm going to close this in favor of Bug 1204930.
Status: NEW → RESOLVED
Closed: 9 years ago
Flags: needinfo?(nalexander)
Resolution: --- → DUPLICATE
You need to log in before you can comment on or make changes to this bug.