import { authService } from './auth/auth.service'; import { NavigationService, navigationService, } from './navigation/navigation.interface'; // Mock OAuth configuration const GOOGLE_CLIENT_ID = 'mock_google_client_id'; const GITHUB_CLIENT_ID = 'mock_github_client_id'; // Mock OAuth endpoints const GOOGLE_AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth'; const GITHUB_AUTH_URL = 'https://github.com/login/oauth/authorize'; // Mock OAuth scopes const GOOGLE_SCOPES = 'openid email profile'; const GITHUB_SCOPES = 'user:email'; // Mock redirect URI const REDIRECT_URI = 'http://localhost:3000/auth/callback'; // Mock OAuth state generation const generateState = () => crypto.randomUUID(); /** * OAuth Service Class * * Handles OAuth authentication flows with dependency injection for navigation, * making it more testable and maintainable. */ export class OAuthService { constructor(private navigation: NavigationService = navigationService) {} /** * Initiates Google OAuth authentication flow */ googleAuth(): void { const state = generateState(); const url = new URL(GOOGLE_AUTH_URL); url.searchParams.append('client_id', GOOGLE_CLIENT_ID); url.searchParams.append('response_type', 'code'); url.searchParams.append('scope', GOOGLE_SCOPES); url.searchParams.append('redirect_uri', REDIRECT_URI); url.searchParams.append('state', state); // Store state for CSRF protection localStorage.setItem('oauth_state', state); // Redirect to Google's auth endpoint this.navigation.redirectTo(url.toString()); } /** * Initiates GitHub OAuth authentication flow */ githubAuth(): void { const state = generateState(); const url = new URL(GITHUB_AUTH_URL); url.searchParams.append('client_id', GITHUB_CLIENT_ID); url.searchParams.append('response_type', 'code'); url.searchParams.append('scope', GITHUB_SCOPES); url.searchParams.append('redirect_uri', REDIRECT_URI); url.searchParams.append('state', state); // Store state for CSRF protection localStorage.setItem('oauth_state', state); // Redirect to GitHub's auth endpoint this.navigation.redirectTo(url.toString()); } /** * Handles Google OAuth callback */ async handleGoogleCallback() { const params = this.navigation.getSearchParams(); const code = params.get('code'); const state = params.get('state'); const storedState = localStorage.getItem('oauth_state'); // Verify state to prevent CSRF attacks if (state !== storedState) { throw new Error('Invalid OAuth state'); } // Clear stored state localStorage.removeItem('oauth_state'); // Exchange code for token const accessToken = await this.mockExchangeCodeForToken('google', code); // Get user info const userInfo = await this.mockGetUserInfo('google', accessToken); // Register or login the user return authService.loginWithOAuth('google', { email: userInfo.email, username: userInfo.name, }); } /** * Handles GitHub OAuth callback */ async handleGithubCallback() { const params = this.navigation.getSearchParams(); const code = params.get('code'); const state = params.get('state'); const storedState = localStorage.getItem('oauth_state'); // Verify state to prevent CSRF attacks if (state !== storedState) { throw new Error('Invalid OAuth state'); } // Clear stored state localStorage.removeItem('oauth_state'); // Exchange code for token const accessToken = await this.mockExchangeCodeForToken('github', code); // Get user info const userInfo = await this.mockGetUserInfo('github', accessToken); // Register or login the user return authService.loginWithOAuth('github', { email: userInfo.email, username: userInfo.name, }); } /** * Mock token exchange * In a real implementation, this would make a POST request to the token endpoint */ private async mockExchangeCodeForToken( provider: 'google' | 'github', _code: string | null ): Promise { // For this mock, we'll just return a mock access token return `mock_${provider}_access_token_${crypto.randomUUID()}`; } /** * Mock user info retrieval * In a real implementation, this would make a GET request to the user info endpoint */ private async mockGetUserInfo( provider: 'google' | 'github', _accessToken: string ): Promise<{ email: string; name: string }> { // For this mock, we'll return mock user info return { email: `mock_${provider}_user_${crypto.randomUUID()}@example.com`, name: `Mock ${provider.charAt(0).toUpperCase() + provider.slice(1)} User`, }; } } // Default instance for production use const oauthService = new OAuthService(); // Export both the class and convenience functions for backward compatibility export const googleAuth = () => oauthService.googleAuth(); export const githubAuth = () => oauthService.githubAuth(); export const handleGoogleCallback = () => oauthService.handleGoogleCallback(); export const handleGithubCallback = () => oauthService.handleGithubCallback(); // Export the service instance for direct use export { oauthService }; export default oauthService;