Introduction
This guide explains how to implement Firebase authentication for Matrix login using Synapse's custom authentication module. This allows users to log in to your Matrix server using their Firebase credentials, providing a seamless authentication experience.
Prerequisites
Existing Setup:
Matrix Synapse server running
PostgreSQL database configured
Firebase project created
Required Components:
Firebase Admin SDK credentials
Python 3.7+ installed
Basic understanding of Matrix authentication flow
Step 1: Set Up Firebase Project
Create Firebase Project:
Go to Firebase Console
Create a new project
Enable Authentication service
Configure sign-in methods (Email/Password, Google, etc.)
Get Firebase Admin SDK Credentials:
Go to Project Settings > Service Accounts
Generate new private key
Save the JSON file securely
Step 2: Create Custom Authentication Module
Create Module Directory:
mkdir -p /path/to/synapse/config/modules
cd /path/to/synapse/config/modules
Create Firebase Auth Module (firebase_auth.py):
import logging
from typing import Any, Dict, Optional, Tuple
from firebase_admin import credentials, initialize_app, auth
from synapse.api.errors import SynapseError
from synapse.module_api import ModuleApi
logger = logging.getLogger(__name__)
class FirebaseAuthProvider:
def __init__(self, config: Dict[str, Any], api: ModuleApi):
self.api = api
self.config = config
# Initialize Firebase Admin SDK
cred = credentials.Certificate(config["firebase_credentials_path"])
initialize_app(cred)
logger.info("Firebase Auth Provider initialized")
async def check_auth(
self, username: str, login_type: str, login_dict: Dict[str, Any]
) -> Optional[Tuple[str, Optional[str]]]:
"""Check Firebase authentication."""
if login_type != "m.login.token":
return None
try:
# Get Firebase token from login_dict
firebase_token = login_dict.get("token")
if not firebase_token:
return None
# Verify Firebase token
decoded_token = auth.verify_id_token(firebase_token)
# Get user's email from Firebase token
firebase_email = decoded_token.get("email")
if not firebase_email:
raise SynapseError(400, "No email in Firebase token")
# Check if email matches requested username
if firebase_email != username:
raise SynapseError(400, "Email mismatch")
# Return user ID and display name
return (username, decoded_token.get("name"))
except auth.InvalidIdTokenError:
logger.warning("Invalid Firebase token")
return None
except Exception as e:
logger.error(f"Firebase auth error: {e}")
return None
Step 3: Configure Synapse
Update homeserver.yaml
password_providers:
- module: "firebase_auth.FirebaseAuthProvider"
config:
firebase_credentials_path: "/path/to/firebase-credentials.json"
# Enable token-based login
enable_registration: true
registration_requires_token: false
Install Required Dependencies:
pip install firebase-admin
Step 4: Client-Side Implementation
Web Client Example
// Initialize Firebase
const firebaseConfig = {
apiKey: "your-api-key",
authDomain: "your-project.firebaseapp.com",
projectId: "your-project",
// ... other config
};
firebase.initializeApp(firebaseConfig);
// Login function
async function loginWithFirebase() {
try {
// Sign in with Firebase
const userCredential = await firebase.auth().signInWithEmailAndPassword(
email,
password
);
// Get Firebase ID token
const idToken = await userCredential.user.getIdToken();
// Login to Matrix with Firebase token
const matrixClient = await sdk.createClient({
baseUrl: "https://your-matrix-server",
});
const response = await matrixClient.login("m.login.token", {
token: idToken,
});
// Store Matrix credentials
localStorage.setItem("mx_access_token", response.access_token);
localStorage.setItem("mx_user_id", response.user_id);
} catch (error) {
console.error("Login failed:", error);
}
}
Mobile Client Example
// test/auth_service_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:matrix_sdk/matrix_sdk.dart';
import '../lib/services/auth_service.dart';
void main() {
group('AuthService Tests', () {
late AuthService authService;
setUp(() {
authService = AuthService();
});
test('Sign in with valid credentials', () async {
final result = await authService.signInWithEmailAndPassword(
'test@example.com',
'password123',
);
expect(result, isA<UserCredential>());
});
test('Login to Matrix after Firebase auth', () async {
await authService.signInWithEmailAndPassword(
'test@example.com',
'password123',
);
await authService.loginToMatrix();
expect(authService.matrixClient, isNotNull);
});
});
}
Step 5: Security Considerations
Token Validation:
Implement token expiration checks
Verify token audience and issuer
Check for revoked tokens
Rate Limiting:
Implement rate limiting for authentication attempts
Monitor failed login attempts
Error Handling:
Implement proper error messages
Log authentication failures
Handle token refresh scenarios
Step 6: Testing the Implementation
Test Cases:
Valid Firebase token
Expired token
Invalid token
Token with mismatched email
Rate limiting
Token refresh
Testing Script:
import pytest
from firebase_auth import FirebaseAuthProvider
from synapse.module_api import ModuleApi
@pytest.mark.asyncio
async def test_firebase_auth():
# Initialize provider
config = {
"firebase_credentials_path": "path/to/credentials.json"
}
provider = FirebaseAuthProvider(config, ModuleApi())
# Test valid token
result = await provider.check_auth(
"user@example.com",
"m.login.token",
{"token": "valid-firebase-token"}
)
assert result is not None
# Test invalid token
result = await provider.check_auth(
"user@example.com",
"m.login.token",
{"token": "invalid-token"}
)
assert result is None
Step 7: Monitoring and Maintenance
Logging:
Implement detailed logging
Monitor authentication attempts
Track token validation results
Performance Monitoring:
Monitor Firebase token validation latency
Track authentication success rates
Monitor error rates
Regular Updates:
Keep Firebase Admin SDK updated
Update Synapse regularly
Monitor security advisories
Troubleshooting
Common Issues:
Token validation failures
Email mismatch errors
Firebase connection issues
Rate limiting problems
Debugging Steps:
Check Firebase credentials
Verify token format
Check network connectivity
Review server logs
Best Practices
Security:
Use HTTPS for all communications
Implement proper token validation
Secure Firebase credentials
Regular security audits
Performance:
Cache Firebase tokens
Implement token refresh
Optimize validation process
User Experience:
Clear error messages
Smooth token refresh
Proper session management
Conclusion
This implementation provides a secure and efficient way to authenticate Matrix users using Firebase. Remember to:
Keep security configurations up to date
Monitor authentication patterns
Implement proper error handling
Regular security testing
Maintain documentation