🎯 Learning Path:
Welcome to the REL-ID Push Notification Integration codelab! This tutorial enhances your existing REL-ID application with secure push notification capabilities using REL-ID SDK's setDeviceToken API.
In this codelab, you'll enhance your existing REL-ID application with:
setDeviceToken APIBy completing this codelab, you'll master:
Before starting this codelab, ensure you have:
The code to get started can be found in a GitHub repository.
You can clone the repository using the following command:
git clone https://github.com/uniken-public/codelab-react-native.git
Navigate to the relid-push-notification-token folder in the repository you cloned earlier
REL-ID push notifications provide a secure, two-channel architecture that goes beyond standard push messaging with secure wake-up signals, MITM-proof channels, transaction approvals, and device-bound credentials.
REL-ID Flow: FCM Wake-up → App Launch → Secure REL-ID Channel → Encrypted Data Retrieval → User Action
This codelab implements two core components:
Before implementing push notifications, let's understand how REL-ID's secure notification system works.
REL-ID uses a sophisticated two-channel approach for maximum security:
📱 REL-ID Server → FCM/APNS (Wake-up Signal) → Mobile App → REL-ID Secure Channel → Encrypted Data
Here's how setDeviceToken() enables secure REL-ID communications:
// Device Registration Flow
FCM Token Generation → setDeviceToken(token) → REL-ID Backend Registration →
Secure Channel Establishment → Transaction Approval Capability
Step | Description | Security Benefit |
1. FCM Token | Generate platform-specific device identifier | Device uniqueness |
2. REL-ID Registration |
| Device-server binding |
3. Secure Channel | Establish encrypted communication channel | MITM protection |
4. Transaction Support | Enable approve/reject actions with MFA | Multi-factor security |
Once integrated, your app can handle these secure notification types:
Firebase Role: Provides the platform infrastructure (FCM token generation)
REL-ID Role: Provides the secure communication and transaction approval capabilities
Now let's implement the core push notification service that handles FCM token management and REL-ID integration.
First, add the device token registration method to your existing REL-ID service:
// src/uniken/services/rdnaService.ts (addition to existing class)
/**
* Registers device push notification token with REL-ID SDK
* enabling the server to send secure push notifications to this specific device.
* The token is used to establish a secure communication channel for:
* - Transaction approvals
* - Authentication challenges
* - Security alerts
* - Multi-factor authentication requests
* This method registers the device's FCM/APNS push notification token with the REL-ID SDK.
* The token is used by the backend to send push notifications to this specific device.
* Unlike other REL-ID APIs, this method is synchronous and doesn't use callbacks.
*
* @param token The FCM (Android) or APNS (iOS) device token string
* @throws Error if token registration fails
*/
setDeviceToken(token: string): void {
console.log('RdnaService - Registering device push token with REL-ID SDK');
console.log('RdnaService - Token length:', token.length);
try {
// Register token with REL-ID native SDK
RdnaClient.setDeviceToken(token);
console.log('RdnaService - Device push token registration successful');
} catch (error) {
console.error('RdnaService - Device push token registration failed:', error);
throw new Error(`Failed to register device push token: ${error}`);
}
}
Now create the singleton service that manages all push notification functionality for both Android and iOS:
// src/uniken/services/pushNotificationService.ts
/**
* Push Notification Service
*
* Cross-platform FCM integration for REL-ID SDK (Android & iOS).
* Handles token registration with REL-ID backend via rdnaService.setDeviceToken().
*
* Features:
* - Android & iOS FCM token retrieval and registration
* - Android 13+ POST_NOTIFICATIONS permission handling
* - iOS authorization via @react-native-firebase/messaging (no AppDelegate changes needed)
* - Automatic token refresh handling
* - REL-ID SDK integration
*
* iOS Note: Requires GoogleService-Info.plist and APNS certificate uploaded to Firebase Console.
* The @react-native-firebase/messaging library handles APNS delegate methods automatically via swizzling.
*
* Usage:
* const pushService = PushNotificationService.getInstance();
* await pushService.initialize();
*/
import { Platform, PermissionsAndroid } from 'react-native';
import { getApp, getApps, initializeApp } from '@react-native-firebase/app';
import messaging from '@react-native-firebase/messaging';
import { RdnaService } from './rdnaService';
/**
* Push Notification Service
* Cross-platform singleton for FCM token management (Android & iOS)
*/
export class PushNotificationService {
private static instance: PushNotificationService;
private rdnaService: RdnaService;
private isInitialized: boolean = false;
private constructor() {
this.rdnaService = RdnaService.getInstance();
}
/**
* Gets singleton instance
*/
static getInstance(): PushNotificationService {
if (!PushNotificationService.instance) {
PushNotificationService.instance = new PushNotificationService();
}
return PushNotificationService.instance;
}
/**
* Initialize FCM and register token with REL-ID SDK
* Supports both Android and iOS platforms
*/
async initialize(): Promise<void> {
if (this.isInitialized) {
console.log('PushNotificationService - Already initialized');
return;
}
console.log(`PushNotificationService - Starting FCM initialization for ${Platform.OS}`);
try {
// Ensure Firebase is initialized (auto-init or manual fallback)
await this.ensureFirebaseInitialized();
// Request permissions (handles both Android and iOS)
const hasPermission = await this.requestPermissions();
if (!hasPermission) {
console.warn(`PushNotificationService - Permission not granted on ${Platform.OS}`);
return;
}
// Get and register initial token
await this.getAndRegisterToken();
// Set up token refresh listener
this.setupTokenRefreshListener();
this.isInitialized = true;
console.log(`PushNotificationService - Initialization complete for ${Platform.OS}`);
} catch (error) {
console.error('PushNotificationService - Initialization failed:', error);
throw error;
}
}
/**
* Ensure Firebase is initialized
* React Native Firebase auto-initializes natively, this just verifies it's ready
*/
private async ensureFirebaseInitialized(): Promise<void> {
try {
// Give native auto-initialization a moment to complete
// React Native Firebase initializes from native side before JS loads
await new Promise(resolve => setTimeout(resolve, 100));
const apps = getApps();
if (apps.length === 0) {
console.warn('PushNotificationService - No Firebase app found after native init');
console.warn('PushNotificationService - This may indicate GoogleService-Info.plist is not being loaded');
throw new Error('Firebase failed to auto-initialize. Check GoogleService-Info.plist configuration.');
} else {
console.log('PushNotificationService - Firebase already initialized via native auto-init');
}
} catch (error) {
console.error('PushNotificationService - Firebase initialization check failed:', error);
throw error;
}
}
/**
* Request FCM permissions
* Android: Handles POST_NOTIFICATIONS permission for Android 13+
* iOS: Requests notification authorization (Alert, Sound, Badge)
*/
private async requestPermissions(): Promise<boolean> {
try {
console.log('PushNotificationService - Platform OS:', Platform.OS);
console.log('PushNotificationService - Platform Version:', Platform.Version);
// Android 13+ (API 33+) requires POST_NOTIFICATIONS permission
if (Platform.OS === 'android' && Platform.Version >= 33) {
console.log('PushNotificationService - Requesting POST_NOTIFICATIONS permission (Android 13+)');
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
console.log('PushNotificationService - POST_NOTIFICATIONS result:', granted);
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
console.log('PushNotificationService - POST_NOTIFICATIONS permission denied');
return false;
}
}
// Request FCM authorization (works for both Android and iOS)
console.log(`PushNotificationService - Requesting FCM authorization for ${Platform.OS}`);
// Use modular API - get app then messaging
const app = getApp();
const messagingInstance = messaging(app);
console.log('PushNotificationService - Got messaging instance');
const authStatus = await messagingInstance.requestPermission();
console.log('PushNotificationService - FCM auth status:', authStatus);
console.log('PushNotificationService - AUTHORIZED value:', messaging.AuthorizationStatus.AUTHORIZED);
console.log('PushNotificationService - PROVISIONAL value:', messaging.AuthorizationStatus.PROVISIONAL);
// iOS supports PROVISIONAL authorization (quiet notifications without prompt)
const enabled = authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
console.log('PushNotificationService - FCM permission:', enabled ? 'granted' : 'denied');
return enabled;
} catch (error) {
console.error('PushNotificationService - Permission request failed:', error);
return false;
}
}
/**
* Get FCM token and register with REL-ID SDK
* Android: Gets FCM registration token
* iOS: Gets FCM token (mapped from APNS token by Firebase automatically)
*/
private async getAndRegisterToken(): Promise<void> {
try {
console.log(`PushNotificationService - Getting FCM token for ${Platform.OS}`);
const app = getApp();
// On iOS, check if APNS token is available first
if (Platform.OS === 'ios') {
const apnsToken = await messaging(app).getAPNSToken();
if (apnsToken) {
console.log('PushNotificationService - iOS APNS token available, length:', apnsToken.length);
} else {
console.log('PushNotificationService - iOS APNS token not yet available, will retry via getToken()');
}
}
const token = await messaging(app).getToken();
if (!token) {
console.warn(`PushNotificationService - No FCM token received for ${Platform.OS}`);
return;
}
console.log(`PushNotificationService - FCM token received for ${Platform.OS}, length:`, token.length);
console.log('PushNotificationService - FCM TOKEN:', token);
// Register with REL-ID SDK (works for both Android FCM and iOS FCM tokens)
this.rdnaService.setDeviceToken(token);
console.log('PushNotificationService - Token registered with REL-ID SDK');
} catch (error) {
console.error('PushNotificationService - Token registration failed:', error);
throw error;
}
}
/**
* Set up automatic token refresh
* Handles token refresh for both Android and iOS
*/
private setupTokenRefreshListener(): void {
console.log(`PushNotificationService - Setting up token refresh listener for ${Platform.OS}`);
const app = getApp();
messaging(app).onTokenRefresh(async (token) => {
console.log(`PushNotificationService - Token refreshed for ${Platform.OS}, length:`, token.length);
console.log('PushNotificationService - REFRESHED FCM TOKEN:', token);
try {
// Register new token with REL-ID SDK
this.rdnaService.setDeviceToken(token);
console.log('PushNotificationService - Refreshed token registered with REL-ID SDK');
} catch (error) {
console.error('PushNotificationService - Token refresh registration failed:', error);
}
});
}
/**
* Get current FCM token (for debugging)
* Works for both Android and iOS
*/
async getCurrentToken(): Promise<string | null> {
try {
const app = getApp();
return await messaging(app).getToken();
} catch (error) {
console.error('PushNotificationService - Failed to get current token:', error);
return null;
}
}
/**
* Cleanup (reset initialization state)
*/
cleanup(): void {
console.log('PushNotificationService - Cleanup');
this.isInitialized = false;
}
}
// Export singleton instance
const pushNotificationService = PushNotificationService.getInstance();
export default pushNotificationService;
This implementation follows enterprise-grade patterns:
Pattern | Benefit | Implementation |
Singleton | Single point of control |
|
Dependency Injection | Testable, maintainable | Constructor injection of |
Error Handling | Graceful failure management | Try-catch with logging |
State Management | Prevents double initialization |
|
Platform Abstraction | Cross-platform compatibility | Platform OS checks |
Now let's create a React context provider that automatically initializes push notifications when your app starts.
Build a clean provider component that integrates with your existing context hierarchy:
// src/uniken/providers/PushNotificationProvider.tsx
/**
* Push Notification Provider
*
* Ultra-simplified provider that initializes Android FCM push notifications
* and registers tokens directly with REL-ID SDK. No complex state management needed
* since the pushNotificationService singleton handles everything internally.
*
* Usage:
* <PushNotificationProvider>
* <App />
* </PushNotificationProvider>
*/
import React, { useEffect, ReactNode } from 'react';
import pushNotificationService from '../services/pushNotificationService';
/**
* Provider props
*/
interface PushNotificationProviderProps {
children: ReactNode;
}
/**
* Push Notification Provider Component
* Simply initializes FCM on mount and lets the service handle everything
*/
export const PushNotificationProvider: React.FC<PushNotificationProviderProps> = ({ children }) => {
useEffect(() => {
console.log('PushNotificationProvider - Initializing FCM');
pushNotificationService
.initialize()
.then(() => {
console.log('PushNotificationProvider - FCM initialization successful');
})
.catch((error) => {
console.error('PushNotificationProvider - FCM initialization failed:', error);
});
}, []);
return <>{children}</>;
};
export default PushNotificationProvider;
Add the new provider to your providers index:
// src/uniken/providers/index.ts
export { default as SDKEventProvider } from './SDKEventProvider';
export { default as PushNotificationProvider } from './PushNotificationProvider';
Update your main App component to include push notification initialization:
// App.tsx (integration example)
import React from 'react';
import { StatusBar } from 'react-native';
import AppNavigator from './src/tutorial/navigation/AppNavigator';
import {
SessionProvider,
MTDThreatProvider,
SDKEventProvider,
PushNotificationProvider // Add this import
} from './src/uniken';
const App: React.FC = () => {
return (
<SessionProvider>
<MTDThreatProvider>
<SDKEventProvider>
<PushNotificationProvider> {/* Add this provider */}
<StatusBar />
<AppNavigator />
</PushNotificationProvider>
</SDKEventProvider>
</MTDThreatProvider>
</SessionProvider>
);
};
export default App;
This approach provides several architectural advantages:
Benefit | Description | Implementation Detail |
Automatic Initialization | Push notifications start immediately when app launches |
|
Context Integration | Fits naturally into existing provider hierarchy | Nested within existing providers |
Error Isolation | Push notification failures don't crash the app | Try-catch in service layer |
Development Friendly | No complex state management needed | Service handles all complexity |
Production Ready | Graceful handling of permission denials and errors | Comprehensive error logging |
Let's thoroughly test your push notification implementation with comprehensive scenarios to ensure production readiness.
Prerequisites:
google-services.jsonTest Steps:
npx react-native run-android
✅ PushNotificationProvider - Initializing FCM
✅ PushNotificationService - Starting FCM initialization
✅ PushNotificationService - POST_NOTIFICATIONS result: granted
✅ PushNotificationService - FCM token received, length: 142
✅ RdnaService - Device push token registration successful
✅ PushNotificationService - Initialization complete
✅ PushNotificationService - Token refreshed, length: 142
✅ RdnaService - Refreshed token registered with REL-ID SDK
Expected Results:
Android 13+ Permission Test:
iOS Permission Test:
Expected Permission Flow:
📱 POST_NOTIFICATIONS permission request → User grants → FCM initialization
📱 POST_NOTIFICATIONS permission request → User denies → Graceful fallback
Test Missing google-services.json:
google-services.json to google-services.json.backupnpx react-native run-androidTest Google Services Plugin:
# Verify Google Services plugin processing
cd android && ./gradlew app:dependencies | grep google-services
Before deploying to production, verify:
google-services.json from production projectCongratulations! You've successfully implemented secure push notification integration with the REL-ID SDK. Here's your complete implementation overview.
✅ Secure Device Registration - FCM tokens registered with REL-ID backend for two-channel security ✅ Firebase Integration - Complete FCM setup with Google Services auto-initialization ✅ Production-Ready Service - Singleton architecture with error handling and token refresh ✅ Android Configuration - Google Services plugin, permissions, and network security ✅ Provider Integration - Clean React context integration with existing app architecture
src/uniken/services/
├── pushNotificationService.ts ✅ FCM token management singleton
└── rdnaService.ts ✅ Enhanced with setDeviceToken()
src/uniken/providers/
├── PushNotificationProvider.tsx ✅ React context provider
└── index.ts ✅ Updated exports
android/
├── build.gradle ✅ Google Services plugin
├── app/build.gradle ✅ Plugin application
├── app/google-services.json ✅ Firebase configuration
└── app/src/main/res/xml/
└── network_security_config.xml ✅ Development network config
App.tsx ✅ Provider integration
index.js ✅ Clean Firebase initialization
Your implementation demonstrates enterprise-grade patterns:
Component | Pattern | Benefit |
PushNotificationService | Singleton | Centralized token management |
PushNotificationProvider | Context Provider | Automatic initialization |
rdnaService Integration | Dependency Injection | Clean service layer |
Firebase Configuration | Auto-initialization | Zero-configuration startup |
Error Handling | Graceful Degradation | Production reliability |
Your REL-ID push notification integration now enables:
🎉 Congratulations!
You've successfully implemented secure push notification capabilities that integrate seamlessly with the REL-ID security ecosystem. Your app can now participate in secure, two-channel communications for transaction approvals, authentication challenges, and security notifications.
Your users now have a more secure, responsive authentication experience with the power of REL-ID's push notification infrastructure!