Skip to main content

Integration with FxA

Last updated: August 20th, 2026

Overview

Mozilla accounts integration is available for Mozilla groups on request using OpenID Connect (OIDC) and OAuth 2.0. Your service becomes a Relying Party (RP) that authenticates users through Mozilla accounts.

note

This tutorial covers integration steps. You must also meet ongoing integration requirements to maintain uninterrupted service.

What You Need to Provide

Before you can integrate, gather the following information:

ItemDescriptionRequired
OAuth Redirect URLsWhere FxA redirects after authentication (one for stage, one for production)Yes
Webhook URLsEndpoint to receive account events (password changes, deletions, etc.)Yes
Service IconDisplayed in users' Connected Services list. Confirm current specs with the FxA team.No
Technical ContactEmail for API changes and critical updatesYes
Expected TrafficRough estimate of authentication volumeYes
Integration TypeWeb app, native app, browser, API, or extensionYes

Quick Start (for experienced OAuth/OIDC developers)

If you're familiar with OAuth 2.0 and OIDC:

  1. File a Jira issue in the FXA project requesting OAuth credentials (include info from the table above)
  2. Join the mozilla-accounts-notices group for updates
  3. Use staging endpoints for development:
    • Authorization: https://accounts.stage.mozaws.net/authorization
    • Token: https://oauth.stage.mozaws.net/v1/token
    • Profile: https://profile.stage.mozaws.net/v1/profile
  4. Implement standard OIDC authorization code flow
  5. Set up webhook handler for account events
  6. Request production credentials when ready

Discovery document: https://accounts.stage.mozaws.net/.well-known/openid-configuration

How the Flow Works

Step-by-Step Integration Guide

Step 1: Request OAuth Credentials

File an issue in the FXA project in Mozilla's Jira with:

Required Information:

  • OAuth redirect URLs (stage and production)
  • Webhook notification URL
  • Integration type (web app, native app, etc.)
  • Technical contact email
  • Expected traffic estimate
  • Service icon

Tell Us About Your Needs:

  • Do you need ongoing access to profile data when users aren't logged in? (requires refresh tokens with access_type=offline)
  • Do you need to write profile data? (only avatar and displayName are writable via the API)
  • Do you need encryption keys derived from the user's password? (scoped keys - note these change if the user changes their password)
  • Will this be a subscription/paid service?
  • Will you have subscription lead/marketing pages?

Most integrations are Relying Parties (user-facing apps where users log in). If you're building an API that other services call using tokens, you may be a Resource Server - see the distinction below.

You'll receive:

  • client_id - public identifier for your service
  • client_secret - private secret for backend token exchanges (never expose publicly or commit to repositories)

Note: Request separate credentials for each platform (e.g., web, iOS, Android).

Step 2: Implement the Authorization Flow

Build the authorization URL with these parameters:

ParameterRequiredDescription
client_idYesYour OAuth client ID
scopeYesSpace-separated scopes (usually profile)
stateYesYour random state token
redirect_uriYesMust match registered redirect URL
response_typeYesUse code
code_challengeNoOnly needed if using PKCE. (SHA256 hash of verifier)
code_challenge_methodNoOnly needed if using PKCE. Only S256 is supported
access_typeRecommendedonline or offline (offline gives refresh tokens)
actionRecommendedemail or force_auth
acr_valuesNoUse AAL2 to require the user to have completed two-factor authentication. See step-up authentication
max_ageNoMaximum age in seconds of the user's last authentication. Pair with acr_values to force a fresh second-factor challenge
entrypointRecommendedMetrics identifier (coordinate with FxA team)
utm_campaignNo
utm_sourceNo
utm_mediumNo
utm_termNo

Step 3: Set Up Webhook Handler

FxA sends Security Event Tokens (SET) via webhooks when users change passwords, update profiles, change subscriptions, or delete accounts. You must handle these events.

Register Your Webhook

Create a pull request in webservices-infra (FxA can help with this if needed):

Add your client id and, optionally, webhook endpoint URL to eventbroker_endpoint_subscription_config. See example PR.

Verify and Handle Events

Webhooks are JWTs signed by FxA. Verify them using FxA's public keys from the JWKS endpoint (found via jwks_uri in the discovery document). Here is an example in typescript:

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

