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

Step 1: Set Up Firebase Project

Step 2: Create Custom Authentication Module

mkdir -p /path/to/synapse/config/modules

cd /path/to/synapse/config/modules

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

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

// 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);

 }

}

// 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

Step 6: Testing the Implementation

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

Troubleshooting

Best Practices

Conclusion

This implementation provides a secure and efficient way to authenticate Matrix users using Firebase. Remember to: