Push Notification Integration

This document describes the Firebase Cloud Messaging (FCM) integration for biometric confirmation push notifications in the ms-auth service.

Overview

The push notification system sends real-time notifications to mobile devices when web users initiate actions that require biometric confirmation. This enables secure, cross-device authentication workflows where users can approve sensitive actions using their mobile devices.

Architecture

Components

  1. Notification Service (internal/services/notification.go)

    • Handles FCM client initialization
    • Manages notification templates and delivery
    • Tracks notification status and failures
    • Provides audit logging
  2. Database Schema

    • notifications table: Tracks sent notifications and delivery status
    • registered_devices.fcm_token column: Stores FCM tokens for each device
  3. Firebase Integration

    • Uses Firebase Admin SDK for server-to-server communication
    • Supports iOS, Android, and Web Push notifications
    • Handles priority levels and platform-specific configurations

Workflow

  1. Web user initiates sensitive action (payment, document approval, etc.)
  2. System creates confirmation session in database
  3. Notification service finds all user devices with FCM tokens
  4. System generates platform-specific notification templates
  5. FCM messages sent to all registered devices
  6. Mobile app receives notification with deep-link data
  7. User opens app and completes biometric confirmation
  8. Confirmation status updated and web session completes

Configuration

Environment Variables

# Firebase Configuration
FCM_ENABLED=true
FCM_PROJECT_ID=your-firebase-project-id
FCM_CREDENTIALS_FILE=/etc/secrets/firebase-service-account.json

Firebase Service Account

  1. Go to Firebase Console → Project Settings → Service Accounts
  2. Generate new private key
  3. Save JSON file as specified in FCM_CREDENTIALS_FILE
  4. Ensure the service account has "Firebase Admin SDK Administrator" role

Example Service Account JSON

{
  "type": "service_account",
  "project_id": "your-firebase-project-id",
  "private_key_id": "...",
  "private_key": "-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n",
  "client_email": "[email protected]",
  "client_id": "...",
  "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": "...",
  "universe_domain": "googleapis.com"
}

API Endpoints

Update FCM Token

Updates the FCM token for a registered device to enable push notifications.

PUT /api/v1/auth/devices/fcm-token
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "deviceId": "device-uuid",
  "fcmToken": "fcm-registration-token"
}

Response:

{
  "success": true,
  "message": "FCM token updated successfully"
}

Notification Templates

The system supports various notification types with localized templates:

Confirmation Request Types

  1. Payment Approval

    • Title: "Action Confirmation Required"
    • Body: "Approve payment of {amount} {currency}"
    • Deep link: Contains confirmation ID and payment details
  2. Document Approval

    • Title: "Action Confirmation Required"
    • Body: "Approve document: {documentName}"
    • Deep link: Contains document reference
  3. Purchase Order

    • Title: "Action Confirmation Required"
    • Body: "Approve purchase order from {supplier} ({amount} VND)"
    • Deep link: Contains supplier and amount details
  4. User Access

    • Title: "Action Confirmation Required"
    • Body: "Approve access for user: {username}"
    • Deep link: Contains user details

Platform-Specific Configuration

Android

  • Channel ID: biometric_confirmations
  • Icon: ic_notification
  • Color: #2196F3
  • Click Action: OPEN_CONFIRMATION

iOS (APNS)

  • Category: BIOMETRIC_CONFIRMATION
  • Action: CONFIRM_ACTION
  • Sound: default

Web Push

  • Icons: /icons/notification-icon.png
  • Actions: Confirm/Reject buttons
  • Require interaction for high priority

Database Schema

Notifications Table

CREATE TABLE notifications (
    id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    device_id VARCHAR(255) REFERENCES registered_devices(id) ON DELETE SET NULL,
    confirmation_id VARCHAR(255) REFERENCES confirmation_sessions(id) ON DELETE SET NULL,
    notification_type VARCHAR(50) NOT NULL,
    title VARCHAR(255) NOT NULL,
    body TEXT NOT NULL,
    data JSONB,
    fcm_message_id VARCHAR(255),
    delivery_status VARCHAR(50) NOT NULL DEFAULT 'pending',
    error_message TEXT,
    sent_at TIMESTAMP WITH TIME ZONE,
    delivered_at TIMESTAMP WITH TIME ZONE,
    expires_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

FCM Token Column

ALTER TABLE registered_devices ADD COLUMN fcm_token VARCHAR(255);
CREATE INDEX idx_registered_devices_fcm_token ON registered_devices(fcm_token) WHERE fcm_token IS NOT NULL;

Client Implementation

Mobile App Integration

  1. FCM Token Registration

    // Get FCM token on app start
    const fcmToken = await messaging().getToken();
    
    // Update token on server
    await fetch('/api/v1/auth/devices/fcm-token', {
      method: 'PUT',
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        deviceId: deviceId,
        fcmToken: fcmToken
      })
    });
  2. Handle Notification Received

    messaging().onMessage(async remoteMessage => {
      if (remoteMessage.data.type === 'confirmation_request') {
        // Show in-app notification
        // Navigate to confirmation screen
        navigation.navigate('BiometricConfirmation', {
          confirmationId: remoteMessage.data.confirmationId,
          actionType: remoteMessage.data.actionType
        });
      }
    });
  3. Handle Background/Quit State

    messaging().onNotificationOpenedApp(remoteMessage => {
      // Handle notification tap when app is in background
      if (remoteMessage.data.confirmationId) {
        navigation.navigate('BiometricConfirmation', {
          confirmationId: remoteMessage.data.confirmationId
        });
      }
    });

Security Considerations

Token Security

  • FCM tokens are stored encrypted in database
  • Tokens are rotated automatically by Firebase
  • Invalid tokens are cleaned up during send attempts

Message Security

  • All notifications include tamperproof confirmation IDs
  • Deep link data is validated server-side
  • Sensitive data (amounts, names) is included only in display text

Audit Logging

  • All notification attempts are logged
  • Failed deliveries are tracked and retried
  • Security violations logged for suspicious activity

Error Handling

Common Error Scenarios

  1. Invalid FCM Token

    • Token expired or unregistered
    • Automatic cleanup from database
    • User prompted to re-register on next app open
  2. Firebase Service Unavailable

    • Notifications queued for retry
    • Fallback to email notifications if configured
    • Admin alerts for service outages
  3. Device Not Found

    • User has no registered devices with FCM tokens
    • Silent failure (not an error condition)
    • Web UI shows "no mobile devices" message

Monitoring and Analytics

Delivery Metrics

  • Notification send rate and success rate
  • Device-level delivery statistics
  • User engagement metrics (open rates)

Performance Monitoring

  • FCM API response times
  • Database query performance
  • Background job processing times

Alerts

  • High failure rates (>10% in 5 minutes)
  • Firebase service account issues
  • Database connectivity problems

Testing

Development Setup

  1. Create Firebase test project
  2. Generate test service account key
  3. Use Firebase Console to send test messages
  4. Verify end-to-end flow with test devices

Integration Tests

  • Mock FCM client for unit tests
  • Test notification template generation
  • Verify database state changes
  • Validate error handling scenarios

Deployment

Docker Configuration

# Ensure secrets directory exists
RUN mkdir -p /etc/secrets

# Mount service account key
VOLUME /etc/secrets

Kubernetes Deployment

apiVersion: v1
kind: Secret
metadata:
  name: firebase-service-account
type: Opaque
data:
  firebase-service-account.json: <base64-encoded-json>

---
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: ms-auth
        env:
        - name: FCM_ENABLED
          value: "true"
        - name: FCM_PROJECT_ID
          value: "your-project-id"
        - name: FCM_CREDENTIALS_FILE
          value: "/etc/secrets/firebase-service-account.json"
        volumeMounts:
        - name: firebase-credentials
          mountPath: /etc/secrets
          readOnly: true
      volumes:
      - name: firebase-credentials
        secret:
          secretName: firebase-service-account

Troubleshooting

Common Issues

  1. Notifications not received

    • Check FCM token validity in database
    • Verify Firebase project configuration
    • Check device network connectivity and app permissions
  2. High failure rates

    • Monitor Firebase quotas and limits
    • Check service account permissions
    • Verify JSON key file format
  3. Performance issues

    • Monitor database query performance
    • Check FCM API response times
    • Optimize notification batching

Debug Commands

# Check FCM token registration
curl -X GET "http://localhost:8080/api/v1/auth/devices" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# Update FCM token manually
curl -X PUT "http://localhost:8080/api/v1/auth/devices/fcm-token" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"deviceId":"device-id","fcmToken":"fcm-token"}'

Logs to Monitor

# Notification service initialization
grep "Notification service initialized" /var/log/ms-auth.log

# FCM token updates
grep "FCM token updated" /var/log/ms-auth.log

# Notification delivery
grep "Notification sent successfully" /var/log/ms-auth.log

# Failures
grep "Failed to send" /var/log/ms-auth.log