1import { NativeModules, AppState, Linking, Platform } from 'react-native'; 2 3const DevLauncherAuth = NativeModules.EXDevLauncherAuth; 4 5let redirectHandler: ((event: any) => void) | null = null; 6let onWebBrowserCloseAndroid: null | (() => void) = null; 7let isAppStateAvailable: boolean = AppState.currentState !== null; 8 9function onAppStateChangeAndroid(state: any) { 10 if (!isAppStateAvailable) { 11 isAppStateAvailable = true; 12 return; 13 } 14 15 if (state === 'active' && onWebBrowserCloseAndroid) { 16 onWebBrowserCloseAndroid(); 17 } 18} 19 20function stopWaitingForRedirect() { 21 if (!redirectHandler) { 22 throw new Error( 23 `The WebBrowser auth session is in an invalid state with no redirect handler when one should be set` 24 ); 25 } 26 27 Linking.removeEventListener('url', redirectHandler); 28 redirectHandler = null; 29} 30 31async function openBrowserAndWaitAndroidAsync(startUrl: string): Promise<any> { 32 const appStateChangedToActive = new Promise<void>((resolve) => { 33 onWebBrowserCloseAndroid = resolve; 34 AppState.addEventListener('change', onAppStateChangeAndroid); 35 }); 36 37 let result = { type: 'cancel' }; 38 await DevLauncherAuth.openWebBrowserAsync(startUrl); 39 const type = 'opened'; 40 41 if (type === 'opened') { 42 await appStateChangedToActive; 43 result = { type: 'dismiss' }; 44 } 45 46 AppState.removeEventListener('change', onAppStateChangeAndroid); 47 onWebBrowserCloseAndroid = null; 48 return result; 49} 50 51function waitForRedirectAsync(returnUrl: string): Promise<any> { 52 return new Promise((resolve) => { 53 redirectHandler = (event: any) => { 54 if (event.url.startsWith(returnUrl)) { 55 resolve({ url: event.url, type: 'success' }); 56 } 57 }; 58 59 Linking.addEventListener('url', redirectHandler); 60 }); 61} 62 63async function openAuthSessionPolyfillAsync(startUrl: string, returnUrl: string): Promise<any> { 64 if (redirectHandler) { 65 throw new Error( 66 `The WebBrowser's auth session is in an invalid state with a redirect handler set when it should not be` 67 ); 68 } 69 70 if (onWebBrowserCloseAndroid) { 71 throw new Error(`WebBrowser is already open, only one can be open at a time`); 72 } 73 74 try { 75 return await Promise.race([ 76 openBrowserAndWaitAndroidAsync(startUrl), 77 waitForRedirectAsync(returnUrl), 78 ]); 79 } finally { 80 stopWaitingForRedirect(); 81 } 82} 83 84export async function openAuthSessionAsync(url: string, returnUrl: string): Promise<any> { 85 if (DevLauncherAuth.openAuthSessionAsync) { 86 // iOS 87 return await DevLauncherAuth.openAuthSessionAsync(url, returnUrl); 88 } 89 // Android 90 return await openAuthSessionPolyfillAsync(url, returnUrl); 91} 92 93export async function getAuthSchemeAsync(): Promise<string> { 94 if (Platform.OS === 'android') { 95 return 'expo-dev-launcher'; 96 } 97 98 return await DevLauncherAuth.getAuthSchemeAsync(); 99} 100 101export async function setSessionAsync(session: string): Promise<void> { 102 return await DevLauncherAuth.setSessionAsync(session); 103} 104 105export async function restoreSessionAsync(): Promise<{ 106 [key: string]: any; 107 sessionSecret: string; 108}> { 109 return await DevLauncherAuth.restoreSessionAsync(); 110} 111