Skip to main content

Integration with FxA

Last updated: September 7th, 2023

Overview

Mozilla accounts integration is available for Mozilla groups on request. This integration is handled using OpenID Connect (OIDC), OAuth 2.0, and webhooks for authentication, authorization, and receiving events regarding FxA users. Integrations with FxA assume the role of a Relying Party (RP) and/or a Resource Server (RS) depending on the type of integration, with FxA assuming the role of an OpenID Provider.

note

This tutorial will help you integrate with Mozilla accounts but there are additional requirements a relying party is expected to fulfill and maintain. Please ensure you're in compliance with expectations to continue uninterrupted service.

Pre-Development

Before starting integration, please send a request to fxa-staff[at]mozilla.com to request a short meeting so we can all document our expectations and timelines. Please include answers to the following questions in the email:

  1. What type of Relying Party / Resource Server are you integrating? Examples would be, a web site, a native app, a browser, an API, or an extension in the browser.

    Most integrations will be a Relying Party, not a Resource Server. See below for clarification on the differences.

  2. Do you know how to implement OIDC/OAuth?

  3. Will you need read access to a user’s profile data? See available profile data.

  4. Will you need ongoing read access to a user’s profile data even if the user isn't actively logging in? If necessary, a relying party can use a refresh token to query for a user's profile data, whether or not that user is logged in to the relying party. For example, if a user changes their email address and the relying party wants to update their local database with the changes.

    A refresh token is given out if access_type=offline when making the authorization request.

  5. Will you need write access to a user’s profile data? Only the avatar and displayname can be changed remotely. See the API documentation.

  6. Will you need to access Sync data? This is likely only needed for browser integrations. Most relying parties will not need this.

  7. Will you need encryption keys to encrypt user data? Optionally, when a relying party gets their access token they can also get a stable encryption key back (this is a scoped key). This key is derived from the user's password and if the user changes or forgets their password this key will change.

  8. Will your application display its own “enter your email” form? Providing your own form can give users a tight knit experience, but you'll need to send your own top of funnel metrics if you do.

  9. Who are the stakeholders?

  10. Who can be contacted for important updates, e.g., API changes?

  11. What is the schedule and key dates? At a minimum, when will QA start, when do you want to be live?

  12. Roughly, what amount of traffic do you expect?

  13. Will your integration provide a subscription service? If it will, please describe the products your integration will provide service for.

  14. Will your integration include subscription lead pages? These are generally marketing pages that include a link to start the subscription flow.

  15. Will you utilize JWT tokens? This is rare. Learn more.

We communicate with our relying parties via the firefox-accounts-notices group. Please join this list to avoid any surprises.

OpenID Connect Integration

Relying Party vs. Resource Server

A Relying Party (RP) is an application or website that outsources its user authentication functionality to an OpenID Provider, defined as part of the OpenID specifications. The user will be prompted to login to the Relying Party with their Firefox Account, and is able to see the login as a distinct Connected Service in FxA Settings. OpenID Connect contains many specifications for a variety of identity exchange and authorization, based on the OAuth 2.0 framework of specifications.

A Relying Party may also act as a Resource Server, an OAuth 2.0 term for an API server. In that case a user will never see a direct Relying Party login and will not see a distinct row in their Connected Devices list on the FxA Settings page. Services that are accessed via Firefox (Sync, Relay, etc.) using its OAuth token are all examples of Resource Servers. Their access shows up in FxA Settings as the browser session the user is logged into.

Development

note

You are encouraged to use our staging servers to develop against. Our staging server has a persistent database and changes made there are saved. Spot testing and some new accounts are fine but you are expected to clean up any significant amount of data (eg. accounts made from automated testing). If you're using PyFxA here is an example using destroy_account().

  1. Review the OpenID Connect and OAuth 2.0 documentation.
  2. Register for staging OAuth credentials by filing an issue in the SVCSE project in Mozilla's Jira. See OAuth credentials.
  3. Your development servers should point to: https://oauth.stage.mozaws.net.
  4. User authentication follows the OpenID Connect protocol.
  5. Query parameters are set and validate when redirecting to Mozilla accounts.
  6. If you are hosting your own login form initialize and propagate the top of funnel metrics.
  7. User data and account notifications are properly handled and compliant with Firefox Account requirements.
  8. An icon suitable to display in Firefox Account’s Connected Services list has been sent to Firefox Account developers. Please confirm with Mozilla accounts what the current requirements are.
  9. If multiple Resource Servers are accessed, create a distinct token for communicating with each server, limited to only the scopes required by that server. This may mean dropping your initial access token and using a refresh token to get additional, less privileged access tokens.

Preparing for Production

  1. Update your deployment bug asking for production OAuth credentials and setup of your production webhook endpoint.
  2. Production servers point to https://oauth.accounts.firefox.com/. Additional endpoints can be discovered dynamically at https://accounts.firefox.com/.well-known/openid-configuration.
  3. Someone from the FxA team has reviewed the integration code and tested the flow.

