UNPKG

recoder-shared

Version:

Shared types, utilities, and configurations for Recoder

319 lines 9.86 kB
"use strict"; /** * Mobile OAuth Implementation * Handles OAuth flows for mobile platforms (React Native) */ Object.defineProperty(exports, "__esModule", { value: true }); exports.MobileOAuthHandler = void 0; const oauth_handler_1 = require("./oauth-handler"); class MobileOAuthHandler { constructor(authClient, config = {}) { /** * Handle linking URL event */ this.handleLinkingUrl = (event) => { const url = typeof event === 'string' ? event : event.url; if (this.deepLinkHandler && url.startsWith(`${this.config.customScheme}://oauth/callback/`)) { this.deepLinkHandler(url); } }; this.authClient = authClient; this.oauthHandler = new oauth_handler_1.OAuthHandler(authClient); this.config = { customScheme: config.customScheme || 'recoder', useInAppBrowser: config.useInAppBrowser ?? true, browserOptions: { dismissButtonStyle: 'done', preferredBarTintColor: '#000000', preferredControlTintColor: '#ffffff', readerMode: false, animated: true, modalPresentationStyle: 'overCurrentContext', modalTransitionStyle: 'coverVertical', ...config.browserOptions } }; this.setupEventHandlers(); } setupEventHandlers() { this.oauthHandler.on('authSuccess', ({ provider, user, isNewUser }) => { console.log(`Successfully authenticated with ${provider}!`, { user, isNewUser }); }); this.oauthHandler.on('authError', ({ provider, error }) => { console.error(`Authentication failed with ${provider}:`, error); }); } /** * Start OAuth flow for mobile */ async login(provider) { const authUrl = this.oauthHandler.generateAuthUrl(provider, 'mobile', { redirectUri: `${this.config.customScheme}://oauth/callback/${provider}` }); return new Promise((resolve, reject) => { // Set up deep link handler const handleDeepLink = async (url) => { try { const callbackData = this.parseCallbackUrl(url); const result = await this.oauthHandler.handleCallback(provider, 'mobile', callbackData); // Clean up this.removeDeepLinkHandler(); resolve(result); } catch (error) { this.removeDeepLinkHandler(); reject(error); } }; this.setDeepLinkHandler(handleDeepLink); // Open OAuth URL this.openAuthUrl(authUrl); // Timeout after 5 minutes setTimeout(() => { this.removeDeepLinkHandler(); reject(new Error('Authentication timed out')); }, 5 * 60 * 1000); }); } /** * Login with Google */ async loginWithGoogle() { return this.login('google'); } /** * Login with GitHub */ async loginWithGitHub() { return this.login('github'); } /** * Login with Microsoft */ async loginWithMicrosoft() { return this.login('microsoft'); } /** * Handle deep link callback */ async handleDeepLink(url) { const callbackData = this.parseCallbackUrl(url); const provider = this.extractProviderFromUrl(url); if (!provider) { throw new Error('Unable to determine OAuth provider from callback URL'); } return await this.oauthHandler.handleCallback(provider, 'mobile', callbackData); } /** * Open OAuth URL using platform-specific method */ openAuthUrl(url) { try { // Try React Native Linking first const Linking = this.getLinking(); if (Linking) { Linking.openURL(url); return; } // Try Expo WebBrowser const WebBrowser = this.getWebBrowser(); if (WebBrowser) { WebBrowser.openBrowserAsync(url, { ...this.config.browserOptions, dismissButtonStyle: this.config.browserOptions.dismissButtonStyle }); return; } // Fallback console.log('Open this URL in your browser:', url); } catch (error) { console.error('Failed to open OAuth URL:', error); console.log('Please open this URL manually:', url); } } /** * Set up deep link handler */ setDeepLinkHandler(handler) { this.deepLinkHandler = handler; try { // React Native Linking const Linking = this.getLinking(); if (Linking) { Linking.addEventListener('url', this.handleLinkingUrl); return; } // Expo Linking const ExpoLinking = this.getExpoLinking(); if (ExpoLinking) { const subscription = ExpoLinking.addEventListener('url', this.handleLinkingUrl); // Store subscription for cleanup this._linkingSubscription = subscription; return; } } catch (error) { console.error('Failed to set up deep link handler:', error); } } /** * Remove deep link handler */ removeDeepLinkHandler() { try { // React Native Linking const Linking = this.getLinking(); if (Linking && Linking.removeEventListener) { Linking.removeEventListener('url', this.handleLinkingUrl); } // Expo Linking if (this._linkingSubscription) { this._linkingSubscription.remove(); delete this._linkingSubscription; } } catch (error) { console.error('Failed to remove deep link handler:', error); } this.deepLinkHandler = undefined; } /** * Parse callback URL parameters */ parseCallbackUrl(url) { try { const urlObj = new URL(url); return { code: urlObj.searchParams.get('code') || '', state: urlObj.searchParams.get('state') || '', error: urlObj.searchParams.get('error') || '', errorDescription: urlObj.searchParams.get('error_description') || '' }; } catch (error) { throw new Error('Invalid callback URL format'); } } /** * Extract provider from callback URL */ extractProviderFromUrl(url) { const match = url.match(/\/oauth\/callback\/([^?]+)/); return match ? match[1] : null; } /** * Get React Native Linking module */ getLinking() { try { return require('react-native').Linking; } catch { return null; } } /** * Get Expo WebBrowser module */ getWebBrowser() { try { return require('expo-web-browser'); } catch { return null; } } /** * Get Expo Linking module */ getExpoLinking() { try { return require('expo-linking'); } catch { return null; } } /** * React Native Hook for OAuth */ static createUseAuth() { return function useAuth() { // This would need React Native context implementation return { isAuthenticated: false, user: null, loading: true, login: async (provider) => { // Implementation would go here }, logout: async () => { // Implementation would go here } }; }; } /** * Setup deep linking for the app */ static setupDeepLinking(customScheme = 'recoder') { return { // For React Navigation linking: { prefixes: [customScheme + '://'], config: { screens: { OAuthCallback: 'oauth/callback/:provider', }, }, }, // For Expo app.json expoConfig: { scheme: customScheme, ios: { bundleIdentifier: 'com.recoder.app', }, android: { package: 'com.recoder.app', intentFilters: [ { action: 'VIEW', data: { scheme: customScheme, }, category: ['BROWSABLE', 'DEFAULT'], }, ], }, } }; } /** * Get available OAuth providers */ getAvailableProviders() { return this.oauthHandler.getSupportedProviders().map(p => p.name); } /** * Check if user is authenticated */ isAuthenticated() { return this.authClient.isAuthenticated(); } /** * Get current user */ getCurrentUser() { return this.authClient.getUser(); } /** * Logout */ async logout() { await this.authClient.logout(); } } exports.MobileOAuthHandler = MobileOAuthHandler; exports.default = MobileOAuthHandler; //# sourceMappingURL=oauth-mobile.js.map