// Fetch and cache these at startup from the jwks_uri
let publicJwks: Array<{ kid: string; [key: string]: any }>;

// Your client_id, provided when you registered your OAuth credentials
const CLIENT_ID = 'your_client_id';

async function verifyWebhook(authHeader: string): Promise<object> {
if (!authHeader?.startsWith('Bearer ')) {
throw new Error('Invalid authorization header');
}

const token = authHeader.slice(7);
const decoded = jwt.decode(token, { complete: true });

if (!decoded || typeof decoded === 'string') {
throw new Error('Invalid token');
}

const jwk = publicJwks.find(j => j.kid === decoded.header.kid);
if (!jwk) {
throw new Error(`Unknown key ID: ${decoded.header.kid}`);
}

return jwt.verify(token, jwkToPem(jwk), {
algorithms: ['RS256'],
issuer: 'https://accounts.firefox.com',
audience: CLIENT_ID,
});
}

app.post('/webhook', async (req, res) => {
try {
const event = await verifyWebhook(req.headers.authorization);

// Handle the event based on type
// Events: password-change, profile-change, subscription-state-change, delete-user

res.status(200).send('OK');
} catch (error) {
console.error('Webhook verification failed:', error);
res.status(401).send('Unauthorized');
}
});

Important: Resource Servers must return 200 even for unknown users. See Account Events for the full list of webhook events and their payloads.

Step 4: Test on Staging

note

Use staging servers for development. The staging database is persistent. Clean up test accounts when done - if using PyFxA, use destroy_account().

Verify:

  • Users can authenticate and your app receives profile data
  • Refresh tokens work (if using access_type=offline)
  • Webhooks are received and processed correctly
  • Error cases are handled gracefully

Step 5: Create Active Accounts BigQuery Table

FxA has a process to delete accounts that have not been active for two or more years. Relying parties might have a different account retention policy. In order for FxA to bypass active accounts of an RP in the inactive account deletion process, the RP needs to maintain a table of active account uids. (The uid is the main account identifier, and the only column in the table.) The table will be created in a FxA BigQuery dataset, and the RP can access it using a GCP Service Account.

You can create this table by opening a pull request in webservices-infra to add an entry in the rp_active_accounts_clients map for the env (stage, prod). The key of the entry is your OAuth client id (one of the values you should have received in Step 1 above), and the value is one or more GCP service account address you have access to.

For example, if your OAuth client id is bfeb828c4b20236a and the service account you will be using to update the table is fxa-bq-active-accounts-updater@my-gcp-project.iam.gserviceaccount.com:

rp_active_accounts_clients = {
"ea3ca969f8c6bb0d" = [ # pragma: allowlist secret - OAuth client id
"fxa-bq-active-accounts-updater@my-gcp-project.iam.gserviceaccount.com",
]
}

(rp_active_accounts_clients is shown in its entirety in the example, but you only need to add the entry.)

When the pull request is ready, request a review in the #fxa Slack channel.

Once the table is created, the service account(s) should have read-write access to it.

Production Checklist

Before going live:

  • Request production OAuth credentials via your Jira issue
  • Use production endpoints:
    • Discovery: https://accounts.firefox.com/.well-known/openid-configuration
    • OAuth: https://oauth.accounts.firefox.com/
  • Register production webhook URL
  • Submitted your service icon
  • Joined mozilla-accounts-notices mailing list

Reference

Profile Data

Mozilla accounts stores minimal identity data:

FieldDescription
uidStable user identifier
emailUser's primary email address
additionalEmailsVerified secondary email addresses; an empty array if there are none
localeBrowser locale at account creation
displayNameOptional display name
avatarOptional profile image URL

Scopes

Most integrations need only scope=profile. For advanced scopes (Resource Servers, Sync access), see OAuth scopes documentation.

Relying Party vs Resource Server

Relying PartyResource Server
What it isUser-facing app with loginAPI accessed by other services
User sees login?YesNo
Appears in Connected Services?Yes, as distinct entryNo (shows as parent service)
Receives ALL account events (not just users who have signed in to that specific service)?NoYes
ExamplesWebsites, appsSync, Relay APIs

Most integrations are Relying Parties. Resource Servers are typically internal Mozilla services accessed via Firefox's OAuth token.