User Authentication with OpenID Connect in a nutshell

  1. Create a state token (randomly generated and unguessable) and associate it with a local session.
  2. Send /authentication request to Mozilla accounts. Upon completion, Mozilla accounts redirects back to your app with state and code.
  3. Confirm the returned state token by comparing it with the state token associated with the local session.
  4. Exchange the code for an access token and possibly a refresh token.
  5. If you asked for scope=profile you can fetch user profile information, using the access token, from the FxA Profile Server.
  6. Associate the profile information with the local session and create an account in the local application database as needed.

OAuth Credentials

OAuth Client Credentials are needed for each application accessing FxA. For example, a website that users can login to with mobile applications for iOS and Android should request 3 sets of OAuth credentials, one for each mobile app/platform, and one for the web service.

  1. client_id - a public identifier that is used to identify your service. Can be public.
  2. client_secret - a private secret that is sent from the backend when interacting with the OAuth server. Must not be shared publicly, checked into a public repository, or bundled with compiled code.

Profile Data

Mozilla accounts only stores core identity data and associated profile information about users. Mozilla accounts does not store user data specific to relying services. Core identity data stored in Mozilla accounts includes:

  • a stable user identifier (uid)
  • the user provided email address
  • the user's locale provided by the browser during account creation
  • an optional display name
  • an optional profile image

/authorization query parameters

  1. client_id (required)
  2. scope (required). This is a space separated string. Review the list of scopes.
  3. state (required). This must be a randomly generated unguessable string.
  4. entrypoint (required). This is for metrics purposes and should represent the service making the request. This should be agreed upon by the Mozilla accounts team.
  5. email (required for self hosted email-first flow)
  6. flow_begin_time (required for self hosted email-first flow)
  7. flow_id (required for self hosted email-first flow)
  8. code_challenge (required for PKCE) This is a hash of a randomly generated string.
  9. code_challenge_method (required for PKCE) As of this writing only s256 is supported.
  10. action (suggested). This should be either email or force_auth.
  11. access_type (suggested). This should be either online or offline.
  12. utm_campaign (suggested)
  13. utm_source (suggested)
  14. utm_medium (optional)
  15. utm_term (optional)

Scopes

This will be scope=profile for most Relying Parties, but there is further documentation which integrations acting as a Resource Server should be aware of.

Webhook Events

If your integration includes an application service that stores profile information, you should create a webhook URL handler to handle Security Event Tokens (SET) for Relying Party events. These events will need to be verified using the FxA JWT keys that can be found from following the jwks_uri in the FxA well-known open-id configuration. For production, this URL is https://accounts.firefox.com/.well-known/openid-configuration.

The FxA JWT public keys should be retrieved from this URL at start-up, and used to verify the webhook JWT. The documentation on verifying a JWT for Step 1/2 are applicable to FxA JWT events.

Integrations that are acting as a Resource Server will need to respond successfully with a 200 status code even if the user referenced has not used the integration.

If you're using TypeScript, an example of verifying a JWT is shown here:

import http from 'http';
import jwt from 'jsonwebtoken';
import jwkToPem from 'jwk-to-pem';


function authenticate(request: http.IncomingMessage): object {
// Assuming this is how you retrieve your auth header.
const authHeader = request.headers.authorization;

// Require an auth header
if (!authHeader) {
throw Error('No auth header found');
}

// Extract the first portion which should be 'Bearer'
const headerType = authHeader.substr(0, authHeader.indexOf(' '));
if (headerType !== 'Bearer') {
throw Error('Invalid auth type');
}

// The remaining portion, which should be the token
const headerToken = authHeader.substr(authHeader.indexOf(' ') + 1);

// Decode the token, require it to come out ok as an object
const token = jwt.decode(headerToken, { complete: true });
if (!token || typeof token === 'string') {
throw Error('Invalid token type');
}

// Verify we have a key for this kid, this assumes that you have fetched
// the publicJwks from FxA and put both them in an Array.
const jwk = publicJwks.find(j => j.kid === token.header.kid);
if (!jwk) {
throw Error('No jwk found for this kid: ' + token.header.kid);
}
const jwkPem = jwkToPem(jwk);

// Verify the token is valid
const decoded: string | object = jwt.verify(headerToken, jwkPem, {
algorithms: ['RS256'],
});
if (!isIdToken(decoded)) {
throw Error('Invalid token format: ' + decoded);
}
// This is the JWT data itself.
return decoded;
}

Webhooks are processed from our event broker service. Currently, we emit events for password change, profile change, subscription change and delete account.

For additional documentation please reference the readme.

Register for webhooks

Once you have setup a service to receive webhook events, you can then register the webhook url by creating a pull request in cloudops-infra. To edit webhooks coming from FxA stage, you'll need to edit projects/fxa/tf/nonprod/envs/stage/resources/eventbroker.tf. To edit webhooks coming from FxA prod you'll need to edit projects/fxa/tf/prod/envs/prod/resources/eventbroker.tf. You'll need to add your client id to endpoint_topic_config, and your webhook url to endpoint_subscription_config. See an example PR.

Integrations that are acting as a Resource Server should indicate their role when having their webhook URL endpoint configured by the FxA team, this is a separate option in the webhook configuration.

Some flow diagrams

A full oauth flow