463 lines
13 KiB
Markdown
463 lines
13 KiB
Markdown
# 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
|
|
<key>UIBackgroundModes</key>
|
|
<array>
|
|
<string>fetch</string>
|
|
<string>remote-notification</string>
|
|
</array>
|
|
```
|
|
|
|
Enable capabilities in Xcode:
|
|
- **Push Notifications**
|
|
- **Background Modes** → **Remote notifications**
|
|
|
|
## API Endpoints
|
|
|
|
### Device Registration
|
|
|
|
#### Register Device
|
|
```http
|
|
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:
|
|
```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 <JWT_TOKEN>
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"fcmToken": "new-token-here",
|
|
"apnsToken": "new-token-here"
|
|
}
|
|
```
|
|
|
|
#### Get Registered Devices
|
|
```http
|
|
GET /api/v1/devices
|
|
Authorization: Bearer <JWT_TOKEN>
|
|
```
|
|
|
|
#### Deregister Device
|
|
```http
|
|
DELETE /api/v1/devices/:deviceId
|
|
Authorization: Bearer <JWT_TOKEN>
|
|
```
|
|
|
|
## 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
|