Closed Bug 1577690 Opened 5 years ago Closed 5 years ago

Browser support for opting out of sync during the Firefox signin flow

Categories

(Firefox :: Firefox Accounts, enhancement)

enhancement
Not set
normal

Tracking

()

VERIFIED FIXED
Firefox 71
Tracking Status
firefox71 --- verified

People

(Reporter: rfkelly, Assigned: eoger)

References

Details

Attachments

(2 files)

As part of Bug 1570565, we will be adding signin flows to Firefox that result in the user being signed in to the browser, but do not enable sync. The current Firefox signin flow is triggered from the following URL:

https://accounts.firefox.com/?service=sync&context=fx_desktop_v3

Which is, pretty clearly, specific to Sync. We will need to adjust the FxA authentication broker to allow signing in to the browser and enabling other services without enabling Sync and without requiring sync-specific messaging in the flow.

We're still working out some of the details, but I wanted to get this on file for the purposes of tracking the work. Next steps here are to confirm the designed UX for:

  • The about:welcome signin flow, in which sync should be optional
  • The about:protections signin flow, in which sync should not be mentioned at all

Once we understand the scope of the UX we'll be able to fill in the details of the underlying protocol. I'm pretty hopeful that it won't be a significant change, but don't want to get too far ahead of the UX here.

The about:protections signin flow, in which sync should not be mentioned at all

I'm going to narrow the scope of this but to be just about making sync optional in the about:welcome flow, and spin out a separate bug for the protection-report changes.

I've started a proposal for the FxA-side changes in a corresponding github issue here:

https://github.com/mozilla/fxa/issues/2396

:markh could you please take a look at that proposal and see if it makes sense to you?

Flags: needinfo?(markh)
Summary: Implement a webchannel signin flow that doesn't enable sync → Make sync optional in the about:welcome signin flow

A note to self that, if we go the route of removing service=sync from the signin URL, we'll also need to update all the places where we check that service value post-signin, such as:

Here's a summary of the planned changes to the interactions between Firefox and accounts.firefox.com to make Sync optional.

I'm going to walk through it in what may seem like excruciating detail, so that we can use this as a reference for secreview as well as to guide implementation. But I'm not wedded to any of the particular naming conventions etc used here.

The flow for signing in to Firefox involves the following steps, where I am using "FxA" as shorthand to mean "web content running on https://accounts.firefox.com":

  1. Launch the flow. This involves opening FxA with special query parameters that trigger the behaviours below.
  2. Status Handshake. FxA sends a fxaccounts:fxa_status webchannel message to the browser, telling it about the signin flow that's about to start. The browser replies with some information about its capabilities, which can be used to customize the flow.
  3. User signin. The user enters their credentials in FxA. It sends a can_link_account webchannel message to tell the browser the claimed identity of the user, and the browser may reply telling FxA to abort the signin. We use this to show a warning if you're signing in to a browser that may contain another user's data.
  4. Choose what to sync. For new signups, FxA shows a "choose what to sync" page where the user can decide which datatypes to sync. The options displayed are based on the capabilities reported by the browser in step (2).
  5. Login complete. FxA sends a fxaccounts:login webchannel message to the browser, which contains the user's id and email, various auth tokens and keys, and the results of the choose-what-to-sync screen if any.

From there, the browser has everything it needs access the user's account and sync their data up to the cloud.

The current implementation of this flow assumes that the user is signing in to use sync. We're going to keep that flow as-is, but add some extra hooks so that the browser can opt-in to a modified flow where the user has a choice about whether or not to sync at all.

1. Launch the flow

The current flow is launched by navigating to the following URL:

We'll add support for entering this flow without the service query parameter, which will make FxA responsible for asking the user what services to enable:

2. Status Handshake

The current fxaccounts:fxa_status handshake message from FxA to the Browser contains the following fields:

fxa_status: {
  service: [the service query param, if any],
  isPairing: [whether it's trying to execute a pairing flow (which it won't be in this case)],
}

As noted in Comment 2, the browser uses this information to decide whether it will share any existing sign-in state back to web content. Since we may not be connecting for any particular service, let's have this message also echo back the context parameter, so that code like this can check for context=fx_desktop_v3 rather than service=sync. So the new message would look like:

fxa_status: {
  // As above, plus...
  context: [the context query param, if any; "fx_desktop_v3" in this case],
}

The browser responds to this message with a description of its capabilities, and information about the currently-signed-in user (if any):

