Start trying to support Microsoft Azure Sign in
This commit is contained in:
		
							
								
								
									
										5
									
								
								app.json
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								app.json
									
									
									
									
									
								
							@@ -33,7 +33,10 @@
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
      "permissions": [
 | 
					      "permissions": [
 | 
				
			||||||
        "android.permission.ACCESS_COARSE_LOCATION",
 | 
					        "android.permission.ACCESS_COARSE_LOCATION",
 | 
				
			||||||
        "android.permission.ACCESS_FINE_LOCATION"
 | 
					        "android.permission.ACCESS_FINE_LOCATION",
 | 
				
			||||||
 | 
					        "android.permission.RECEIVE_BOOT_COMPLETED",
 | 
				
			||||||
 | 
					        "android.permission.VIBRATE",
 | 
				
			||||||
 | 
					        "android.permission.INTERNET"
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
      "package": "com.gbrown.techtracker"
 | 
					      "package": "com.gbrown.techtracker"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,7 @@ const AppleSignInButton = () => {
 | 
				
			|||||||
        //projectId,
 | 
					        //projectId,
 | 
				
			||||||
      //});
 | 
					      //});
 | 
				
			||||||
      if (credential.identityToken) {
 | 
					      if (credential.identityToken) {
 | 
				
			||||||
 | 
					        const email = credential.email;
 | 
				
			||||||
        const full_name = (
 | 
					        const full_name = (
 | 
				
			||||||
          credential.fullName &&
 | 
					          credential.fullName &&
 | 
				
			||||||
          credential.fullName.givenName &&
 | 
					          credential.fullName.givenName &&
 | 
				
			||||||
@@ -41,16 +42,26 @@ const AppleSignInButton = () => {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
        console.log(JSON.stringify({ error, user }, null, 2))
 | 
					        console.log(JSON.stringify({ error, user }, null, 2))
 | 
				
			||||||
        if (!error) {
 | 
					        if (!error) {
 | 
				
			||||||
          if (user) {
 | 
					          if (user && session) {
 | 
				
			||||||
            const data: any = {
 | 
					            const data: any = {};
 | 
				
			||||||
              full_name,
 | 
					            if (email) data.email = email;
 | 
				
			||||||
            };
 | 
					            if (full_name) data.full_name = full_name;
 | 
				
			||||||
            const { error: updateError } = await supabase.auth.updateUser({
 | 
					            const { error: updateError } = await supabase.auth.updateUser({
 | 
				
			||||||
              data,
 | 
					              data,
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            if (updateError) {
 | 
					            if (updateError) {
 | 
				
			||||||
              console.error('Error updating user metadata:', updateError);
 | 
					              console.error('Error updating user metadata:', updateError);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            const { error: updateProfileError } =
 | 
				
			||||||
 | 
					              await supabase.from('profiles').upsert({
 | 
				
			||||||
 | 
					              id: session.user.id,
 | 
				
			||||||
 | 
					              username: data?.email.split('@')[0],
 | 
				
			||||||
 | 
					              full_name: data?.full_name,
 | 
				
			||||||
 | 
					              updated_at: new Date(),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            if (updateProfileError) {
 | 
				
			||||||
 | 
					              console.error('Error updating profile:', updateProfileError);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
@@ -67,10 +78,10 @@ const AppleSignInButton = () => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (Platform.OS !== 'ios') return <div/>;
 | 
					  if (Platform.OS !== 'ios') return <ThemedView />;
 | 
				
			||||||
  else return (
 | 
					  else return (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <ThemedView style={[styles.verticallySpaced, styles.mt20]}>
 | 
					    <ThemedView style={styles.verticallySpaced}>
 | 
				
			||||||
      <AppleAuthentication.AppleAuthenticationButton
 | 
					      <AppleAuthentication.AppleAuthenticationButton
 | 
				
			||||||
        buttonType={AppleAuthentication.AppleAuthenticationButtonType.SIGN_IN}
 | 
					        buttonType={AppleAuthentication.AppleAuthenticationButtonType.SIGN_IN}
 | 
				
			||||||
        buttonStyle={
 | 
					        buttonStyle={
 | 
				
			||||||
@@ -92,8 +103,6 @@ const styles = StyleSheet.create({
 | 
				
			|||||||
    paddingTop: 4,
 | 
					    paddingTop: 4,
 | 
				
			||||||
    paddingBottom: 4,
 | 
					    paddingBottom: 4,
 | 
				
			||||||
    alignItems: 'center',
 | 
					    alignItems: 'center',
 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  mt20: {
 | 
					 | 
				
			||||||
    marginTop: 20,
 | 
					    marginTop: 20,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
}); 
 | 
					}); 
 | 
				
			||||||
@@ -2,7 +2,8 @@ import React, { useState, useEffect } from 'react';
 | 
				
			|||||||
import { Alert, StyleSheet, AppState, Image, Platform } from 'react-native';
 | 
					import { Alert, StyleSheet, AppState, Image, Platform } from 'react-native';
 | 
				
			||||||
import { supabase } from '@/lib/supabase';
 | 
					import { supabase } from '@/lib/supabase';
 | 
				
			||||||
import { ThemedView, ThemedText, ThemedTextButton, ThemedTextInput } from '@/components/theme';
 | 
					import { ThemedView, ThemedText, ThemedTextButton, ThemedTextInput } from '@/components/theme';
 | 
				
			||||||
import AppleSignInButton from '@/components/auth/AppleSignIn.native';
 | 
					import AppleSignInButton from '@/components/auth/AppleSignIniOS';
 | 
				
			||||||
 | 
					import AzureSignIn from './AzureSignIn';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Tells Supabase Auth to continuously refresh the session automatically if
 | 
					// Tells Supabase Auth to continuously refresh the session automatically if
 | 
				
			||||||
// the app is in the foreground. When this is added, you will continue to receive
 | 
					// the app is in the foreground. When this is added, you will continue to receive
 | 
				
			||||||
@@ -103,6 +104,7 @@ const Auth = () => {
 | 
				
			|||||||
        />
 | 
					        />
 | 
				
			||||||
      </ThemedView>
 | 
					      </ThemedView>
 | 
				
			||||||
      <AppleSignInButton />
 | 
					      <AppleSignInButton />
 | 
				
			||||||
 | 
					      <AzureSignIn />
 | 
				
			||||||
    </ThemedView>
 | 
					    </ThemedView>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										109
									
								
								components/auth/AzureSignIn.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								components/auth/AzureSignIn.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
				
			|||||||
 | 
					import React, { useState } from 'react';
 | 
				
			||||||
 | 
					import * as WebBrowser from 'expo-web-browser';
 | 
				
			||||||
 | 
					import { makeRedirectUri, useAuthRequest, useAutoDiscovery } from 'expo-auth-session';
 | 
				
			||||||
 | 
					import { Platform, StyleSheet } from 'react-native';
 | 
				
			||||||
 | 
					import { supabase } from '@/lib/supabase';
 | 
				
			||||||
 | 
					import { ThemedView, ThemedTextButton } from '@/components/theme';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is important - it completes the auth session when the browser redirects back to your app
 | 
				
			||||||
 | 
					WebBrowser.maybeCompleteAuthSession();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const AzureSignIn = () => {
 | 
				
			||||||
 | 
					  const [loading, setLoading] = useState(false);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Get environment variables
 | 
				
			||||||
 | 
					  const tenantId = process.env.EXPO_PUBLIC_AZURE_TENANT_ID as string;
 | 
				
			||||||
 | 
					  const clientId = process.env.EXPO_PUBLIC_AZURE_CLIENT_ID as string;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Set up the discovery endpoint for Azure AD
 | 
				
			||||||
 | 
					  const discovery = useAutoDiscovery(
 | 
				
			||||||
 | 
					    `https://login.microsoftonline.com/${tenantId}/v2.0`
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Create a redirect URI that matches what you configured in Azure
 | 
				
			||||||
 | 
					  // This should match the redirect URI you set in your Supabase dashboard
 | 
				
			||||||
 | 
					  const redirectUri = makeRedirectUri({
 | 
				
			||||||
 | 
					    scheme: Platform.OS === 'web' ? undefined : 'com.gbrown.techtracker',
 | 
				
			||||||
 | 
					    path: Platform.OS === 'web' ? 'auth/callback' : undefined,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Set up the auth request with the needed scopes
 | 
				
			||||||
 | 
					  const [request, response, promptAsync] = useAuthRequest(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      clientId,
 | 
				
			||||||
 | 
					      scopes: ['openid', 'profile', 'email', 'offline_access'],
 | 
				
			||||||
 | 
					      redirectUri,
 | 
				
			||||||
 | 
					      responseType: 'code', // Important for Supabase
 | 
				
			||||||
 | 
					      usePKCE: true, // Use PKCE for added security
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    discovery
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  const signInWithAzure = async () => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      setLoading(true);
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // For Expo Go and mobile apps, we need to use the Expo Auth Session flow
 | 
				
			||||||
 | 
					      if (Platform.OS !== 'web') {
 | 
				
			||||||
 | 
					        // Launch the browser for authentication
 | 
				
			||||||
 | 
					        const result = await promptAsync();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (result.type === 'success') {
 | 
				
			||||||
 | 
					          // If we got an authorization code, use Supabase to exchange it for a session
 | 
				
			||||||
 | 
					          const { code } = result.params;
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          const { data, error } = await supabase.auth.exchangeCodeForSession(code);
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          if (error) {
 | 
				
			||||||
 | 
					            throw error;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          console.log('Successfully signed in with Azure!', data.user);
 | 
				
			||||||
 | 
					        } else if (result.type === 'error') {
 | 
				
			||||||
 | 
					          throw new Error(result.error?.message || 'Authentication failed');
 | 
				
			||||||
 | 
					        } else if (result.type === 'cancel') {
 | 
				
			||||||
 | 
					          console.log('User cancelled the login flow');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        // For web, we can use Supabase's built-in OAuth flow
 | 
				
			||||||
 | 
					        const { data, error } = await supabase.auth.signInWithOAuth({
 | 
				
			||||||
 | 
					          provider: 'azure',
 | 
				
			||||||
 | 
					          options: {
 | 
				
			||||||
 | 
					            scopes: 'email profile openid offline_access',
 | 
				
			||||||
 | 
					            redirectTo: window.location.origin + '/auth/callback',
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (error) {
 | 
				
			||||||
 | 
					          throw error;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.error('Error signing in with Azure:', error);
 | 
				
			||||||
 | 
					      alert(`Error signing in: ${error.message}`);
 | 
				
			||||||
 | 
					    } finally {
 | 
				
			||||||
 | 
					      setLoading(false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <ThemedView style={styles.verticallySpaced}>
 | 
				
			||||||
 | 
					      <ThemedTextButton
 | 
				
			||||||
 | 
					        text="Sign in with Microsoft"
 | 
				
			||||||
 | 
					        disabled={loading || !request}
 | 
				
			||||||
 | 
					        onPress={signInWithAzure}
 | 
				
			||||||
 | 
					        fontSize={18}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </ThemedView>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export default AzureSignIn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const styles = StyleSheet.create({
 | 
				
			||||||
 | 
					  verticallySpaced: {
 | 
				
			||||||
 | 
					    paddingTop: 4,
 | 
				
			||||||
 | 
					    paddingBottom: 4,
 | 
				
			||||||
 | 
					    alignItems: 'center',
 | 
				
			||||||
 | 
					    marginTop: 20,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					}); 
 | 
				
			||||||
							
								
								
									
										31
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										31
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -19,6 +19,7 @@
 | 
				
			|||||||
        "aes-js": "^3.1.2",
 | 
					        "aes-js": "^3.1.2",
 | 
				
			||||||
        "expo": "~52.0.28",
 | 
					        "expo": "~52.0.28",
 | 
				
			||||||
        "expo-apple-authentication": "~7.1.3",
 | 
					        "expo-apple-authentication": "~7.1.3",
 | 
				
			||||||
 | 
					        "expo-auth-session": "~6.0.3",
 | 
				
			||||||
        "expo-blur": "~14.0.3",
 | 
					        "expo-blur": "~14.0.3",
 | 
				
			||||||
        "expo-constants": "~17.0.7",
 | 
					        "expo-constants": "~17.0.7",
 | 
				
			||||||
        "expo-dev-client": "~5.0.12",
 | 
					        "expo-dev-client": "~5.0.12",
 | 
				
			||||||
@@ -7668,6 +7669,24 @@
 | 
				
			|||||||
        "react-native": "*"
 | 
					        "react-native": "*"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/expo-auth-session": {
 | 
				
			||||||
 | 
					      "version": "6.0.3",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/expo-auth-session/-/expo-auth-session-6.0.3.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-s7LmmMPiiY1NXrlcXkc4+09Hlfw9X1CpaQOCDkwfQEodG1uCYGQi/WImTnDzw5YDkWI79uC8F1mB8EIerilkDA==",
 | 
				
			||||||
 | 
					      "license": "MIT",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "expo-application": "~6.0.2",
 | 
				
			||||||
 | 
					        "expo-constants": "~17.0.5",
 | 
				
			||||||
 | 
					        "expo-crypto": "~14.0.2",
 | 
				
			||||||
 | 
					        "expo-linking": "~7.0.5",
 | 
				
			||||||
 | 
					        "expo-web-browser": "~14.0.2",
 | 
				
			||||||
 | 
					        "invariant": "^2.2.4"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependencies": {
 | 
				
			||||||
 | 
					        "react": "*",
 | 
				
			||||||
 | 
					        "react-native": "*"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/expo-blur": {
 | 
					    "node_modules/expo-blur": {
 | 
				
			||||||
      "version": "14.0.3",
 | 
					      "version": "14.0.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-14.0.3.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-14.0.3.tgz",
 | 
				
			||||||
@@ -7693,6 +7712,18 @@
 | 
				
			|||||||
        "react-native": "*"
 | 
					        "react-native": "*"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/expo-crypto": {
 | 
				
			||||||
 | 
					      "version": "14.0.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/expo-crypto/-/expo-crypto-14.0.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-WRc9PBpJraJN29VD5Ef7nCecxJmZNyRKcGkNiDQC1nhY5agppzwhqh7zEzNFarE/GqDgSiaDHS8yd5EgFhP9AQ==",
 | 
				
			||||||
 | 
					      "license": "MIT",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "base64-js": "^1.3.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependencies": {
 | 
				
			||||||
 | 
					        "expo": "*"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/expo-dev-client": {
 | 
					    "node_modules/expo-dev-client": {
 | 
				
			||||||
      "version": "5.0.12",
 | 
					      "version": "5.0.12",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-5.0.12.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-5.0.12.tgz",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,7 +56,8 @@
 | 
				
			|||||||
    "@expo/ngrok": "4.1.0",
 | 
					    "@expo/ngrok": "4.1.0",
 | 
				
			||||||
    "expo-notifications": "~0.29.13",
 | 
					    "expo-notifications": "~0.29.13",
 | 
				
			||||||
    "expo-device": "~7.0.2",
 | 
					    "expo-device": "~7.0.2",
 | 
				
			||||||
    "expo-location": "~18.0.7"
 | 
					    "expo-location": "~18.0.7",
 | 
				
			||||||
 | 
					    "expo-auth-session": "~6.0.3"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@babel/core": "^7.25.2",
 | 
					    "@babel/core": "^7.25.2",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -72,6 +72,7 @@ async function registerForPushNotificationsAsync() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    const projectId = Constants.expoConfig?.extra?.eas?.projectId;
 | 
					    const projectId = Constants.expoConfig?.extra?.eas?.projectId;
 | 
				
			||||||
    if (!projectId) {
 | 
					    if (!projectId) {
 | 
				
			||||||
 | 
					      console.warn('No project id found');
 | 
				
			||||||
      alert('Project ID not found');
 | 
					      alert('Project ID not found');
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user