163 lines
5.2 KiB
TypeScript
163 lines
5.2 KiB
TypeScript
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 (
|
|
<ThemedView style={styles.verticallySpaced}>
|
|
<ThemedButton disabled={loading} onPress={signInWithAzure}>
|
|
<ThemedText
|
|
lightColor={Colors.dark.text}
|
|
darkColor={Colors.light.text}
|
|
type='defaultSemiBold'
|
|
>
|
|
{loading ? 'Signing in...' : 'Sign in with Microsoft'}
|
|
</ThemedText>
|
|
</ThemedButton>
|
|
</ThemedView>
|
|
);
|
|
};
|
|
|
|
export default AzureSignIn;
|
|
|
|
const styles = StyleSheet.create({
|
|
verticallySpaced: {
|
|
paddingTop: 4,
|
|
paddingBottom: 4,
|
|
alignItems: 'center',
|
|
marginTop: 20,
|
|
},
|
|
});
|