Firebase Cloud Messaging (FCM) Integration - Complete Setup Guide¶
Status: โ
Phases 1-3 Complete
Date: April 10, 2026
Implementation Time: ~2 hours
๐ What Was Implemented¶
Phase 1: Database & Models โ ¶
1.1 DeviceToken Model¶
- File: models/notify.py
- Stores FCM tokens per user with metadata
- Fields:
user_id,fcm_token(unique),platform,device_name,is_active,last_used_at - Automatically tracks
created_onandupdated_on
1.2 Alembic Migration¶
- File: alembic/versions/564f2g8a1h3b_add_device_tokens_table_for_push_notifications.py
- Creates
notify.device_tokenstable with indexes for performance - Includes
user_idforeign key with CASCADE delete - Creates indexes on:
user_id,fcm_token,platform,is_active
1.3 Push Templates (Already Existed)¶
- File: seed/template_seed.json
- Push templates for:
send2FA,INTERNAL_TRANSFER,DEPOSIT,INITIAL_DEPOSIT - Support for:
en,pt,frlanguages - Uses same placeholder system as SMS/Email
Phase 2: Firebase Backend โ ¶
2.1 Firebase Service Wrapper¶
- File: utils/notify/firebase.py
- Singleton pattern for Firebase Admin SDK initialization
- Methods:
send_notification()- Single device pushsend_multicast()- Push to multiple devicessend_to_topic()- Push to topic subscriberssubscribe_to_topic()/unsubscribe_from_topic()- Topic management- Error handling for invalid/unregistered tokens
- Logging for all operations
2.2 Push Notification Celery Task¶
- File: utils/notify/tasks.py
- Updated
send_push_notification()async function with full FCM integration - Supports title, body, data payload, and image_url
- Returns CustomResponse with FCM message ID
- Integrated into
send_via_service()router - Extracts push-specific data from message.udf:
title,push_data,image_url
2.3 Configuration¶
- File: core/config.py
- Added
FIREBASE_CONFIGdictionary with: credentials_path: Path to Firebase service account JSONproject_id: Firebase project ID
Phase 3: Integration & APIs โ ¶
3.1 Extended queue_template_notification()¶
- File: controller/notify.py
- Added
user_idparameter for device token lookup - Added push channel-specific processing:
- Stores
title(from template.subject) - Stores
push_datawith template context - Stores
image_urlif provided in placeholders - Maintains backward compatibility with SMS/Email
3.2 Device Token Management Endpoints¶
- File: routers/device_tokens.py
- Base URL:
/api/v1/notifications/devices - Endpoints:
POST /register- Register new device tokenGET /- List user's device tokensGET /{id}- Get specific device tokenPUT /{id}/deactivate- Deactivate tokenPUT /{id}/activate- Reactivate tokenDELETE /{id}- Delete single tokenDELETE /- Delete all tokens- All endpoints require authentication
- Filter by
active_onlyquery parameter
3.3 Router Integration¶
- File: main.py
- Component imported:
from routers.device_tokens import router as device_token_router - Added to routers list for inclusion in FastAPI app
๐ Getting Started¶
Prerequisites¶
- Firebase Project created at https://console.firebase.google.com
- Firebase Admin SDK enabled
- Service account JSON downloaded with private key
Step 1: Setup Environment Variables¶
Add to your .env file:
# Firebase Configuration
FIREBASE_PROJECT_ID=your-project-id
FIREBASE_CREDENTIALS_PATH=/app/firebase-credentials.json
For Docker, mount the credentials file:
Step 2: Install Dependencies¶
Or add to requirements.txt:
Step 3: Run Database Migration¶
This creates the notify.device_tokens table.
Step 4: (Optional) Seed Push Templates¶
Push templates already exist in seed/template_seed.json. They're automatically seeded via the existing template seeding process.
๐ฑ API Usage Examples¶
Register Device Token (Mobile/Web)¶
curl -X POST http://localhost:8000/api/v1/notifications/devices/register \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"fcm_token": "eHxO_SA:APA91bH...",
"platform": "mobile",
"device_name": "iPhone 14 Pro"
}'
Response:
{
"id": 1,
"fcm_token": "eHxO_SA:APA91bH...",
"platform": "mobile",
"device_name": "iPhone 14 Pro",
"is_active": true,
"created_on": "2026-04-10T10:00:00.000Z"
}
List User's Device Tokens¶
Send Push via Notify Pipeline¶
from controller.notify import queue_template_notification
from sqlalchemy.orm import Session
queue_template_notification(
db=db_session,
channel="push",
template_name="INTERNAL_TRANSFER",
receiver="USER_FCM_TOKEN_HERE", # FCM token from device_tokens table
sender="CodeForge Banking",
placeholders={
"debit_name": "John Doe",
"credit_name": "Jane Smith",
"amount": "500.00",
"currency": "USD",
"debit_account": "123456789",
"credit_account": "987654321",
"transaction_reference_id": "TXN123456"
},
service="push",
priority=True, # Send immediately (optional)
language="en"
)
๐ How Push Notifications Flow¶
1. User registers FCM token via API
โ
2. Application queues notification via queue_template_notification()
- Template formatted with placeholders
- Message stored in notify.message table with status="pending"
โ
3. Celery worker picks up message
- Calls send_message task
- Routes to send_via_service based on service type
โ
4. Firebase Service sends notification
- Uses FCM token as recipient
- Sends title, body, data via FCM Admin SDK
โ
5. Firebase Cloud Messaging
- Delivers to registered device
- Handles retries and failures
โ
6. Mobile/Web app receives notification
- Firebase SDK on client displays notification
- App can handle additional logic
โ๏ธ Configuration Reference¶
environment Variables¶
| Variable | Description | Example |
|---|---|---|
FIREBASE_PROJECT_ID |
Firebase Project ID | my-banking-app |
FIREBASE_CREDENTIALS_PATH |
Path to service account JSON | /app/firebase-credentials.json |
Message Templates¶
Templates use consistent placeholder syntax:
{
"channel_id": "push",
"language_id": "en",
"name": "TRANSACTION_ALERT",
"subject": "Transaction Alert",
"message": "Transaction of {currency} {amount} to {recipient_name}"
}
Available placeholders: See TransactionTemplate documentation
Device Token Fields¶
| Field | Type | Purpose |
|---|---|---|
fcm_token |
String(unique) | Firebase Cloud Messaging token |
platform |
String | "mobile" or "web" |
device_name |
String | Device identifier (e.g., "iPhone 14") |
is_active |
Boolean | Whether token can receive notifications |
last_used_at |
DateTime | Track token usage |
๐ก๏ธ Error Handling¶
Token Errors¶
- InvalidArgumentError: Token format invalid โ log and return 400
- UnregisteredError: Token expired/revoked โ mark as inactive (410)
- FirebaseError: Firebase service issue โ return 500
Recovery Strategies¶
- Automatic token deactivation on UnregisteredError
- Circuit breaker pattern for provider failures
- Exponential backoff for retries
- Dead letter queue for failed messages
๐ Monitoring & Logging¶
All push operations logged to: - File: Configured via Python logging in logger setup - Celery logs: Track async task execution - FCM logs: Firebase admin SDK operations
Key metrics to monitor:
- messages_sent_total - Successfully sent
- messages_failed_total - Failed messages
- messages_requeued_* - Retried messages
- FCM API response times
๐ Security Considerations¶
- Firebase Credentials
- Keep
firebase-credentials.jsonprivate - Use environment variables, never hardcode
-
Mount as Docker secret in production
-
FCM Tokens
- Encryption: Stored as
EncryptedTypein database (like receiver/sender) - Access: Only user can access their own tokens
-
Authentication: All endpoints require valid JWT
-
Payload Validation
- Title: Max 240 characters (FCM limit)
- Body: Max 240 characters (FCM limit)
- Data: String key-value pairs only
- Image URL: Must be HTTPS
๐งช Testing¶
Unit Tests (To Create)¶
def test_register_device_token():
# Test token registration
pass
def test_send_push_notification():
# Test Firebase integration
pass
def test_invalid_fcm_token():
# Test error handling
pass
Manual Testing¶
# 1. Register device
curl -X POST http://localhost:8000/api/v1/notifications/devices/register \
-H "Authorization: Bearer TOKEN" \
-d '{"fcm_token":"TEST_TOKEN","platform":"mobile"}'
# 2. Create test message
python manage.py send_push --user_id=1
# 3. Check Celery logs
docker logs backend-celery-worker
๐ Next Steps (Phase 4)¶
Mobile App Integration (Flutter)¶
- [ ] Firebase initialization in flutter app
- [ ] Generate FCM registration token
- [ ] Auto-register token with backend API
- [ ] Setup Firebase Cloud Messaging handlers
- [ ] Display notifications in system tray
Web App Integration (React)¶
- [ ] Service Worker setup
- [ ] Firebase Web SDK initialization
- [ ] Generate FCM token
- [ ] Register token via API
- [ ] Handle notification clicks
See PUSH_NOTIFICATIONS_MOBILE_SETUP.md¶
๐ Troubleshooting¶
Firebase credentials not found¶
Fix: EnsureFIREBASE_CREDENTIALS_PATH points to valid JSON file
Invalid FCM Token Error¶
Fix: Token expired or invalid format. User should re-register device.Service not in notify.channel¶
Fix: Ensure "push" channel exists in notify.channel table. Check seeded data.Tokens not being sent¶
Fix: 1. Check Celery worker is running 2. Verify Redis connection 3. Check FCM credentials are valid๐ Support¶
For issues or questions: 1. Check Firebase Admin SDK docs: https://firebase.google.com/docs/admin/setup 2. Review Celery documentation: https://docs.celeryproject.io/ 3. Check notify module: controller/notify.py