15 KiB
Firebase Cloud Messaging (FCM) - Comprehensive Guide
Overview
This guide consolidates all FCM (Firebase Cloud Messaging) knowledge for Sojorn, covering setup, deployment, troubleshooting, and platform-specific considerations for both Web and Android.
Quick Start (TL;DR)
- Get VAPID key from Firebase Console
- Download Firebase service account JSON
- Update Flutter app with VAPID key
- Upload JSON to server at
/opt/sojorn/firebase-service-account.json - Add to
/opt/sojorn/.env:FIREBASE_CREDENTIALS_FILE=/opt/sojorn/firebase-service-account.json - Restart Go backend
- Test notifications
Architecture Overview
How FCM Works in Sojorn
- User opens app → Flutter requests notification permission
- Permission granted → Firebase generates FCM token
- Token sent to backend → Stored in
fcm_tokenstable - Event occurs (new message, follow, etc.) → Go backend calls
PushService.SendPush() - FCM sends notification → User's device/browser receives it
- User clicks notification → App opens to relevant screen
Notification Triggers
- New chat message (
chat_handler.go:156) - New follower (
user_handler.go:141) - Follow request accepted (
user_handler.go:319)
Platform Differences
Web (Working ✅)
- Uses VAPID key for authentication
- Service worker handles background messages
- Token format:
d2n2ELGKel7yzPL3wZLGSe:APA91b... - Requires user to grant notification permission in browser
Android (Requires Setup ❓)
- Uses
google-services.jsonfor authentication - Native Android handles background messages
- Token format: Different from web, longer
- Requires runtime permission on Android 13+
- Needs notification channels (Android 8+)
Setup Instructions
Step 1: Get Firebase Credentials
A. Get VAPID Key (for Web Push)
- Go to https://console.firebase.google.com/project/sojorn-a7a78/settings/cloudmessaging
- Scroll to Web configuration section
- Under Web Push certificates, copy the Key pair
- It should look like:
BNxS7_very_long_string_of_characters...
B. Download Service Account JSON (for Server)
- Go to https://console.firebase.google.com/project/sojorn-a7a78/settings/serviceaccounts
- Click Generate new private key
- Click Generate key - downloads JSON file
- Save it somewhere safe (you'll upload it to server)
Example JSON structure:
{
"type": "service_account",
"project_id": "sojorn-a7a78",
"private_key_id": "abc123...",
"private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-xxxxx@sojorn-a7a78.iam.gserviceaccount.com",
"client_id": "123456789...",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/..."
}
Step 2: Update Flutter App with VAPID Key
File: sojorn_app/lib/config/firebase_web_config.dart
Replace line 24:
static const String _vapidKey = 'YOUR_VAPID_KEY_HERE';
With your actual VAPID key:
static const String _vapidKey = 'BNxS7_your_actual_vapid_key_from_firebase_console';
Commit and push:
cd c:\Webs\Sojorn
git add sojorn_app/lib/config/firebase_web_config.dart
git commit -m "Add FCM VAPID key for web push notifications"
git push
Step 3: Upload Firebase Service Account JSON to Server
From Windows PowerShell:
scp -i "C:\Users\Patrick\.ssh\mpls.pem" "C:\path\to\sojorn-a7a78-firebase-adminsdk-xxxxx.json" patrick@194.238.28.122:/tmp/firebase-service-account.json
Replace C:\path\to\... with the actual path to your downloaded JSON file.
Step 4: Configure Server
SSH to server:
ssh -i "C:\Users\Patrick\.ssh\mpls.pem" patrick@194.238.28.122
Manual setup:
# Move JSON file
sudo mv /tmp/firebase-service-account.json /opt/sojorn/firebase-service-account.json
sudo chmod 600 /opt/sojorn/firebase-service-account.json
sudo chown patrick:patrick /opt/sojorn/firebase-service-account.json
# Edit .env
sudo nano /opt/sojorn/.env
Add these lines to .env:
# Firebase Cloud Messaging
FIREBASE_CREDENTIALS_FILE=/opt/sojorn/firebase-service-account.json
FIREBASE_WEB_VAPID_KEY=BNxS7_your_actual_vapid_key_here
Save and exit (Ctrl+X, Y, Enter)
Step 5: Restart Go Backend
cd /home/patrick/sojorn-backend
sudo systemctl restart sojorn-api
sudo systemctl status sojorn-api
Check logs for successful initialization:
sudo journalctl -u sojorn-api -f --since "1 minute ago"
Look for:
[INFO] PushService initialized successfully
Android-Specific Setup
Prerequisites
- google-services.json: Download from Firebase Console for Android app
- Package Name: Must match
com.gosojorn.app - Build Configuration: Proper Gradle setup
- Permissions: Runtime notification permissions (Android 13+)
Android Configuration Files
1. google-services.json
Location: sojorn_app/android/app/google-services.json
Verify package name:
{
"project_info": {
"project_number": "486753572104",
"project_id": "sojorn-a7a78"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:486753572104:android:abc123...",
"android_client_info": {
"package_name": "com.gosojorn.app"
}
}
}
]
}
2. build.gradle.kts (Project Level)
File: sojorn_app/android/build.gradle.kts
dependencies {
classpath("com.google.gms:google-services:4.4.0")
}
3. build.gradle.kts (App Level)
File: sojorn_app/android/app/build.gradle.kts
plugins {
id("com.android.application")
id("kotlin-android")
id("com.google.gms.google-services")
}
android {
namespace = "com.gosojorn.app"
// ... other config
}
dependencies {
implementation("com.google.firebase:firebase-messaging")
// ... other dependencies
}
4. AndroidManifest.xml
File: sojorn_app/android/app/src/main/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application>
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_notification" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/colorAccent" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/default_notification_channel_id" />
</application>
</manifest>
5. strings.xml (Notification Channel)
File: sojorn_app/android/app/src/main/res/values/strings.xml
<resources>
<string name="default_notification_channel_id">chat_messages</string>
<string name="default_notification_channel_name">Chat messages</string>
</resources>
Testing & Verification
Test 1: Check Token Registration
Web
- Open Sojorn web app in browser
- Open DevTools (F12) > Console
- Look for:
FCM token registered (web): d2n2ELGKel7yzPL3wZLGSe... - If you see "Web push is missing FIREBASE_WEB_VAPID_KEY", VAPID key is not set correctly
Android
- Run the app:
cd c:\Webs\Sojorn && .\run_dev.ps1 - Check logs:
adb logcat | findstr "FCM" - Look for:
[FCM] Initializing for platform: android [FCM] Token registered (android): eXaMpLe... [FCM] Token synced with Go Backend successfully
Test 2: Check Database
sudo -u postgres psql sojorn
-- Check FCM tokens are being stored
SELECT user_id, platform, LEFT(fcm_token, 30) as token_preview, created_at
FROM public.fcm_tokens
ORDER BY created_at DESC
LIMIT 5;
Expected output:
user_id | platform | token_preview | created_at
-------------------------------------+----------+--------------------------------+-------------------
5568b545-5215-4734-875f-84b3106cd170 | web | d2n2ELGKel7yzPL3wZLGSe:APA91b | 2026-01-29 05:50
5568b545-5215-4734-875f-84b3106cd170 | android | eXaMpLe_android_token_here... | 2026-01-29 06:00
Test 3: Send Test Message
- Open two browser windows (or use two different users)
- User A sends a chat message to User B
- User B should receive a push notification (if browser is in background)
Check server logs:
sudo journalctl -u sojorn-api -f | grep -i push
You should see:
[INFO] Sending push notification to user 5568b545...
[INFO] Push notification sent successfully
Troubleshooting
Common Issues & Solutions
Issue: "Web push is missing FIREBASE_WEB_VAPID_KEY"
Cause: VAPID key not set in Flutter app
Fix:
- Update
firebase_web_config.dartwith actual VAPID key - Hot restart Flutter app
- Check console again
Issue: "Failed to initialize PushService"
Cause: Firebase service account JSON not found or invalid
Fix:
# Check file exists
ls -la /opt/sojorn/firebase-service-account.json
# Check .env has correct path
sudo cat /opt/sojorn/.env | grep FIREBASE_CREDENTIALS_FILE
# Validate JSON
cat /opt/sojorn/firebase-service-account.json | jq .
# Check permissions
ls -la /opt/sojorn/firebase-service-account.json
# Should show: -rw------- 1 patrick patrick
Issue: Android "Token is null after getToken()"
Cause: Firebase not properly initialized or google-services.json mismatch
Fix:
- Verify
google-services.jsonpackage name matches:"package_name": "com.gosojorn.app" - Check
build.gradle.ktshas:applicationId = "com.gosojorn.app" - Rebuild:
flutter clean && flutter pub get && flutter run
Issue: Android "Permission denied"
Cause: User denied notification permission or Android 13+ permission not requested
Fix:
- Check
AndroidManifest.xmlhas:<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> - On Android 13+, permission must be requested at runtime
- Uninstall and reinstall app to re-trigger permission prompt
Issue: Notifications not received
Checklist:
- Browser notification permissions granted
- FCM token registered (check console)
- Token stored in database (check SQL)
- Go backend logs show push being sent
- Service worker registered (check DevTools > Application > Service Workers)
Check service worker:
- Open DevTools > Application > Service Workers
- Should see
firebase-messaging-sw.jsregistered - If not, check
sojorn_app/web/firebase-messaging-sw.jsexists
Debug Checklist for Android
Run through this checklist:
google-services.jsonexists inandroid/app/- Package name matches in all files
build.gradle.ktshasgoogle-servicespluginAndroidManifest.xmlhasPOST_NOTIFICATIONSpermission- App has notification permission granted
- Android logs show FCM initialization
- Android logs show token generated
- Token appears in database
fcm_tokenstable - Backend logs show notification being sent
- Android logs show notification received
Current Configuration
Firebase Project:
- Project ID:
sojorn-a7a78 - Sender ID:
486753572104 - Console: https://console.firebase.google.com/project/sojorn-a7a78
Server Paths:
- .env:
/opt/sojorn/.env - Service Account:
/opt/sojorn/firebase-service-account.json - Backend:
/home/patrick/sojorn-backend
Flutter Files:
- Config:
sojorn_app/lib/config/firebase_web_config.dart - Service Worker:
sojorn_app/web/firebase-messaging-sw.js - Notification Service:
sojorn_app/lib/services/notification_service.dart
Android Files:
- Firebase Config:
sojorn_app/android/app/google-services.json - Build Config:
sojorn_app/android/app/build.gradle.kts - Manifest:
sojorn_app/android/app/src/main/AndroidManifest.xml - Strings:
sojorn_app/android/app/src/main/res/values/strings.xml
Quick Reference Commands
# SSH to server
ssh -i "C:\Users\Patrick\.ssh\mpls.pem" patrick@194.238.28.122
# Check .env
sudo cat /opt/sojorn/.env | grep FIREBASE
# Check service account file
ls -la /opt/sojorn/firebase-service-account.json
cat /opt/sojorn/firebase-service-account.json | jq .project_id
# Restart backend
sudo systemctl restart sojorn-api
# View logs
sudo journalctl -u sojorn-api -f
# Check FCM tokens in DB
sudo -u postgres psql sojorn -c "SELECT COUNT(*) as token_count FROM public.fcm_tokens;"
# View recent tokens
sudo -u postgres psql sojorn -c "SELECT user_id, platform, created_at FROM public.fcm_tokens ORDER BY created_at DESC LIMIT 5;"
# Android debug commands
adb logcat | findstr "FCM"
adb shell pm list packages | findstr gosojorn
adb uninstall com.gosojorn.app
adb shell dumpsys notification | findstr gosojorn
Expected Behavior
When working correctly:
Web
- App starts → User grants notification permission
- Token generated →
FCM token registered (web): ... - Token synced → Token appears in database
- Message sent → Backend sends push → Notification appears in browser
Android
- App starts →
[FCM] Initializing for platform: android - Permission requested → User grants →
[FCM] Permission status: AuthorizationStatus.authorized - Token generated →
[FCM] Token registered (android): eXaMpLe... - Token synced →
[FCM] Token synced with Go Backend successfully - Message sent → Backend sends push →
[FCM] Foreground message received - Notification appears in Android notification tray
Files Modified During Implementation
sojorn_app/lib/config/firebase_web_config.dart- Added VAPID key placeholdergo-backend/.env.example- Updated FCM configuration formatsojorn_app/android/app/google-services.json- Firebase Android configurationsojorn_app/android/app/build.gradle.kts- Gradle configurationsojorn_app/android/app/src/main/AndroidManifest.xml- Permissions and metadatasojorn_app/lib/services/notification_service.dart- Enhanced logging for debugging
Next Steps After Deployment
- Monitor logs for FCM errors
- Test notifications with real users
- Check FCM token count grows as users log in
- Verify push notifications work on:
- Chrome (desktop & mobile)
- Firefox (desktop & mobile)
- Safari (if supported)
- Edge
- Android devices
Support
If you encounter issues:
- Check logs:
sudo journalctl -u sojorn-api -f - Verify configuration:
sudo cat /opt/sojorn/.env | grep FIREBASE - Test JSON validity:
cat /opt/sojorn/firebase-service-account.json | jq . - Check Firebase Console for errors: https://console.firebase.google.com/project/sojorn-a7a78/notification
- For Android issues, share logcat output:
adb logcat | findstr "FCM"
Last Updated: January 30, 2026 Status: ✅ Web notifications working, Android setup in progress