150 lines
4.8 KiB
TypeScript

import React, { useState } from 'react';
import { Image, 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 & open in browser
const authUrl = await request.makeAuthUrlAsync(discovery);
const result = await WebBrowser.openAuthSessionAsync(authUrl, redirectUri, {
showInRecents: true,
});
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');
}
// Exchange the code for tokens
const tokenResult = await AuthSession.exchangeCodeAsync(
{
clientId: clientId!,
code: params.code,
redirectUri,
extraParams: {
code_verifier: request.codeVerifier || '',
},
},
discovery,
);
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,
});
console.log(JSON.stringify({ data, error }, null, 2));
const { data: profile, error: profileError } = await supabase
.from('profiles')
.select('*')
.eq('id', data.user?.id)
.single();
if (profileError) {
console.error('Supabase profile error:', profileError);
throw profileError;
}
console.log(JSON.stringify({ profile, error: profileError }, null, 2));
if (profile?.provider !== 'azure') {
const { error: updateProfileError } = await supabase
.from('profiles')
.upsert({
id: data.session?.user.id ?? '',
provider: 'azure',
updated_at: new Date(),
});
if (updateProfileError) {
console.error('Supabase profile error:', 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 (
<ThemedButton disabled={loading} onPress={signInWithAzure}>
<Image source={require('@/assets/images/Microsoft_Logo.png')} style={styles.microsoftLogo} />
<ThemedText
type='custom'
fontWeight='semibold'
fontSize={26}
lightColor={Colors.dark.text}
darkColor={Colors.light.text}
>
{loading ? 'Signing in...' : 'Sign in with Microsoft'}
</ThemedText>
</ThemedButton>
);
};
export default AzureSignIn;
const styles = StyleSheet.create({
microsoftLogo: {
height: 30,
width: 30,
marginRight: 10,
},
});