Files
Kordant/docs/PUSH_NOTIFICATIONS_SETUP.md

13 KiB

Push Notifications Setup Guide (FCM & APNs)

Overview

This guide covers setting up Firebase Cloud Messaging (FCM) for Android and Apple Push Notification service (APNs) for iOS push notifications in the ShieldAI mobile app.

Architecture

┌─────────────┐     ┌──────────────────┐     ┌─────────────────┐
│   Mobile    │────▶│   API Gateway    │────▶│  Notification   │
│    App      │     │  /devices/*      │     │     Service     │
└─────────────┘     └──────────────────┘     └────────┬────────┘
                                                       │
                        ┌──────────────────────────────┼──────────────────────┐
                        │                               │                      │
                        ▼                               ▼                      ▼
               ┌────────────────┐            ┌─────────────────┐    ┌──────────────────┐
               │  Firebase      │            │  Apple Push     │    │  Redis (Rate     │
               │  Cloud         │            │  Notification   │    │  Limiting)       │
               │  Messaging     │            │  Service        │    │                  │
               │  (Android)     │            │  (iOS)          │    │                  │
               └────────────────┘            └─────────────────┘    └──────────────────┘

Environment Variables

Add the following to your .env file:

# Firebase Cloud Messaging (FCM)
FCM_PROJECT_ID=your-firebase-project-id
FCM_CLIENT_EMAIL=service-account@your-project.iam.gserviceaccount.com
FCM_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"

# Apple Push Notification (APNs)
APNS_KEY_ID=your-key-id
APNS_TEAM_ID=your-team-id
APNS_BUNDLE_ID=com.yourcompany.shieldai
APNS_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"

# Redis (for rate limiting)
REDIS_URL=redis://localhost:6379

# Rate Limits
PUSH_RATE_LIMIT=100
RATE_LIMIT_WINDOW_SECONDS=60

Firebase Cloud Messaging (FCM) Setup

1. Create Firebase Project

  1. Go to Firebase Console
  2. Click Add project
  3. Enter project name: shieldai-production (or your preferred name)
  4. Disable Google Analytics (optional)
  5. Click Create project

2. Enable Cloud Messaging

  1. In Firebase Console, go to Project settings
  2. Scroll down to Your apps section
  3. Click the Web icon </> to add a web app
  4. Register app nickname: ShieldAI API
  5. Do not check "Also set up for Firebase Hosting"
  6. Click Register app
  7. Copy the firebaseConfig for later use in mobile app

3. Generate Service Account Key

  1. Go to Project settingsService accounts
  2. Click Generate new private key
  3. Save the downloaded JSON file securely
  4. Extract the following values:
    • project_id
    • client_email
    • private_key

4. Configure FCM in ShieldAI

Create a file packages/shared-notifications/.fcm-config.json (gitignored):

{
  "projectId": "your-firebase-project-id",
  "clientEmail": "service-account@your-project.iam.gserviceaccount.com",
  "privateKey": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
}

Set environment variables:

export FCM_PROJECT_ID="your-firebase-project-id"
export FCM_CLIENT_EMAIL="service-account@your-project.iam.gserviceaccount.com"
export FCM_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"

5. Install Firebase Admin SDK

cd packages/shared-notifications
npm install firebase-admin

Apple Push Notification (APNs) Setup

1. Create App ID

  1. Go to Apple Developer Portal
  2. Click + to create new identifier
  3. Select App IDsApp
  4. Enter description: ShieldAI Mobile
  5. Enter Bundle ID: com.yourcompany.shieldai
  6. Enable Push Notifications capability
  7. Click ContinueRegister

2. Create APNs Key

  1. Go to Certificates, IDs & ProfilesKeys
  2. Click + to create new key
  3. Enter key name: ShieldAI APNs Key
  4. Enable Apple Push Notification service (APNs)
  5. Click ContinueRegister
  6. Download the key (.p8 file) - you can only download once!
  7. Note the Key ID displayed

3. Configure APNs in ShieldAI

Convert the .p8 file to PEM format:

# Convert .p8 to PEM
openssl pkcs8 -topk8 -nocrypt -in AuthKey_XXXXXX.p8 -out apns_key.pem

# Copy the PEM content
cat apns_key.pem

Set environment variables:

export APNS_KEY_ID="XXXXXX"
export APNS_TEAM_ID="YYYYYY"
export APNS_BUNDLE_ID="com.yourcompany.shieldai"
export APNS_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"

4. Configure in Xcode

In your iOS app's Info.plist:

<key>UIBackgroundModes</key>
<array>
  <string>fetch</string>
  <string>remote-notification</string>
</array>

Enable capabilities in Xcode:

  • Push Notifications
  • Background ModesRemote notifications

API Endpoints

Device Registration

Register Device

POST /api/v1/devices/register
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json

{
  "platform": "android",
  "fcmToken": "eXwR9...",
  "appVersion": "1.0.0",
  "osVersion": "Android 13"
}

Response:

{
  "success": true,
  "device": {
    "deviceId": "dev_1234567890_abc123",
    "platform": "android",
    "registeredAt": "2026-05-14T13:00:00.000Z"
  },
  "message": "Device registered successfully"
}

Update Device Tokens

PUT /api/v1/devices/:deviceId/tokens
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json

{
  "fcmToken": "new-token-here",
  "apnsToken": "new-token-here"
}

Get Registered Devices

GET /api/v1/devices
Authorization: Bearer <JWT_TOKEN>

Deregister Device

DELETE /api/v1/devices/:deviceId
Authorization: Bearer <JWT_TOKEN>

Mobile App Integration

Android (React Native)

import messaging from '@react-native-firebase/messaging';
import { API } from '@shieldai/api-client';

// Request permission
async function requestPushPermission() {
  const authStatus = await messaging().requestPermission();
  const enabled =
    authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
    authStatus === messaging.AuthorizationStatus.PROVISIONAL;

  if (enabled) {
    console.log('Push permission granted:', authStatus);
  }
}

// Get FCM token
async function getFCMToken() {
  const token = await messaging().getToken();
  return token;
}

// Register device with API
async function registerDevice() {
  try {
    const fcmToken = await getFCMToken();
    
    const response = await API.post('/devices/register', {
      platform: 'android',
      fcmToken,
      appVersion: '1.0.0',
      osVersion: Platform.OS + ' ' + Platform.Version,
    });

    console.log('Device registered:', response.data);
  } catch (error) {
    console.error('Failed to register device:', error);
  }
}

// Listen for token refresh
messaging().onTokenRefresh(async (newToken) => {
  await API.put('/devices/tokens', {
    fcmToken: newToken,
  });
});

// Handle foreground messages
messaging().onMessage(async (remoteMessage) => {
  console.log('Foreground message received:', remoteMessage);
  // Show local notification
});

// Handle background messages
messaging().setBackgroundMessageHandler(async remoteMessage => {
  console.log('Background message received:', remoteMessage);
});

iOS (React Native)

import messaging from '@react-native-firebase/messaging';
import { API } from '@shieldai/api-client';

// Request permission
async function requestPushPermission() {
  const authStatus = await messaging().requestPermission();
  const enabled =
    authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
    authStatus === messaging.AuthorizationStatus.PROVISIONAL;

  if (enabled) {
    console.log('Push permission granted:', authStatus);
  }
}

// Get APNs token
async function getAPNSToken() {
  const token = await messaging().getToken();
  return token;
}

// Register device with API
async function registerDevice() {
  try {
    const apnsToken = await getAPNSToken();
    
    const response = await API.post('/devices/register', {
      platform: 'ios',
      apnsToken,
      appVersion: '1.0.0',
      osVersion: Platform.OS + ' ' + Platform.Version,
    });

    console.log('Device registered:', response.data);
  } catch (error) {
    console.error('Failed to register device:', error);
  }
}

// Handle foreground messages
messaging().onMessage(async (remoteMessage => {
  console.log('Foreground message received:', remoteMessage);
  // Show local notification
}));

Testing Push Notifications

Test with Firebase Console

  1. Go to Firebase ConsoleCloud Messaging
  2. Click Send your first message
  3. Enter notification title and body
  4. Choose test device or all users
  5. Click Send test message

Test with API

# Send test push notification
curl -X POST https://your-api.com/api/v1/notifications/send \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "user-123",
    "channel": "push",
    "title": "Test Notification",
    "body": "This is a test push notification",
    "data": {
      "type": "test",
      "action": "open_app"
    }
  }'

Test with cURL (Direct FCM)

curl -X POST https://fcm.googleapis.com/v1/projects/your-project-id/messages:send \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" \
  -d '{
    "message": {
      "token": "FCM_TOKEN_HERE",
      "notification": {
        "title": "Test Title",
        "body": "Test Body"
      },
      "data": {
        "type": "test"
      }
    }
  }'

Production Checklist

FCM Configuration

  • Firebase project created
  • Service account key generated and stored securely
  • FCM environment variables configured
  • Firebase Admin SDK initialized
  • Test notifications sent successfully

APNs Configuration

  • App ID created with push capability
  • APNs key generated and downloaded
  • Key converted to PEM format
  • APNs environment variables configured
  • Xcode capabilities enabled
  • Test notifications sent from simulator

API Configuration

  • Device registration endpoints working
  • Token update endpoints working
  • Rate limiting configured
  • Error handling implemented
  • Logging for push notifications

Mobile App Configuration

  • Push permissions requested
  • Token retrieval implemented
  • Device registration on app start
  • Token refresh handling
  • Foreground message handling
  • Background message handling
  • Notification display implemented

Troubleshooting

FCM Token Not Received

  • Check Firebase configuration in mobile app
  • Verify Google Services (Android) / GoogleService-Info.plist (iOS)
  • Ensure push permissions granted

APNs Token Not Received

  • Verify App ID configuration in Apple Developer
  • Check APNs key configuration
  • Ensure background modes enabled in Xcode
  • Test on actual device (not simulator)

Notifications Not Delivering

  • Check device registration status
  • Verify tokens are valid
  • Check rate limiting status
  • Review server logs for errors

Token Refresh Issues

  • Listen for onTokenRefresh events
  • Update tokens via API immediately
  • Handle network errors gracefully

Security Considerations

  1. Never expose service account keys in client-side code
  2. Always validate device ownership on server
  3. Use HTTPS for all API calls
  4. Implement rate limiting to prevent abuse
  5. Store tokens securely using secure storage libraries
  6. Deregister devices on user logout

Monitoring

Monitor the following metrics:

  • Push notification delivery rate
  • Token refresh frequency
  • Device registration failures
  • Rate limit hits
  • Notification open rate

Support