/** * React Native Unified Auth Provider * * Creates a platform-specific auth provider that automatically selects the appropriate storage: * - Web: Uses IndexedDBTokenStorage for DPoP (CryptoKey persistence) or LocalStorage * - Mobile: Uses AsyncStorage-based storage */ import { Platform } from 'react-native'; import { DefaultAuthProvider, AccountOwnerType, LocalStorageTokenStorage, IndexedDBTokenStorage } from '@explorins/pers-sdk/core'; import { ReactNativeSecureStorage } from '../storage/rn-secure-storage'; import type { TokenStorage } from '@explorins/pers-sdk/core'; // Use React Native's built-in platform detection const isWebPlatform = Platform.OS === 'web'; /** * Check if IndexedDB is available (web environments only) * Uses globalThis to avoid TypeScript errors in React Native context */ function hasIndexedDB(): boolean { return typeof globalThis !== 'undefined' && typeof (globalThis as Record).indexedDB !== 'undefined'; } /** * Configuration for React Native Auth Provider */ export interface ReactNativeAuthConfig { /** Custom storage key prefix */ keyPrefix?: string; /** Enable debug logging */ debug?: boolean; /** Custom token storage implementation */ customStorage?: TokenStorage; /** Authentication type (default: AccountOwnerType.USER) */ authType?: AccountOwnerType; /** DPoP Configuration (optional) */ dpop?: { enabled?: boolean; cryptoProvider?: import('@explorins/pers-sdk/core').DPoPCryptoProvider; }; } /** * Create a React Native auth provider with platform-appropriate storage * * Automatically selects storage implementation: * - Web: Uses LocalStorageTokenStorage (from core SDK) * - Mobile: Uses AsyncStorageTokenStorage (React Native specific) * * @param projectKey - PERS project key * @param config - Configuration options * @returns DefaultAuthProvider configured for the current platform */ export function createReactNativeAuthProvider( projectKey: string, config: ReactNativeAuthConfig = {} ): DefaultAuthProvider { if (!projectKey || typeof projectKey !== 'string') { throw new Error('createReactNativeAuthProvider: projectKey is required and must be a string'); } const { keyPrefix = `pers_${projectKey.slice(0, 8)}_`, debug = false, customStorage, authType = AccountOwnerType.USER, dpop } = config; // Platform-specific storage selection // Web with DPoP: Use IndexedDB for CryptoKey persistence // Web without DPoP or no IndexedDB: Use LocalStorage // Mobile: Use ReactNativeSecureStorage let tokenStorage: TokenStorage; if (customStorage) { tokenStorage = customStorage; } else if (isWebPlatform) { // On web, prefer IndexedDB when DPoP is enabled for proper CryptoKey persistence const dpopEnabled = dpop?.enabled ?? true; if (dpopEnabled && hasIndexedDB()) { tokenStorage = new IndexedDBTokenStorage(); } else { if (dpopEnabled && !hasIndexedDB()) { console.warn('[ReactNativeAuthProvider] DPoP requested but IndexedDB not available. Falling back to LocalStorage (DPoP will be disabled by DefaultAuthProvider).'); } tokenStorage = new LocalStorageTokenStorage(); } } else { tokenStorage = new ReactNativeSecureStorage(keyPrefix); } // Prepare DPoP config ensuring enabled is boolean (default true) const dpopConfig = dpop ? { enabled: dpop.enabled ?? true, cryptoProvider: dpop.cryptoProvider } : undefined; // Return DefaultAuthProvider configured with platform-appropriate storage return new DefaultAuthProvider({ authType, projectKey, storage: tokenStorage, dpop: dpopConfig }); }