-
Notifications
You must be signed in to change notification settings - Fork 107
fx account client
(note: this text will move to the Mozilla wiki once it's stable)
The Firefox Accounts system enables a "sign-into-browser" action, where a user empowers a browser to use their Firefox Account by entering an email address and password. This is currently only used by Sync, but can be extended to other client code that lives inside the browser and needs to act upon the Account.
A user agent which starts with email+password and completes the onepw protocol dance gains the ability to request BrowserID-based signed certificates. These certificates can be used to produce BrowserID-like assertions for arbitrary audiences. The assertion can then be delivered (as a bearer token) to an external relying server, which can validate it, and grant authority to the sender (typically in the form of a session token).
The successful FxA client also learns two master encryption keys: kA (for "email-recoverable" data), and kB (for non-recoverable password-locked data). Client code can derive application-specific keys from these and use them to encrypt user data.
The "FxA Account Object" manages the protocol messages and user interaction. Gecko-side client code can ask this object for assertions and keys.
We're still working on making clean interfaces to the FxA client code. The current best reference is the Sync code which uses it:
services/sync/modules/browserid_identity.js
The FxA code itself lives in services/fxaccounts/FxAccounts.jsm
.
At any time, your code may call getSignedInUser()
to learn about the currently signed-in user (if any):
Cu.import("resource://gre/modules/FxAccounts.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
"resource://gre/modules/FxAccounts.jsm");
return fxAccounts.getSignedInUser().then(accountData => {
if (!accountData) {
this._log.info("initializeWithCurrentIdentity has no user logged in");
This promise will fire quickly: it only waits to load data from disk.
If accountData
is null, then no user has signed in. Otherwise, it will be an object with the following properties:
* email: The user's email address
* uid: The user's unique id
* sessionToken: Session for the FxA server
* kA: An encryption key from the FxA server
* kB: An encryption key derived from the user's FxA password
* verified: email verification status
* authAt: The time (seconds since epoch) that this record was
* authenticated
verified
is a boolean, true
if the account's recovery email has been verified (i.e. a link in the challenge email has been clicked). getSignedInUser
will provide user data as soon as an account is configured, without waiting for it to be verified. But the client cannot get assertions or keys until verification is complete.
Your code should use uid
as the primary identifier (e.g. when indexing stored data on a server). This will generally be in the form [email protected]
, but will vary in testing and staging environments. The email
field currently contains the (exactly 1) recovery email address, but in the future, FxA may support users with multiple emails, or other recovery methods, and accounts may not have an email address at all. If you use email
, be prepared for it to change in the future.
kA
and kB
are the master encryption keys. They must not be used directly: applications should use HKDF with a suitable per-purpose "CTXinfo" field to derive application-specific keys. kA
will remain constant for the life of the account. kB
will change if the account is ever reset (i.e. the user forgets their password, and performs the email-recovery flow).
authAt
indicates the last time that the user's password was presented. Client code which initiates high-value transactions may want a "fresh" session (e.g. the password has been presented within the last 5 minutes). A future API will enable code to request this.
The sessionToken
is for internal use, and should not be used by client code. It gives the FxA code the authority to request signed certificates.
If your code were to constantly poll getSignedInUser()
, it would observe the following series of state transitions:
- null
.verified = false
-
.verified = true
but kA/kB = null -
.verified = true
, kA/kB are set
There are other functions which can be used to wait for these later states, but they are currently meant for internal use.
Once you have seen .verified = true
, you can ask for an assertion:
fxAccounts.getAssertion(audience).then(assertion => {});
This is a BrowserID -style assertion, in which the certificate has the following fields:
-
principal.email
:[email protected]
- ... (lastAuthAt? generation? email?)
It should be delivered over a secure channel to a server named by audience
. That server can then verify it like regular BrowserID assertions, except that there is a dedicated issuer. FxA provides a new verifier service for FxA assertions that knows about these rules.
The FxA code manages keypair generation, certificate signing requests, and assertion signing.
This is currently pretty ugly. Please don't use it.
When the user signs into the browser (currently driven by the "about:accounts" page and the Sync setup flow), a notification is sent to all interested observers.
// FxAccountsCommon.js doesn't use a "namespace", so create one here.
let fxAccountsCommon = {};
Cu.import("resource://gre/modules/FxAccountsCommon.js", fxAccountsCommon);
const OBSERVER_TOPICS = [
fxAccountsCommon.ONLOGIN_NOTIFICATION,
fxAccountsCommon.ONVERIFICATION_NOTIFICATION,
fxAccountsCommon.ONLOGOUT_NOTIFICATION,
];
onLogin
fires when the user has finished signing into an account. It does not mean that the email address has been verified (the return value from getSignedInUser()
will be non-null but accountData.verified
is not guaranteed to be true). You could call .whenVerified(accountData)
to get a promise that will fire when verification is complete. Account-management UI may need to distinguish between this signed-in-but-not-verified state and the fully-verified state, but regular clients should not. Regular clients should not do anything with the not-yet-verified state.
onVerification
fires when the user has finished signing into an account and that account has finished email verification. It also waits until the kA/kB encryption keys are available.
The onLogout
event fires when the user manually signs out of the browser. Currently, the UI for this is described as "disconnecting" your Sync account.
Neither onLogin
nor onVerification
will fire in a new browser session when the user finished signin and verification in a previous browser session. We do not yet have a good single tool for discovering when an account is available.
Currently, the way Sync manages this is to:
- listen for
onLogin
andonLogout
at browser startup - call
getSignedInUser()
at browser startup: if it indicates a user is signed in, it waits with.whenVerified()
, then kicks off the rest of Sync. If no user is signed in, it does nothing. - when
onLogin
fires, it callsgetSignedInUser()
and waits with.whenVerified()
, then kicks off the rest of Sync.
Note that this is (somewhat) distinct from "sign-into-the-web", for which the ...
Our plan is to replace the FxAccounts singleton with an "FxA object"...
There should be a single tool that client code can use to be notified when a fully-verified ready-to-go account is ready, that fires shortly after browser startup (for previously-established sessions) or later (when the user signs in in this browser session). A whenSignedIn()
promise would do, but folks may not be comfortable with keeping a callback chain hanging around forever, which may never get used.
This tool should return a "FxA Object" which has easy to use methods like:
- getFxAUID() -> "[email protected]"
- getRecoveryEmail()
- getKeysFor(subsystem) -> kA/kB derivatives
- getAssertionFor(audience)