Skip to content
This repository was archived by the owner on Nov 1, 2018. It is now read-only.

oauth design

Brian Warner edited this page Feb 27, 2014 · 11 revisions

FxA Inter-Service Authentication and Delegation

This document describes the method by which Mozilla web services (called "RP"s, Relying Parties) can allow their users to "Sign In With Your Firefox Account". The RP server will receive proof that the user controls the given FxA, as well as credentials that grant it certain access to data on other servers on behalf of that user.

This uses an OAuth2 flow and a new "fxa-oauth-server" to issue and validate tokens. RPs can use these tokens to convince other servers (known as "Delegated Services") to accept their requests.

Basic Flow

The RP web page redirects the browser to a special login page on the FxA Content Server. The user then enters their email address and FxA password on this page, which verifies them and allocates a secret code, then redirects the browser back to the RP page. The code is then used by the RP backend server to verify the user's identity and obtain the OAuth token it will use for subsequent requests.

Detailed Flow

Before the user even turns on the computer, several things must happen behind the scenes. Each RP which wants to use FxA logins must be registered with the fxa-oauth-server. They provide a callback_uri, and receive a client_id and a shared client_secret.

The real login process starts when the user directs her browser at an RP like the Mozilla Marketplace. The "RP Backend Server" delivers a web page which we call the "RP Frontend Page". This includes a "Sign In With Your Firefox Account" link.

When this link is clicked, the page redirects the browser to a page on the fxa-content-server, and includes the following query arguments (which might be included in the page ahead of time, generated by JS with some XHR calls at the moment the login button is clicked, or generated by the RP server via an intermediate redirect):

  • client_id: which pre-registered RP is asking for tokens
  • redirect_uri: an RP URL to which the browser will be returned after login
  • state: a nonce used to bind the outbound redirect with the eventual return
  • scope (optional)

For example, the browser might be redirected to: https://login.accounts.firefox.org/login.html?client_id=3a903bd6a81a2cdc&redirect_uri=https%3A%2F%2Fmarketplace.mozilla.com%2Flogin2.html&state=64c4e4dcfedd9c2d25e8fe8ce017aa31

The fxa-content-server then serves a login.html page which describes the name of the RP asking for access (derived from the client_id), the sort of delegated access they're asking for (scope), and asks the user for their email address and password. The content-server page uses email+password in XHR requests to the fxa-auth-server's /account/login API endpoint to obtain a session token. It submits the session token to a new fxa-auth-server API to allocate a "precode", and forgets the session token. The page then uses an API on the new fxa-oauth-server to exchange this precode for a "code" (which is bound to the client_id and scope) and verifies that the requested redirect_uri matches the origin registered for that client. Finally, the page redirects the browser back to the RP's redirect_uri, along with several query arguments:

  • state: matches the original redirect argument, to bind them together
  • code: allocated by fxa-oauth-server

The RP backend server sees the argument-augmented redirect_uri request, checks that state matches a flow that it remembers, and extracts code. It them makes an HTTPS POST to a different fxa-oauth-server endpoint (meant for RP backend servers, rather than web browsers), which includes the following arguments:

  • client_id: same as before
  • client_secret: the shared secret, to convince fxa-oauth-server that this is the named+registered RP and not a stranger or some other registered RP (1).
  • code: extracted from the return redirect URL

The fxa-oauth-server looks up the client_id, verifies client_secret, then looks up code to make sure it is bound to the given client_id. If everything is ok, it retrieves the scope that was previously requested and removes code from the table. It then allocates a new OAuth2 "access_token", binding it to the client_id and scope. The POST response is a JSON blob with these properties:

  • access_token
  • scopes: list of scope identifiers
  • token_type: the literal string "bearer"

If client_secret or code are wrong, or the code is not bound to the right client_id, the fxa-oauth-server will return an error.

The RP receives the POST response. If fxa-oauth-server signalled and error, the client request was invalid, and the RP returns an error page that invites the user to try to log in again. If it indicates success, the RP stores at least access_token in a database, and typically links it to a new RP session token, which is then returned to the user's browser as a cookie. RP pages can then deliver the RP-session-token cookie (plus suitable CSRF defenses) to validate subsequent RP requests.

How RPs Exercise Token-Based Powers

(still WIP)

When the RP Backend Server wants to use some of the power it's been granted by the user, it makes an HTTPS-protected request to the corresponding API endpoint and includes the access_token in the request (either in a header, or a query argument, depending upon the API) (2). The Delegated Service which hosts this API endpoint submits the token to the fxa-oauth-server's "validate-token" API, which either returns a tuple of (account-id, scopes), or an error if the token is invalid. The delegated service then has enough information to decide whether to honor the RP's request or not.

Why Aren't fxa-auth-server's BrowserID Assertions Enough?

(still WIP)

The protocols defined in https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol enables basic login: specifically, a client application (in conjunction with a human who knows the account password) can use HTTPS APIs and some brief cryptographic operations to talk to the fxa-auth-server to obtain a signed BrowserID certificate. This certificate can be used to create BrowserID assertions, and these assertions can be used to convince specific RPs (named in the "Audience" of each assertion) that the bearer (i.e. the client app) rightfully has control over the numbered FxA account named in the certificate.

Delegation

But.. Privacy!

Persona/BrowserID offers several privacy and useability improvements over traditional third-party login systems. First, the use of public-key signatures allows RPs to verify certificates from IdPs without first establishing a shared HMAC secret (i.e. RPs do not have to register with the IdP). Second, the use of two separate public-key signatures allows RPs to verify assertions without revealing (to the IdP) which user is signing in.

However the close relationship between the FxA IdP (fxa-auth-server) and the Mozilla services using these logins (RPs) negates these benefits.

(the

Since BrowserID assertions

we need more than this

third-party convincing

delegated authority

web flow

Footnotes

  • (1): it might be better to use the shared secret as an HMAC key, instead of as a bearer token, but doing so is non-trivial and requires the fxa-oauth-server to store all of these secrets verbatim

  • (2): again, RP backend servers want to use a better proof-of-knowledge technique when exercising the access_token in requests to delegated services.

Clone this wiki locally