{
  signedInUser: [various sign-in state info, which we're not planning to change],
  capabilities: {
    engines: [list of sync engines to show on the CWTS page],
    pairing: [whether this browser supports the pairing flow],
  },

We can modify this response to signal that this browser supports the new, sync-optional signin flow. I think we should also return the OAuth client id for Desktop Firefox in order to replace some uses of service=sync on the backend. So the new response would look like:

  signedInUser: [as above],
  clientId: "5882386c6d801776", // This is the OAuth client_id for Firefox itself
  capabilities: {
    // As above, plus...
    multiService: true,
  },

If context=fx_desktop_v3 was specified but service=sync was not, and if the browser does not report multiService=true, we should consider showing an error. Whatever the user was trying to achieve, it probably won't work out.

3. User signin

I don't believe we intend to change anything here. If the user is trying to sign in to a browser where another user was previously signed in, they'll get a wanring about possibly merging their sync data with that other user. The warning is not wrong, but it might be a bit weird for users who don't intend to sync.

Since we're only interested in signing in from about:welcome for now, I think it's safe to assume this flow will almost always be used on a new profile, and we won't expose any users to this weirdness in practice

4. Choose what to sync

If service=sync was provided in step (1), then behaviour at this step doesn't change - FxA will show the CWTS screen to new signups and will proceed immediately to step (5) for existing users.

If the service parameter was not provided, we use the new UX provided by :rfeeley. New signups see the CWTS screen with an option to "Don't sync this device", while existing users see a new screen where they have to explicitly choose whether or not to sync this device.

In the future, we'll want a way to signal that we're signing in to Firefox for some other reason, such as to view Monitor data in about:protections. The details of that are deliberately out of scope in this proposal, but the intention is that we do not show any CWTS screens in such a flow.

5. Login complete

The current fxaccounts:login webchannel message from FxA to the Browser contains the following fields (with some irrelevant ones omitted):

fxaccounts:login {
  data: {
    // User profile data.
    uid,
    email,
    // Authentication tokens
    sessionToken,
    keyFetchToken,
    unwrapBKey,
    // Data from CWTS, if shown
    offeredSyncEngines: ["bookmarks", "history", "passwords"],
    declinedSyncEngines: ["passwords"],
  }
}

For browsers that do not report {multiService: true} in their capabilities handshake, this message will be unmodified. Such browsers are assumed to always be signing in to Sync.

For browsers that do report {multiService: true} in their capabilities handshake, we will move the CWTS data into a new services key, like this:

fxaccounts:login {
  data: {
    // User profile data, as above.
    // Authentication tokens, as above.
    // What services the user asked us to enable, and any configuration info for them.
    services: {
      // In this case, the user asked to enable sync.
      sync: {
        offeredEngines: ["bookmarks", "history", "passwords"],
        declinedEngines: ["passwords"],
      }
    }
  }
}

If the services object contains a sync key, then the user opted-in to sync (either explicitly, or by starting the flow with service=sync in the query params). If it does not, then the user opted out of sync and it should not be enabled in the browser.

Note that even if the user opts out of sync, the browser is still capable of syncing at this point - the authentication tokens and key material provided in the fxacounts:login message give it essentially full access to any data in the user's account. It's up to the browser to respect the user's choice.

Open questions

How should the browser behave if it receives an fxaccounts:login message when a user is already signed in? This can happen for legitimate reasons, e.g. if the user changes or resets their password on the web in a signed-in browser. We'll need to be careful that we don't accidentally enable or disable sync in this case.

Flags: needinfo?(markh)

Mark, Vlad, does the above match your expectations? As I said, I'm not wedded to any names etc here, but wanted to capture the changes to the flow so there's something concrete to discuss as part of the review in Bug 1570572.

Flags: needinfo?(vlad)
Flags: needinfo?(markh)

Yes, that sounds great.

How should the browser behave if it receives an fxaccounts:login message when a user is already signed in?
This can happen for legitimate reasons, e.g. if the user changes or resets their password on the web in a signed-in browser.

Can we assume that in those legitimate reasons there will be no/an empty services object? If so, I guess the browser could use that - although that still means FxA could remotely enable sync, which could be considered either a feature or not :) So another option is simply that if FxA is already configured when we get that message, we refuse to even look at services? I've no strong opinion here.

Flags: needinfo?(markh)

Can we assume that in those legitimate reasons there will be no/an empty services object?

Not sending services in this case seems reasonable to me.

I'd like to add one additional requirement here, which is that we signal to the Fxa server backend whether or not the browser has sync enabled. Previously the backend could tell this by the presence of service=sync in the login request, and we use that for a variety of purposes listed here.

We can re-work most of those without any help from the browser, but the one piece for which we do need the browser's help is in tracking sync activity metrics. Short-term I'd like to propose that we take advantage of the existing logic in the /certificate/sign route which takes an optional service query parameter. When the browser calls signCertificate currently it does not include a service parameter. We could have it include ?service=sync in such calls when sync is enabled, and omit the service parameter when it is not.

We'll have some followup work to do around enabling other services in the browser, but I expect we'll be able to derive those metrics from OAuth activity rather than /certificate/sign.

(You might ask: why don't we get sync activity metrics out of the sync server, rather than inferring them from what happens on the FxA server? Great question! We should move to such a world longer-term. But we haven't built the infrastructure for that currently).

Blocks: 1581709

I'd like to add one additional requirement here, which is that we signal to the Fxa server backend whether or not the browser has sync enabled.

After slack conversation with :markh, I've spun out a separate issue for this: Bug 1581709.

Sounds good!

Flags: needinfo?(vlad)

As discussed in today's meeting, there's value in separating out the "infrastructural" work of supporting the new sync-optional signin flow from the user-facing work of actually using the new flow from about:welcome. I've filed Bug 1582002 to be specifically about "enable the sync-optional flow from about:welcome" and am changing the title of this bug to reflect that it's about the infrastructural changes to the login flow.

Thus, this bug depends on the corresponding FxA-side changes from https://github.com/mozilla/fxa/issues/2396, and encompasses the following work:

  • [ ] Changing the handling of fxa_status webchannel message, so that we decide whether to report signedInUser based on the context parameter rather than the service param.
  • [ ] Changing the fxa_status webchannel response to include the OAuth client_id and multiService capability
  • [ ] Changing handling of the login webchannel message to:
    • [ ] Only enable sync if there is a sync key in the services field
    • [ ] Obtain the user's CWTS selections from said sync key intead of from top-level fields in the message
    • [ ] Not enable/disable sync if we receive a login message when we're already signed in
Summary: Make sync optional in the about:welcome signin flow → Browser support for opting out of sync during the Firefox signin flow
Blocks: 1582002

Vlad and I had a good slack discussion about handling the case where the user is already signed in to the browser, looping back to the bug here to continue it.

How should the browser behave if it receives an fxaccounts:login message when a user is already signed in?
This can happen for legitimate reasons, e.g. if the user changes or resets their password on the web in a signed-in browser.

I was mistaken about the password-change case here; that's actually handle by a different "fxaccounts:change_password" notification so we don't need to worry about any changes on their front.

However, there are two cases where the browser might receive "fxaccounts:login" when it already has a user signed in:

  1. If the user navigates to https://accounts.firefox.com/signin?context=fx_desktop_v3 and does a fresh sign-in
  2. If the user's session gets disconnected, and the browser guides them through a "force auth" flow

Can anyone think of any others?

For case (1) I think it's reasonable to treat this like any other signin, using the services field values selected by the new user. But perhaps there are some subtleties there around handing merging of data that we should take into consideration?

For case (2) I think FxA should avoid showing any choose-what-to-sync or choose-whether-to-sync screens in the force-auth flow. It doesn't seem necessary to allow for users trying to enable or disable services when signing back in to a disconnected Firefox, especially since we don't actually have any other services at this stage. So I propose that the browser should launch the force-auth flow with context=fx_desktop_v3 and no service parameter, FxA should return an "fxaccounts:login" message with no services field, and the browser should leave the existing user's service selections alone.

There may be more complexities here though; :eoger, care to weigh in?

Assignee: nobody → eoger
Depends on: 1582837
No longer depends on: 1582837
Depends on: 1584340
Pushed by eoger@mozilla.com:
https://hg.mozilla.org/integration/autoland/rev/5f0064458765
WebChannel support for optional Sync. r=vladikoff
Status: NEW → RESOLVED
Closed: 5 years ago
Resolution: --- → FIXED
Target Milestone: --- → Firefox 71
Depends on: 1587450

The following expected results were verified using Windows Pro 10 64-bit and macOS Catalina 10.15 on FF 71.0b7 (20191104135555):
From about:welcome the users reach a Sign in to Continue to Firefox
In the Create account flow the users reach the CWTS (choose what to sync screen) where they can Enable sync for multiple components or choose not to Sync.
All users now have a "Don't sync this device" option which, if clicked, continues the sign in flow and with display in about:preferences#sync as Sync OFF.
There is no "back" button from this screen, but it the browser back button can be used.
The URL for the create account/ sign in from about:welcome has been updated to "https://accounts.firefox.com/?context=fx_desktop_v3".
In the scenario in which the account is logged in multiple devices and a password change is performed on one of them, through a force_auth flow in which the user has to choose again to enable to not sync.

Status: RESOLVED → VERIFIED
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: