Skip to content

Deploying Firefox Account Server

Introduction

Firefox Account Server (FxA) is Mozilla’s robust, production-ready authentication and identity service that powers Firefox Sync, Mozilla VPN, and other Mozilla services. As a self-hosted solution, Firefox Account Server provides enterprise-grade authentication, OAuth 2.0 integration, two-factor authentication, and a complete user identity management system with the privacy and control of running on your own infrastructure.

Firefox Account Server delivers powerful identity management capabilities:

  • OAuth 2.0 Provider: Complete OAuth 2.0 and OpenID Connect implementation for single sign-on
  • Firefox Sync Support: Enable browser synchronization for bookmarks, passwords, history, and tabs
  • Two-Factor Authentication: Built-in TOTP support for enhanced security
  • Email Verification: Automatic email verification workflows with customizable templates
  • Session Management: Sophisticated session tracking and device management
  • API-First Architecture: RESTful API for integration with custom applications
  • Account Recovery: Secure account recovery flows with email-based verification
  • Profile Management: User profile data storage with avatar support
  • Security Events: Comprehensive audit logging and security event tracking
  • Rate Limiting: Built-in protection against brute force and abuse
  • Multi-Language Support: Internationalization support for global deployments
  • Webhook Integration: Real-time notifications for account events

Whether you need centralized authentication for internal tools, want to provide Firefox Sync for your organization, or require a privacy-focused OAuth provider, Firefox Account Server delivers a battle-tested solution with Mozilla’s security standards.

This comprehensive guide walks you through deploying Firefox Account Server on Klutch.sh using Docker, including MySQL and Redis configuration, OAuth setup, environment variables, security best practices, and production deployment strategies.


Prerequisites

Before you begin, ensure you have the following:

  • A Klutch.sh account
  • A GitHub account with a repository for your project
  • Docker installed locally for testing (optional but recommended)
  • Basic understanding of OAuth 2.0 and authentication systems
  • Access to MySQL and Redis instances (deploy separately on Klutch.sh - see MySQL and Redis guides)
  • SMTP server credentials for sending verification emails

Architecture Overview

Firefox Account Server requires several components working together:

  1. FxA Server: The main authentication service (Node.js application)
  2. MySQL Database: Stores user accounts, sessions, and authentication tokens
  3. Redis Cache: Handles session caching and rate limiting
  4. SMTP Server: Sends verification and notification emails

Deployment Strategy: Deploy each component as a separate app on Klutch.sh:

  • MySQL database with persistent volume (see MySQL guide)
  • Redis cache with persistent volume (see Redis guide)
  • Firefox Account Server (this guide)

Installation and Setup

Step 1: Create Your Project Directory

First, create a new directory for your Firefox Account Server deployment:

Terminal window
mkdir firefox-accounts-klutch
cd firefox-accounts-klutch
git init

Step 2: Create the Dockerfile

Create a Dockerfile in your project root. Firefox Account Server requires Node.js and specific configuration:

FROM node:18-alpine
# Install system dependencies
RUN apk add --no-cache \
git \
python3 \
make \
g++ \
cairo-dev \
jpeg-dev \
pango-dev \
giflib-dev
# Set working directory
WORKDIR /app
# Clone Firefox Account Server repository
RUN git clone --depth 1 --branch v1.232.0 https://github.com/mozilla/fxa.git .
# Install dependencies for the auth server
WORKDIR /app/packages/fxa-auth-server
RUN npm ci --production
# Create configuration directory
RUN mkdir -p /app/config
# Expose the default FxA port
EXPOSE 9000
# Set environment variables
ENV NODE_ENV=production \
PORT=9000 \
HOST=0.0.0.0
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD node -e "require('http').get('http://localhost:9000/__heartbeat__', (res) => { process.exit(res.statusCode === 200 ? 0 : 1); })"
# Start the auth server
CMD ["node", "bin/key_server.js"]

Step 3: Advanced Production Dockerfile

For production deployments with custom configuration and optimization:

FROM node:18-alpine AS builder
# Install build dependencies
RUN apk add --no-cache \
git \
python3 \
make \
g++ \
cairo-dev \
jpeg-dev \
pango-dev \
giflib-dev
WORKDIR /app
# Clone specific version of FxA
ARG FXA_VERSION=v1.232.0
RUN git clone --depth 1 --branch ${FXA_VERSION} https://github.com/mozilla/fxa.git .
# Build auth server
WORKDIR /app/packages/fxa-auth-server
RUN npm ci --production && \
npm cache clean --force
# Production stage
FROM node:18-alpine
# Install runtime dependencies only
RUN apk add --no-cache \
cairo \
jpeg \
pango \
giflib \
tini
WORKDIR /app
# Copy built application
COPY --from=builder /app/packages/fxa-auth-server /app
# Create non-root user
RUN addgroup -g 1001 fxa && \
adduser -D -u 1001 -G fxa fxa && \
chown -R fxa:fxa /app
USER fxa
# Expose port
EXPOSE 9000
# Environment defaults
ENV NODE_ENV=production \
PORT=9000 \
HOST=0.0.0.0 \
LOG_LEVEL=info
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD node -e "require('http').get('http://localhost:9000/__heartbeat__', (res) => { process.exit(res.statusCode === 200 ? 0 : 1); })"
# Use tini for proper signal handling
ENTRYPOINT ["/sbin/tini", "--"]
# Start server
CMD ["node", "bin/key_server.js"]

Step 4: Create Configuration File Template

Create a config/production.json file for custom configuration:

{
"publicUrl": "https://example-app.klutch.sh",
"contentUrl": "https://example-app.klutch.sh",
"listen": {
"host": "0.0.0.0",
"port": 9000
},
"mysql": {
"host": "your-mysql-app.klutch.sh",
"port": 8000,
"database": "fxa",
"user": "fxa_user",
"password": "your-secure-password",
"connectionLimit": 10,
"timezone": "UTC"
},
"redis": {
"host": "your-redis-app.klutch.sh",
"port": 8000,
"password": "your-redis-password",
"db": 0,
"keyPrefix": "fxa:"
},
"smtp": {
"host": "smtp.example.com",
"port": 587,
"secure": false,
"auth": {
"user": "noreply@example.com",
"pass": "your-smtp-password"
}
},
"emailService": {
"from": "Firefox Accounts <noreply@example.com>",
"verificationUrl": "https://example-app.klutch.sh/verify",
"supportUrl": "https://support.example.com"
},
"oauth": {
"secretKey": "generate-a-random-32-byte-hex-string",
"clientIds": {
"your-app": "your-app-client-id"
}
},
"securityHistory": {
"enabled": true,
"ipProfiling": {
"enabled": true,
"threshold": 0.95
}
},
"signinConfirmation": {
"enabled": true,
"forcedEmailAddresses": ["suspicious-pattern@example.com"]
}
}

Step 5: Create Environment Variables Template

Create a .env.example file to document required environment variables:

Terminal window
# Application URLs
PUBLIC_URL=https://example-app.klutch.sh
CONTENT_URL=https://example-app.klutch.sh
# Server Configuration
NODE_ENV=production
PORT=9000
HOST=0.0.0.0
LOG_LEVEL=info
# MySQL Database (Deploy separately on Klutch.sh)
MYSQL_HOST=your-mysql-app.klutch.sh
MYSQL_PORT=8000
MYSQL_DATABASE=fxa
MYSQL_USER=fxa_user
MYSQL_PASSWORD=your-secure-password
# Redis Cache (Deploy separately on Klutch.sh)
REDIS_HOST=your-redis-app.klutch.sh
REDIS_PORT=8000
REDIS_PASSWORD=your-redis-password
# SMTP Email Configuration
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=noreply@example.com
SMTP_PASSWORD=your-smtp-password
EMAIL_FROM=Firefox Accounts <noreply@example.com>
# OAuth and Security
OAUTH_SECRET_KEY=generate-a-random-32-byte-hex-string-here
SESSION_SECRET=another-random-32-byte-hex-string
VERIFICATION_SECRET=third-random-32-byte-hex-string
# Feature Flags
SIGNIN_CONFIRMATION_ENABLED=true
SECURITY_HISTORY_ENABLED=true
TOTP_ENABLED=true
# Rate Limiting
RATE_LIMIT_ENABLED=true
MAX_VERIFY_CODES=3
MAX_FAILED_LOGIN_ATTEMPTS=5
# Account Settings
ACCOUNT_DELETION_ENABLED=true
PROFILE_IMAGES_ENABLED=true
EMAIL_CHANGE_ENABLED=true

Security Note: Never commit actual credentials to your repository. Use these as templates and configure actual values in the Klutch.sh dashboard.

Step 6: Create Database Schema

Create a schema.sql file to initialize your MySQL database:

