User Services/Sync/FxA Client States
FxA Client States
Firefox Account clients start in the "single" state, which means the browser profile has never been attached to a Firefox Account.
When the "sign in to firefox" UI gets far enough to obtain a "session token" and a "key fetch token", the profile is now in the "engaged" state. In this state, the client is polling the fxa-auth-server, awaiting notification that the account's email address has been verified, and then fetching the account keys. When the kA/kB keys have been obtained, the profile moves into the "married" state. The transition into "married" causes the "fxacct:onlogin" event to be published.
This will cause services like Sync to wake up, to ask the FxAccount object for encryption keys and a signed assertion, and then begins the process of synchronizing data like bookmarks and passwords. The FxAccount object interacts with the fxa-auth-server on-demand, rather than using a timer-driven schedule. It will generate Persona keypairs, ask the fxa-auth-server to sign pubkeys, and create signed assertions if and when a service asks for a signed assertion.
If at any point the FxAccount objects concludes that its session has been revoked (e.g. when it is asked for an assertion, and it calls the fxa-auth-server's /certificate/sign API, and receives a "bad session token" error), the profile is moved to the "separated" state. All transitions out of the "married" state, including this one, cause the "fxacct:onlogout" event to be published. The FxAccount code is responsible for providing UI that informs the user that they need to re-login.
If the user explicitly "signs out of Firefox", the profile moves to the "divorced" state (possibly causing an "fxacct:onlogout" event). The FxAccount UI for this state is different than for the "separated" state: instead of suggesting the user needs to sign back in (using the same email address), it mostly looks like the original "single" UI.
"divorced" and "single" differ mainly by a persistent flag (which might be a boolean, or might remember the last-used email address). This flag enables the UI to warn the user about the "all your stuff will get merged with theirs" confusion that could result when you sign in with a different address. This is not a complete solution: there are other situations that start from the "single" state which will also result in confusion, but perhaps this flag can help somewhat.
When Sync makes calls into the FxAccounts object (like getAssertion), it should treat any error it receives as transient, and should arrange to try again later (preferably using a randomized exponential backoff to avoid clobbering an overloaded server). If the error is in fact a persistent revocation, the FxAccount object will internally move itself into the "separated" state, and will notify Sync (via "fxacct:onlogout") that it should stop trying.
Any unusual errors (of the "this should never happen" variety) should move the profile into the "separated" state, from which hopefully the user can recover by signing back in. It may also be appropriate to display some UI that mentions "a weird error as occurred, maybe signing back in will help", and/or to deliver some telemetry back to our servers so we can investigate.
If a dependent service like Sync independently discovers evidence that the account may not be working properly (e.g. assertions are rejected by the Tokenserver), it should call an FxAccounts API (name TBD) that means roughly "I know you think you're in the 'married' state, but I have evidence to the contrary, do with it what you will". This API does not provide a response. The FxAccount object should then try to confirm the state of its account (either by getting a fresh signed certificate, or making some other session-status call). If this reveals that the session has been revoked, the FxAccount object moves to the "separated" state, fxacct:onlogout is posted, and the UI is updated as usual. If everything still looks fine, the FxAccount can continue in the "married" state.
Internal Details
TBD: expand on the diagram with internal states
"married": flowchart showing getAssertion() response: do we have an assertion, do we have a signed certificate, do we have a new-enough keypair, create keypair, ask fxa-auth-server to /certificate/sign, sign assertion
"engaged": verification polling, key fetching
"single": jelly-frame loading, jelly-side password handling, delivery of sessionToken/keyFetchToken to chrome-side code