sojorn/sojorn_docs/FCM_COMPREHENSIVE_GUIDE.md

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)

  1. Get VAPID key from Firebase Console
  2. Download Firebase service account JSON
  3. Update Flutter app with VAPID key
  4. Upload JSON to server at /opt/sojorn/firebase-service-account.json
  5. Add to /opt/sojorn/.env: FIREBASE_CREDENTIALS_FILE=/opt/sojorn/firebase-service-account.json
  6. Restart Go backend
  7. Test notifications

Architecture Overview

How FCM Works in Sojorn

  1. User opens app → Flutter requests notification permission
  2. Permission granted → Firebase generates FCM token
  3. Token sent to backend → Stored in fcm_tokens table
  4. Event occurs (new message, follow, etc.) → Go backend calls PushService.SendPush()
  5. FCM sends notification → User's device/browser receives it
  6. 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.json for 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)

  1. Go to https://console.firebase.google.com/project/sojorn-a7a78/settings/cloudmessaging
  2. Scroll to Web configuration section
  3. Under Web Push certificates, copy the Key pair
  4. It should look like: BNxS7_very_long_string_of_characters...

B. Download Service Account JSON (for Server)

  1. Go to https://console.firebase.google.com/project/sojorn-a7a78/settings/serviceaccounts
  2. Click Generate new private key
  3. Click Generate key - downloads JSON file
  4. 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

  1. google-services.json: Download from Firebase Console for Android app
  2. Package Name: Must match com.gosojorn.app
  3. Build Configuration: Proper Gradle setup
  4. 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

  1. Open Sojorn web app in browser
  2. Open DevTools (F12) > Console
  3. Look for: FCM token registered (web): d2n2ELGKel7yzPL3wZLGSe...
  4. If you see "Web push is missing FIREBASE_WEB_VAPID_KEY", VAPID key is not set correctly

Android

  1. Run the app: cd c:\Webs\Sojorn && .\run_dev.ps1
  2. Check logs: adb logcat | findstr "FCM"
  3. 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

  1. Open two browser windows (or use two different users)
  2. User A sends a chat message to User B
  3. 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:

  1. Update firebase_web_config.dart with actual VAPID key
  2. Hot restart Flutter app
  3. 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:

  1. Verify google-services.json package name matches: "package_name": "com.gosojorn.app"
  2. Check build.gradle.kts has: applicationId = "com.gosojorn.app"
  3. Rebuild: flutter clean && flutter pub get && flutter run

Issue: Android "Permission denied"

Cause: User denied notification permission or Android 13+ permission not requested

Fix:

  1. Check AndroidManifest.xml has: <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
  2. On Android 13+, permission must be requested at runtime
  3. 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:

  1. Open DevTools > Application > Service Workers
  2. Should see firebase-messaging-sw.js registered
  3. If not, check sojorn_app/web/firebase-messaging-sw.js exists

Debug Checklist for Android

Run through this checklist:

  • google-services.json exists in android/app/
  • Package name matches in all files
  • build.gradle.kts has google-services plugin
  • AndroidManifest.xml has POST_NOTIFICATIONS permission
  • App has notification permission granted
  • Android logs show FCM initialization
  • Android logs show token generated
  • Token appears in database fcm_tokens table
  • Backend logs show notification being sent
  • Android logs show notification received

Current Configuration

Firebase Project:

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

  1. App starts → User grants notification permission
  2. Token generated → FCM token registered (web): ...
  3. Token synced → Token appears in database
  4. Message sent → Backend sends push → Notification appears in browser

Android

  1. App starts → [FCM] Initializing for platform: android
  2. Permission requested → User grants → [FCM] Permission status: AuthorizationStatus.authorized
  3. Token generated → [FCM] Token registered (android): eXaMpLe...
  4. Token synced → [FCM] Token synced with Go Backend successfully
  5. Message sent → Backend sends push → [FCM] Foreground message received
  6. Notification appears in Android notification tray

Files Modified During Implementation

  1. sojorn_app/lib/config/firebase_web_config.dart - Added VAPID key placeholder
  2. go-backend/.env.example - Updated FCM configuration format
  3. sojorn_app/android/app/google-services.json - Firebase Android configuration
  4. sojorn_app/android/app/build.gradle.kts - Gradle configuration
  5. sojorn_app/android/app/src/main/AndroidManifest.xml - Permissions and metadata
  6. sojorn_app/lib/services/notification_service.dart - Enhanced logging for debugging

Next Steps After Deployment

  1. Monitor logs for FCM errors
  2. Test notifications with real users
  3. Check FCM token count grows as users log in
  4. Verify push notifications work on:
    • Chrome (desktop & mobile)
    • Firefox (desktop & mobile)
    • Safari (if supported)
    • Edge
    • Android devices

Support

If you encounter issues:

  1. Check logs: sudo journalctl -u sojorn-api -f
  2. Verify configuration: sudo cat /opt/sojorn/.env | grep FIREBASE
  3. Test JSON validity: cat /opt/sojorn/firebase-service-account.json | jq .
  4. Check Firebase Console for errors: https://console.firebase.google.com/project/sojorn-a7a78/notification
  5. For Android issues, share logcat output: adb logcat | findstr "FCM"

Last Updated: January 30, 2026 Status: Web notifications working, Android setup in progress