-- Firefox Account Server Database Schema
CREATE DATABASE IF NOT EXISTS fxa CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE fxa;
-- Accounts table
CREATE TABLE IF NOT EXISTS accounts (
uid BINARY(16) PRIMARY KEY,
normalizedEmail VARCHAR(255) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL,
emailCode BINARY(16) NOT NULL,
emailVerified TINYINT(1) NOT NULL DEFAULT 0,
kA BINARY(32) NOT NULL,
wrapWrapKb BINARY(32) NOT NULL,
authSalt BINARY(32) NOT NULL,
verifierVersion TINYINT UNSIGNED NOT NULL,
verifyHash BINARY(32) NOT NULL,
verifierSetAt BIGINT UNSIGNED NOT NULL,
createdAt BIGINT UNSIGNED NOT NULL,
locale VARCHAR(255),
lockedAt BIGINT UNSIGNED DEFAULT NULL,
INDEX idx_email (normalizedEmail),
INDEX idx_created (createdAt)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Session tokens
CREATE TABLE IF NOT EXISTS sessionTokens (
tokenId BINARY(32) PRIMARY KEY,
tokenData BINARY(32) NOT NULL,
uid BINARY(16) NOT NULL,
createdAt BIGINT UNSIGNED NOT NULL,
uaBrowser VARCHAR(255),
uaBrowserVersion VARCHAR(255),
uaOS VARCHAR(255),
uaOSVersion VARCHAR(255),
uaDeviceType VARCHAR(255),
lastAccessTime BIGINT UNSIGNED NOT NULL,
INDEX idx_uid (uid),
FOREIGN KEY (uid) REFERENCES accounts(uid) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- OAuth tokens
CREATE TABLE IF NOT EXISTS oauthTokens (
tokenId BINARY(32) PRIMARY KEY,
clientId BINARY(8) NOT NULL,
userId BINARY(16) NOT NULL,
email VARCHAR(255) NOT NULL,
scope VARCHAR(255) NOT NULL,
createdAt BIGINT UNSIGNED NOT NULL,
expiresAt BIGINT UNSIGNED NOT NULL,
INDEX idx_user (userId),
FOREIGN KEY (userId) REFERENCES accounts(uid) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Security events
CREATE TABLE IF NOT EXISTS securityEvents (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
uid BINARY(16) NOT NULL,
tokenId BINARY(32),
nameId VARCHAR(255) NOT NULL,
verified TINYINT(1) NOT NULL DEFAULT 0,
createdAt BIGINT UNSIGNED NOT NULL,
INDEX idx_uid_created (uid, createdAt),
FOREIGN KEY (uid) REFERENCES accounts(uid) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Email bounces
CREATE TABLE IF NOT EXISTS emailBounces (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
bounceType VARCHAR(50) NOT NULL,
bounceSubType VARCHAR(50),
createdAt BIGINT UNSIGNED NOT NULL,
INDEX idx_email (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Account reset tokens
CREATE TABLE IF NOT EXISTS accountResetTokens (
tokenId BINARY(32) PRIMARY KEY,
tokenData BINARY(32) NOT NULL,
uid BINARY(16) NOT NULL,
createdAt BIGINT UNSIGNED NOT NULL,
INDEX idx_uid (uid),
FOREIGN KEY (uid) REFERENCES accounts(uid) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Password forgot tokens
CREATE TABLE IF NOT EXISTS passwordForgotTokens (
tokenId BINARY(32) PRIMARY KEY,
tokenData BINARY(32) NOT NULL,
uid BINARY(16) NOT NULL,
passCode BINARY(16) NOT NULL,
createdAt BIGINT UNSIGNED NOT NULL,
tries TINYINT NOT NULL DEFAULT 0,
INDEX idx_uid (uid),
FOREIGN KEY (uid) REFERENCES accounts(uid) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Password change tokens
CREATE TABLE IF NOT EXISTS passwordChangeTokens (
tokenId BINARY(32) PRIMARY KEY,
tokenData BINARY(32) NOT NULL,
uid BINARY(16) NOT NULL,
createdAt BIGINT UNSIGNED NOT NULL,
INDEX idx_uid (uid),
FOREIGN KEY (uid) REFERENCES accounts(uid) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Device metadata
CREATE TABLE IF NOT EXISTS devices (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
uid BINARY(16) NOT NULL,
sessionTokenId BINARY(32) NOT NULL,
name VARCHAR(255),
type VARCHAR(50),
createdAt BIGINT UNSIGNED NOT NULL,
callbackURL VARCHAR(255),
callbackPublicKey VARCHAR(255),
callbackAuthKey VARCHAR(255),
callbackIsExpired TINYINT(1) NOT NULL DEFAULT 0,
INDEX idx_uid (uid),
INDEX idx_session (sessionTokenId),
FOREIGN KEY (uid) REFERENCES accounts(uid) ON DELETE CASCADE,
FOREIGN KEY (sessionTokenId) REFERENCES sessionTokens(tokenId) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- TOTP tokens
CREATE TABLE IF NOT EXISTS totpTokens (
uid BINARY(16) PRIMARY KEY,
sharedSecret VARCHAR(255) NOT NULL,
epoch BIGINT UNSIGNED NOT NULL,
createdAt BIGINT UNSIGNED NOT NULL,
verified TINYINT(1) NOT NULL DEFAULT 0,
enabled TINYINT(1) NOT NULL DEFAULT 0,
FOREIGN KEY (uid) REFERENCES accounts(uid) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Recovery codes
CREATE TABLE IF NOT EXISTS recoveryCodes (
uid BINARY(16) NOT NULL,
codeHash BINARY(32) NOT NULL,
INDEX idx_uid (uid),
PRIMARY KEY (uid, codeHash),
FOREIGN KEY (uid) REFERENCES accounts(uid) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Step 7: Create README Documentation

Create a README.md file with deployment instructions:

# Firefox Account Server Deployment on Klutch.sh
## About
Self-hosted Firefox Account Server providing OAuth 2.0 authentication, Firefox Sync, and identity management.
## Prerequisites
Before deploying FxA, you need:
1. **MySQL Database** - Deploy separately on Klutch.sh
- See the [MySQL deployment guide](/guides/databases/mysql)
- Create database: `fxa`
- Run the schema.sql file to initialize tables
2. **Redis Cache** - Deploy separately on Klutch.sh
- See the [Redis deployment guide](/guides/databases/redis)
- Enable persistence for session data
3. **SMTP Server** - For sending verification emails
- Any SMTP provider (SendGrid, Mailgun, AWS SES, etc.)
- Configure credentials in environment variables
## Initial Setup
After deployment:
1. Initialize the MySQL database with `schema.sql`
2. Configure environment variables in Klutch.sh dashboard
3. Access your FxA instance at your app URL
4. Create admin account via API
5. Configure OAuth clients for your applications
## Security Recommendations
- Use strong, unique secrets for OAuth and sessions
- Enable HTTPS (automatic on Klutch.sh)
- Configure rate limiting to prevent abuse
- Enable two-factor authentication for admin accounts
- Regular database backups (see MySQL guide)
- Monitor security event logs
- Implement IP whitelisting for admin endpoints
## API Endpoints
- `POST /v1/account/create` - Create new account
- `POST /v1/account/login` - Login to account
- `POST /v1/certificate/sign` - Sign certificate for Firefox Sync
- `GET /v1/account/profile` - Get account profile
- `POST /v1/password/forgot/send_code` - Send password reset code
- `GET /.well-known/openid-configuration` - OAuth configuration
## Testing
Test your deployment:
```bash
# Health check
curl https://example-app.klutch.sh/__heartbeat__
# Create test account
curl -X POST https://example-app.klutch.sh/v1/account/create \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"authPW": "your-hashed-password"
}'
### Step 8: Test Locally (Optional)
Before deploying to Klutch.sh, test your setup locally:
```bash
# Build the Docker image
docker build -t firefox-accounts .
# Create a network for containers
docker network create fxa-network
# Start MySQL (for testing - use Klutch.sh deployment in production)
docker run -d \
--name mysql-test \
--network fxa-network \
-e MYSQL_ROOT_PASSWORD=rootpassword \
-e MYSQL_DATABASE=fxa \
-e MYSQL_USER=fxa_user \
-e MYSQL_PASSWORD=fxapassword \
mysql:8.0
# Start Redis (for testing - use Klutch.sh deployment in production)
docker run -d \
--name redis-test \
--network fxa-network \
redis:7-alpine redis-server --requirepass redispassword
# Wait for databases to initialize
sleep 10
# Run FxA server
docker run -d \
--name fxa-test \
--network fxa-network \
-p 9000:9000 \
-e MYSQL_HOST=mysql-test \
-e MYSQL_PORT=3306 \
-e MYSQL_USER=fxa_user \
-e MYSQL_PASSWORD=fxapassword \
-e REDIS_HOST=redis-test \
-e REDIS_PORT=6379 \
-e REDIS_PASSWORD=redispassword \
-e PUBLIC_URL=http://localhost:9000 \
firefox-accounts
# View logs
docker logs -f fxa-test
# Test the server
curl http://localhost:9000/__heartbeat__
# Cleanup when done
docker stop fxa-test redis-test mysql-test
docker rm fxa-test redis-test mysql-test
docker network rm fxa-network

Step 9: Push to GitHub

Commit your files and push to your GitHub repository:

Terminal window
git add Dockerfile config/ schema.sql .env.example README.md
git commit -m "Add Firefox Account Server deployment configuration"
git remote add origin https://github.com/yourusername/firefox-accounts-klutch.git
git branch -M main
git push -u origin main

Deploying to Klutch.sh

Now that your Firefox Account Server project is ready and pushed to GitHub, follow these steps to deploy it on Klutch.sh.

Deployment Steps

    1. Deploy Dependencies First

      Before deploying FxA, set up the required services:

      MySQL Database:

      • Follow the MySQL deployment guide
      • Create a database named fxa
      • Execute the schema.sql file to initialize tables
      • Note the connection URL: your-mysql-app.klutch.sh:8000

      Redis Cache:

      • Follow the Redis deployment guide
      • Enable password protection
      • Enable persistence (AOF or RDB)
      • Note the connection URL: your-redis-app.klutch.sh:8000
    2. Log in to Klutch.sh

      Navigate to klutch.sh/app and sign in to your account.

    3. Create a New Project

      Click on “Create Project” and give your project a meaningful name (e.g., “Firefox Account Server”).

    4. Create a New App

      Click on “Create App” within your project and configure the following settings.

    5. Select Your Repository

      • Choose GitHub as your Git source
      • Select the repository containing your Dockerfile
      • Choose the branch you want to deploy (usually main or master)
    6. Configure Traffic Type

      • Traffic Type: Select HTTP (FxA serves a web API via HTTP)
      • Internal Port: Set to 9000 (the default port Firefox Account Server listens on)
    7. Configure Environment Variables

      Add the following environment variables in the Klutch.sh dashboard (replace with your actual values):

      PUBLIC_URL=https://your-fxa-app.klutch.sh
      CONTENT_URL=https://your-fxa-app.klutch.sh
      NODE_ENV=production
      PORT=9000
      HOST=0.0.0.0
      LOG_LEVEL=info
      MYSQL_HOST=your-mysql-app.klutch.sh
      MYSQL_PORT=8000
      MYSQL_DATABASE=fxa
      MYSQL_USER=fxa_user
      MYSQL_PASSWORD=your-secure-mysql-password
      REDIS_HOST=your-redis-app.klutch.sh
      REDIS_PORT=8000
      REDIS_PASSWORD=your-secure-redis-password
      SMTP_HOST=smtp.example.com
      SMTP_PORT=587
      SMTP_USER=noreply@example.com
      SMTP_PASSWORD=your-smtp-password
      EMAIL_FROM=Firefox Accounts <noreply@example.com>
      OAUTH_SECRET_KEY=generate-32-byte-hex-string
      SESSION_SECRET=generate-32-byte-hex-string
      VERIFICATION_SECRET=generate-32-byte-hex-string
      SIGNIN_CONFIRMATION_ENABLED=true
      SECURITY_HISTORY_ENABLED=true
      TOTP_ENABLED=true

      Generate secure secrets using:

      Terminal window
      node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
    8. Attach Persistent Volume (Optional)

      If you want to store logs or temporary files:

      • Navigate to the “Volumes” section in your app settings
      • Click “Add Volume”
      • Mount Path: Enter /app/logs
      • Size: Allocate 5GB for logs
      • Click “Add” to attach the volume
    9. Deploy Your Application

      Click “Deploy” to start the deployment process. Klutch.sh will automatically:

      • Detect your Dockerfile in the repository root
      • Build the Docker image with Node.js and FxA
      • Deploy the container with the configured settings
      • Map the internal port 9000 to a public URL
    10. Monitor Deployment

      Watch the build logs in the Klutch.sh dashboard to ensure your deployment completes successfully. The initial build may take 5-10 minutes as it downloads dependencies and builds the FxA server.


Post-Deployment Configuration

After your Firefox Account Server is deployed and running, complete these essential setup steps:

Step 1: Verify Server Health

Check that all components are running correctly:

Terminal window
# Health check endpoint
curl https://your-fxa-app.klutch.sh/__heartbeat__
# Expected response: {"ok":true}
# Version endpoint
curl https://your-fxa-app.klutch.sh/__version__
# OpenID Connect configuration
curl https://your-fxa-app.klutch.sh/.well-known/openid-configuration

Step 2: Initialize Database

Connect to your MySQL database and verify the schema:

Terminal window
# Connect to MySQL (use your actual connection details)
mysql -h your-mysql-app.klutch.sh -P 8000 -u fxa_user -p
# Verify tables were created
USE fxa;
SHOW TABLES;
# Check accounts table structure
DESCRIBE accounts;

Step 3: Create First User Account

Create a test account via API:

Terminal window
# Generate auth password (simplified example)
AUTH_PW=$(echo -n "your-password" | openssl dgst -sha256 -binary | xxd -p)
# Create account
curl -X POST https://your-fxa-app.klutch.sh/v1/account/create \
-H "Content-Type: application/json" \
-d "{
\"email\": \"admin@example.com\",
\"authPW\": \"${AUTH_PW}\"
}"

Step 4: Configure OAuth Clients

Register your applications as OAuth clients. Create a clients.json file:

{
"clients": [
{
"id": "your-app-client-id",
"name": "Your Application",
"imageUri": "https://your-app.com/logo.png",
"redirectUri": "https://your-app.com/oauth/callback",
"trusted": true,
"canGrant": true,
"publicClient": false,
"termsUri": "https://your-app.com/terms",
"privacyUri": "https://your-app.com/privacy"
}
]
}

Register clients via the admin API or database insert.

Step 5: Test Authentication Flow

Test the complete OAuth 2.0 flow:

Terminal window
# 1. Initiate OAuth authorization
curl -X GET "https://your-fxa-app.klutch.sh/v1/authorization?client_id=your-client-id&response_type=code&state=random-state&scope=profile+openid"
# 2. Login and get authorization code
# (Browser interaction required)
# 3. Exchange code for access token
curl -X POST https://your-fxa-app.klutch.sh/v1/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "authorization_code",
"code": "authorization-code",
"client_id": "your-client-id",
"client_secret": "your-client-secret"
}'
# 4. Use access token to get profile
curl https://your-fxa-app.klutch.sh/v1/account/profile \
-H "Authorization: Bearer your-access-token"

Integration Examples

Node.js Application Integration

Integrate FxA authentication in a Node.js application:

fxa-client.js
const axios = require('axios');
class FxAClient {
constructor(config) {
this.baseURL = config.baseURL || 'https://your-fxa-app.klutch.sh';
this.clientId = config.clientId;
this.clientSecret = config.clientSecret;
this.redirectUri = config.redirectUri;
}
// Generate authorization URL
getAuthorizationUrl(state, scope = 'profile openid') {
const params = new URLSearchParams({
client_id: this.clientId,
response_type: 'code',
state: state,
scope: scope,
redirect_uri: this.redirectUri
});
return `${this.baseURL}/v1/authorization?${params.toString()}`;
}
// Exchange authorization code for access token
async getAccessToken(code) {
try {
const response = await axios.post(`${this.baseURL}/v1/token`, {
grant_type: 'authorization_code',
code: code,
client_id: this.clientId,
client_secret: this.clientSecret,
redirect_uri: this.redirectUri
});
return response.data;
} catch (error) {
throw new Error(`Token exchange failed: ${error.message}`);
}
}
// Get user profile
async getUserProfile(accessToken) {
try {
const response = await axios.get(`${this.baseURL}/v1/account/profile`, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
return response.data;
} catch (error) {
throw new Error(`Profile fetch failed: ${error.message}`);
}
}
// Verify email
async verifyEmail(uid, code) {
try {
const response = await axios.post(`${this.baseURL}/v1/recovery_email/verify_code`, {
uid: uid,
code: code
});
return response.data;
} catch (error) {
throw new Error(`Email verification failed: ${error.message}`);
}
}
// Logout and destroy session
async logout(accessToken) {
try {
await axios.post(`${this.baseURL}/v1/account/destroy`, {}, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
return true;
} catch (error) {
throw new Error(`Logout failed: ${error.message}`);
}
}
}
// Usage example
const fxaClient = new FxAClient({
baseURL: 'https://your-fxa-app.klutch.sh',
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
redirectUri: 'https://your-app.com/oauth/callback'
});
// Express.js route example
app.get('/login', (req, res) => {
const state = crypto.randomBytes(16).toString('hex');
req.session.oauthState = state;
const authUrl = fxaClient.getAuthorizationUrl(state);
res.redirect(authUrl);
});
app.get('/oauth/callback', async (req, res) => {
const { code, state } = req.query;
if (state !== req.session.oauthState) {
return res.status(400).send('Invalid state');
}
try {
const tokenData = await fxaClient.getAccessToken(code);
const profile = await fxaClient.getUserProfile(tokenData.access_token);
req.session.user = {
email: profile.email,
uid: profile.uid,
accessToken: tokenData.access_token
};
res.redirect('/dashboard');
} catch (error) {
res.status(500).send('Authentication failed');
}
});
module.exports = fxaClient;

Python Application Integration

Integrate FxA with a Python/Flask application:

fxa_client.py
import requests
import secrets
from urllib.parse import urlencode
class FxAClient:
def __init__(self, base_url, client_id, client_secret, redirect_uri):
self.base_url = base_url
self.client_id = client_id
self.client_secret = client_secret
self.redirect_uri = redirect_uri
def get_authorization_url(self, state, scope='profile openid'):
"""Generate OAuth authorization URL"""
params = {
'client_id': self.client_id,
'response_type': 'code',
'state': state,
'scope': scope,
'redirect_uri': self.redirect_uri
}
return f"{self.base_url}/v1/authorization?{urlencode(params)}"
def get_access_token(self, code):
"""Exchange authorization code for access token"""
url = f"{self.base_url}/v1/token"
data = {
'grant_type': 'authorization_code',
'code': code,
'client_id': self.client_id,
'client_secret': self.client_secret,
'redirect_uri': self.redirect_uri
}
response = requests.post(url, json=data)
response.raise_for_status()
return response.json()
def get_user_profile(self, access_token):
"""Fetch user profile with access token"""
url = f"{self.base_url}/v1/account/profile"
headers = {'Authorization': f'Bearer {access_token}'}
response = requests.get(url, headers=headers)
response.raise_for_status()
return response.json()
def verify_email(self, uid, code):
"""Verify user email address"""
url = f"{self.base_url}/v1/recovery_email/verify_code"
data = {'uid': uid, 'code': code}
response = requests.post(url, json=data)
response.raise_for_status()
return response.json()
def create_account(self, email, auth_pw):
"""Create new user account"""
url = f"{self.base_url}/v1/account/create"
data = {'email': email, 'authPW': auth_pw}
response = requests.post(url, json=data)
response.raise_for_status()
return response.json()
# Flask application example
from flask import Flask, redirect, request, session, url_for
import os
app = Flask(__name__)
app.secret_key = os.urandom(32)
fxa_client = FxAClient(
base_url='https://your-fxa-app.klutch.sh',
client_id=os.environ['FXA_CLIENT_ID'],
client_secret=os.environ['FXA_CLIENT_SECRET'],
redirect_uri='https://your-app.com/oauth/callback'
)
@app.route('/login')
def login():
state = secrets.token_urlsafe(16)
session['oauth_state'] = state
auth_url = fxa_client.get_authorization_url(state)
return redirect(auth_url)
@app.route('/oauth/callback')
def oauth_callback():
code = request.args.get('code')
state = request.args.get('state')
if state != session.get('oauth_state'):
return 'Invalid state', 400
try:
token_data = fxa_client.get_access_token(code)
profile = fxa_client.get_user_profile(token_data['access_token'])
session['user'] = {
'email': profile['email'],
'uid': profile['uid'],
'access_token': token_data['access_token']
}
return redirect(url_for('dashboard'))
except Exception as e:
return f'Authentication failed: {str(e)}', 500
@app.route('/dashboard')
def dashboard():
if 'user' not in session:
return redirect(url_for('login'))
return f"Welcome {session['user']['email']}"
if __name__ == '__main__':
app.run(debug=True)

Production Best Practices

Security Hardening

Implement these security measures for production deployments:

  1. Strong Secret Keys

    Terminal window
    # Generate cryptographically secure secrets
    node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
  2. Rate Limiting Configuration

    • Enable rate limiting in environment variables
    • Configure limits based on your traffic patterns
    • Monitor for abuse patterns
  3. Email Security

    • Use dedicated SMTP service (SendGrid, Mailgun, AWS SES)
    • Implement SPF, DKIM, and DMARC records
    • Monitor bounce rates and delivery issues
  4. Database Security

    • Use strong passwords for MySQL and Redis
    • Enable SSL/TLS for database connections
    • Regular backups (see MySQL guide)
    • Implement query timeout limits
  5. Session Management

    • Set appropriate session timeout values
    • Enable secure session cookies
    • Implement device tracking
    • Force re-authentication for sensitive operations

Performance Optimization

Optimize your FxA deployment for better performance:

  1. Redis Caching Strategy

    Terminal window
    # Configure Redis for optimal caching
    REDIS_MAX_MEMORY=2gb
    REDIS_EVICTION_POLICY=allkeys-lru
    REDIS_PERSISTENCE=yes
  2. Database Connection Pooling

    • Configure appropriate connection pool size
    • Monitor connection usage and timeouts
    • Implement read replicas for high traffic
  3. Resource Allocation

    • Allocate sufficient memory (2GB minimum recommended)
    • Monitor CPU usage during peak times
    • Scale horizontally if needed
  4. CDN for Static Assets

    • Serve verification pages through CDN
    • Cache public assets (logos, CSS, JavaScript)
    • Reduce latency for global users

Monitoring and Logging

Set up comprehensive monitoring:

  1. Health Checks

    Terminal window
    # Monitor these endpoints
    https://your-fxa-app.klutch.sh/__heartbeat__
    https://your-fxa-app.klutch.sh/__lbheartbeat__
    https://your-fxa-app.klutch.sh/__version__
  2. Key Metrics to Track

    • Account creation rate
    • Login success/failure rates
    • Token generation frequency
    • Email delivery rates
    • API response times
    • Database query performance
    • Redis cache hit/miss ratio
  3. Error Logging

    • Configure structured logging
    • Set appropriate log levels (info, warn, error)
    • Implement log aggregation
    • Set up alerts for critical errors
  4. Security Event Monitoring

    • Track failed login attempts
    • Monitor suspicious IP addresses
    • Alert on unusual account activity
    • Log OAuth authorization flows

Backup and Disaster Recovery

Implement robust backup strategies:

  1. Database Backups

    • Automated daily MySQL backups
    • Point-in-time recovery capability
    • Test restore procedures regularly
    • Store backups off-site
  2. Configuration Backups

    • Version control all configuration files
    • Document environment variables
    • Backup OAuth client configurations
    • Store encryption keys securely (use secrets manager)
  3. Disaster Recovery Plan

    • Document recovery procedures
    • Test failover scenarios
    • Maintain standby instances for critical services
    • Define RTO (Recovery Time Objective) and RPO (Recovery Point Objective)

Troubleshooting

Common Issues and Solutions

Issue: Server won’t start - “Cannot connect to MySQL”

Solution:

Terminal window
# Verify MySQL is running
curl https://your-mysql-app.klutch.sh:8000
# Check MySQL credentials in environment variables
# Verify database 'fxa' exists
mysql -h your-mysql-app.klutch.sh -P 8000 -u fxa_user -p -e "SHOW DATABASES;"
# Check FxA logs for specific connection errors

Issue: “Redis connection refused”

Solution:

Terminal window
# Test Redis connectivity
redis-cli -h your-redis-app.klutch.sh -p 8000 -a your-redis-password PING
# Verify Redis environment variables
# Check if Redis requires password authentication
# Ensure Redis is accepting connections from FxA server

Issue: Email verification not working

Solution:

Terminal window
# Test SMTP connection
curl -v telnet://smtp.example.com:587
# Check email logs in FxA
# Verify SMTP credentials are correct
# Check spam folder for test emails
# Verify DNS records (SPF, DKIM, DMARC)

Issue: OAuth authorization fails

Solution:

Terminal window
# Verify OAuth client is registered
# Check redirect_uri matches exactly
# Ensure client_secret is correct
# Verify scope permissions are configured
# Check PUBLIC_URL matches your actual URL

Issue: High memory usage

Solution:

Terminal window
# Check for memory leaks in logs
# Monitor Redis memory usage
# Optimize database queries
# Increase container memory allocation
# Implement query result pagination

Issue: Slow response times

Solution:

Terminal window
# Check database connection pool size
# Monitor slow query log in MySQL
# Verify Redis cache is working
# Check network latency to dependencies
# Implement query optimization

Debugging Tips

Enable detailed logging for troubleshooting:

Terminal window
# Set detailed logging level
LOG_LEVEL=debug
# Enable query logging in MySQL
# Enable command logging in Redis
# Check application logs in Klutch.sh dashboard
# Use health check endpoints to isolate issues

Access database for debugging:

Terminal window
# Check active sessions
SELECT COUNT(*) FROM sessionTokens WHERE lastAccessTime > UNIX_TIMESTAMP() - 3600;
# Check recent accounts
SELECT email, createdAt, emailVerified FROM accounts ORDER BY createdAt DESC LIMIT 10;
# Check failed login attempts
SELECT nameId, COUNT(*) as attempts FROM securityEvents
WHERE nameId = 'account.login' AND verified = 0
GROUP BY nameId;
# Check OAuth tokens
SELECT clientId, COUNT(*) as token_count FROM oauthTokens
WHERE expiresAt > UNIX_TIMESTAMP()
GROUP BY clientId;

Advanced Configuration

Multi-Region Deployment

Deploy FxA across multiple regions for better performance:

  1. Deploy separate FxA instances in different regions
  2. Use a shared MySQL primary with read replicas
  3. Configure Redis sentinel for high availability
  4. Implement global load balancer
  5. Configure geo-routing for optimal latency

Custom Email Templates

Customize verification and notification emails:

  1. Create custom email templates directory
  2. Override default templates with branded versions
  3. Implement multi-language email support
  4. Add custom verification flows
  5. Configure email preview testing

WebAuthn Support

Enable passwordless authentication with WebAuthn:

  1. Configure WebAuthn options in environment
  2. Enable biometric authentication
  3. Implement device registration flows
  4. Configure authenticator attestation
  5. Test with hardware security keys

Custom OAuth Scopes

Define custom scopes for your applications:

  1. Register custom scope definitions
  2. Implement scope validation logic
  3. Configure scope-based access control
  4. Document scope requirements for clients
  5. Implement scope consent UI

Use Cases

Enterprise Single Sign-On

Use FxA as central authentication for internal applications:

  • Unified login across all company tools
  • Centralized user management and provisioning
  • MFA enforcement for sensitive applications
  • Audit logging for compliance requirements
  • Role-based access control integration

Multi-Tenant SaaS Platform

Implement FxA for SaaS authentication:

  • Per-tenant OAuth client configuration
  • Custom branding per tenant
  • Usage tracking and billing integration
  • Tenant isolation and security
  • API rate limiting per tenant

Firefox Sync for Organizations

Enable browser synchronization for teams:

  • Organization-controlled sync server
  • Enhanced privacy and data control
  • Custom sync policies and rules
  • Compliance with data residency requirements
  • Integration with MDM solutions

Resources and Further Reading

Official Documentation

Klutch.sh Resources


Conclusion

Deploying Firefox Account Server on Klutch.sh provides a powerful, self-hosted authentication solution with enterprise-grade security and Mozilla’s proven architecture. With this guide, you have everything needed to:

  • Deploy FxA with Docker on Klutch.sh
  • Configure MySQL and Redis dependencies
  • Implement OAuth 2.0 authentication flows
  • Integrate FxA with Node.js and Python applications
  • Secure your deployment with best practices
  • Monitor and optimize for production use
  • Troubleshoot common issues
  • Scale for enterprise workloads

Your Firefox Account Server on Klutch.sh offers a privacy-focused, self-hosted alternative to commercial authentication providers, giving you complete control over user identity and authentication flows while maintaining compatibility with the broader Mozilla ecosystem.

For questions, issues, or contributions, refer to the official Firefox Account Server repository and Klutch.sh documentation. Regular updates and security patches ensure your deployment remains secure and up-to-date with the latest authentication standards.