# 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: ```bash # 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](https://console.firebase.google.com/) 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 settings** → **Service 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): ```json { "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: ```bash 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 ```bash cd packages/shared-notifications npm install firebase-admin ``` ## Apple Push Notification (APNs) Setup ### 1. Create App ID 1. Go to [Apple Developer Portal](https://developer.apple.com/account/resources/identifiers/list) 2. Click **+** to create new identifier 3. Select **App IDs** → **App** 4. Enter description: `ShieldAI Mobile` 5. Enter Bundle ID: `com.yourcompany.shieldai` 6. Enable **Push Notifications** capability 7. Click **Continue** → **Register** ### 2. Create APNs Key 1. Go to **Certificates, IDs & Profiles** → **Keys** 2. Click **+** to create new key 3. Enter key name: `ShieldAI APNs Key` 4. Enable **Apple Push Notification service (APNs)** 5. Click **Continue** → **Register** 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: ```bash # 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: ```bash 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`: ```xml UIBackgroundModes fetch remote-notification ``` Enable capabilities in Xcode: - **Push Notifications** - **Background Modes** → **Remote notifications** ## API Endpoints ### Device Registration #### Register Device ```http POST /api/v1/devices/register Authorization: Bearer Content-Type: application/json { "platform": "android", "fcmToken": "eXwR9...", "appVersion": "1.0.0", "osVersion": "Android 13" } ``` Response: ```json { "success": true, "device": { "deviceId": "dev_1234567890_abc123", "platform": "android", "registeredAt": "2026-05-14T13:00:00.000Z" }, "message": "Device registered successfully" } ``` #### Update Device Tokens ```http PUT /api/v1/devices/:deviceId/tokens Authorization: Bearer Content-Type: application/json { "fcmToken": "new-token-here", "apnsToken": "new-token-here" } ``` #### Get Registered Devices ```http GET /api/v1/devices Authorization: Bearer ``` #### Deregister Device ```http DELETE /api/v1/devices/:deviceId Authorization: Bearer ``` ## Mobile App Integration ### Android (React Native) ```javascript 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) ```javascript 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 Console](https://console.firebase.google.com/) → **Cloud 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 ```bash # 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) ```bash 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 - Firebase Support: https://firebase.google.com/support - Apple Developer Support: https://developer.apple.com/contact/ - FCM Documentation: https://firebase.google.com/docs/cloud-messaging - APNs Documentation: https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server