import React, { useState } from 'react'; import { StyleSheet, Alert } from 'react-native'; import * as WebBrowser from 'expo-web-browser'; import * as Linking from 'expo-linking'; import * as AuthSession from 'expo-auth-session'; import * as QueryParams from 'expo-auth-session/build/QueryParams'; import { supabase } from '@/lib/supabase'; import { ThemedView, ThemedButton, ThemedText } from '@/components/theme'; import { Colors } from '@/constants/Colors'; import type { updateUser } from '@/constants/Types'; WebBrowser.maybeCompleteAuthSession(); // Configuration for Azure AD const tenantId = process.env.EXPO_PUBLIC_AZURE_TENANT_ID; const clientId = process.env.EXPO_PUBLIC_AZURE_CLIENT_ID; // Create MSAL auth request const redirectUri = Linking.createURL('auth/callback'); const discovery = { authorizationEndpoint: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`, tokenEndpoint: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, }; const AzureSignIn = () => { const [loading, setLoading] = useState(false); const signInWithAzure = async () => { try { setLoading(true); // Create the MSAL auth request const request = new AuthSession.AuthRequest({ clientId: clientId!, scopes: ['openid', 'profile', 'email', 'offline_access', 'User.Read'], redirectUri, usePKCE: true, responseType: AuthSession.ResponseType.Code, }); // Generate the auth URL with PKCE const authUrl = await request.makeAuthUrlAsync(discovery); console.log('Generated auth URL:', authUrl); // Open the auth URL in a browser const result = await WebBrowser.openAuthSessionAsync(authUrl, redirectUri, { showInRecents: true, }); console.log('Auth session result type:', result.type); if (result.type === 'success' && result.url) { // Parse the URL to get the authorization code const { params, errorCode } = QueryParams.getQueryParams(result.url); if (errorCode || params.error) { const errorMessage = params.error_description || params.error || errorCode; throw new Error(`Error during authentication: ${errorMessage}`); } if (!params.code) { throw new Error('No authorization code received'); } console.log('Authorization code received'); // Exchange the code for tokens const tokenResult = await AuthSession.exchangeCodeAsync( { clientId: clientId!, code: params.code, redirectUri, extraParams: { code_verifier: request.codeVerifier || '', }, }, discovery, ); console.log('Token exchange successful'); if (!tokenResult.idToken) { throw new Error('No ID token received'); } // Now use the ID token to sign in with Supabase const { data, error } = await supabase.auth.signInWithIdToken({ provider: 'azure', token: tokenResult.idToken, }); // Check if profies table already has info (User is signing in, not signing up) const { data: profileData, error: profileError } = await supabase .from('profiles') .select('*') .eq('id', data.user?.id) .single(); if (profileData.email === '' || !profileData.email && data.session?.user.email) { const updateData: updateUser = { email: data.session?.user.email ?? '', }; const { error: updateAuthError } = await supabase.auth.updateUser({ data: updateData, }); if (updateAuthError) Alert.alert('Error updating auth info:', updateAuthError.message); const { error: updateProfileError } = await supabase .from('profiles') .upsert({ id: data.session?.user.id ?? '', email: data.session?.user.email ?? '', provider: 'azure', updated_at: new Date(), }); if (updateProfileError) Alert.alert('Error updating profile:', updateProfileError.message); } if (error) { console.error('Supabase sign-in error:', error); throw error; } console.log('Successfully signed in with Azure via Supabase'); return data; } else { console.log('Authentication was canceled or failed'); } } catch (error: any) { console.error('Error signing in with Azure:', error); Alert.alert('Sign In Error', error.message || 'An error occurred during sign in'); } finally { setLoading(false); } }; return ( {loading ? 'Signing in...' : 'Sign in with Microsoft'} ); }; export default AzureSignIn; const styles = StyleSheet.create({ verticallySpaced: { paddingTop: 4, paddingBottom: 4, alignItems: 'center', marginTop: 20, }, });