diff --git a/apps/docs/content/guides/getting-started/tutorials/with-expo-react-native-social-auth.mdx b/apps/docs/content/guides/getting-started/tutorials/with-expo-react-native-social-auth.mdx index 2d0d1f7841c..2a2ff4d4683 100644 --- a/apps/docs/content/guides/getting-started/tutorials/with-expo-react-native-social-auth.mdx +++ b/apps/docs/content/guides/getting-started/tutorials/with-expo-react-native-social-auth.mdx @@ -3,12 +3,12 @@ title: 'Build a Social Auth App with Expo React Native' description: 'Learn how to implement social authentication in an app with Expo React Native and Supabase Database and Auth functionality.' --- -This tutorial demonstrates how to build a product management app with social authentication. The app showcases user authentication, data management, and photo uploads using: +This tutorial demonstrates how to build a React Native app with Expo that implements social authentication. You'll learn to create a complete authentication flow with protected navigation using: - [Supabase Database](/docs/guides/database) - a Postgres database for storing your user data with [Row Level Security](/docs/guides/auth#row-level-security) to ensure data is protected and users can only access their own information. - [Supabase Auth](/docs/guides/auth) - enables users to log in through social authentication providers (Apple and Google). -![Supabase Social Auth example](/docs/img/supabase-expo-social-auth.png) +![Supabase Social Auth example](/docs/img/supabase-expo-social-auth-login.png) @@ -522,11 +522,12 @@ While testing, you can disable email confirmation in your [project's email auth ## Integrate social authentication -Now integrate social authentication with Supabase Auth. +Now let's integrate social authentication with Supabase Auth, starting from the Apple authentication. +If you only need to implement Google authentication, you can skip to the [Google authentication](#google-authentication) section. ### Apple authentication -First, add the button that you'll create inside the login screen you created previously: +Let's start by adding the button that we are going to create inside the login screen previously made: <$CodeTabs> @@ -551,10 +552,11 @@ export default function LoginScreen() { -For Apple authentication, you can choose between two options: +For Apple authentication, you can choose between: -- [Expo's AppleAuthentication library](https://docs.expo.dev/versions/latest/sdk/apple-authentication/) - iOS only -- [Invertase's React Native Apple Authentication library](https://github.com/invertase/react-native-apple-authentication) - supports iOS, Android, and Web (using the [react-apple-signin-auth](https://react-apple-signin-auth.ahmedtokyo.com/) web library) +- [Invertase's React Native Apple Authentication library](https://github.com/invertase/react-native-apple-authentication) - that supports iOS, Android +- [react-apple-signin-auth](https://react-apple-signin-auth.ahmedtokyo.com/) - that supports Web, also suggested by Invertase +- [Expo's AppleAuthentication library](https://docs.expo.dev/versions/latest/sdk/apple-authentication/) - that supports iOS only For either option, you'll need to obtain a Service ID from the [Apple Developer Console](https://supabase.com/docs/guides/auth/social-login/auth-apple?queryGroups=framework&framework=nextjs&queryGroups=platform&platform=web#configuration-web). @@ -563,7 +565,7 @@ For either option, you'll need to obtain a Service ID from the [Apple Developer To enable Apple sign-up on Android and Web, you'll also need to register the tunneled URL (e.g., `https://arnrer1-anonymous-8081.exp.direct`) obtained by running: ```bash -npx expo run:web --tunnel +npx expo start --tunnel ``` and add it to the "Allowed Callback URLs" list in your Supabase dashboard Apple provider configuration. @@ -604,7 +606,7 @@ For more information, follow the [Supabase Login with Apple](https://supabase.co <$CodeTabs> - ```tsx name=components/social-auth-buttons/AppleSignInButton.ios.tsx + ```tsx name=components/social-auth-buttons/apple/AppleSignInButton.ios.tsx import { supabase } from '@/lib/supabase'; import { AppleButton, appleAuth } from '@invertase/react-native-apple-authentication'; import type { SignInWithIdTokenCredentials } from '@supabase/supabase-js'; @@ -667,7 +669,7 @@ For more information, follow the [Supabase Login with Apple](https://supabase.co <$CodeTabs> - ```tsx name=components/social-auth-buttons/AppleSignInButton.ios.tsx + ```tsx name=components/social-auth-buttons/apple/AppleSignInButton.ios.tsx ... const credentialState = await appleAuth.getCredentialStateForUser(appleAuthRequestResponse.user); ... @@ -746,7 +748,7 @@ For more information, follow the [Supabase Login with Apple](https://supabase.co <$CodeTabs> - ```tsx name=components/social-auth-buttons/AppleSignInButton.android.tsx + ```tsx name=components/social-auth-buttons/apple/AppleSignInButton.android.tsx import { supabase } from '@/lib/supabase'; import { appleAuthAndroid, AppleButton } from '@invertase/react-native-apple-authentication'; import { SignInWithIdTokenCredentials } from '@supabase/supabase-js'; @@ -824,6 +826,21 @@ For more information, follow the [Supabase Login with Apple](https://supabase.co npx expo run:android --no-build-cache ``` + + + + + #### Prerequisites + + Before proceeding, as per the mobile options you'll need an Apple Service ID. To obtain it you can follow the [Invertase Initial Setup Guide](https://github.com/invertase/react-native-apple-authentication/blob/main/docs/INITIAL_SETUP.md) and the [Invertase Android Setup Guide](https://github.com/invertase/react-native-apple-authentication/blob/main/docs/ANDROID_EXTRA.md) mentioned in the Invertase tab. + + You'll also need to add two new environment variables to the `.env` file: + + ```bash + EXPO_PUBLIC_APPLE_AUTH_SERVICE_ID="YOUR_APPLE_AUTH_SERVICE_ID" + EXPO_PUBLIC_APPLE_AUTH_REDIRECT_URI="YOUR_APPLE_AUTH_REDIRECT_URI" + ``` + #### Web Install the required libraries: @@ -836,7 +853,7 @@ For more information, follow the [Supabase Login with Apple](https://supabase.co <$CodeTabs> - ```tsx name=components/social-auth-buttons/AppleSignInButton.web.tsx + ```tsx name=components/social-auth-buttons/apple/AppleSignInButton.web.tsx import { supabase } from '@/lib/supabase'; import type { SignInWithIdTokenCredentials } from '@supabase/supabase-js'; import { useEffect, useState } from 'react'; @@ -954,7 +971,7 @@ For more information, follow the [Supabase Login with Apple](https://supabase.co <$CodeTabs> - ```tsx name=components/social-auth-buttons/AppleSignInButton.tsx + ```tsx name=components/social-auth-buttons/apple/AppleSignInButton.tsx import { supabase } from '@/lib/supabase'; import { AppleButton, appleAuth } from '@invertase/react-native-apple-authentication'; import type { SignInWithIdTokenCredentials } from '@supabase/supabase-js'; @@ -1019,3 +1036,349 @@ For more information, follow the [Supabase Login with Apple](https://supabase.co + +### Google authentication + +Let's start by adding the button that we are going to create inside the login screen previously made: + +<$CodeTabs> + +```tsx name=app/login.tsx +... +import { GoogleSignInButton } from '@/components/social-auth-buttons/google/GoogleSignInButton'; +... +export default function LoginScreen() { + return ( + <> + + + ... + + ... + + + ); +} +... +``` + + + +For Google authentication, you can choose between the following options: + +- [GN Google Sign In Premium](https://react-native-google-signin.github.io/docs/install#sponsor-only-version) - that supports iOS, Android, and Web by using the latest Google's OneTap sign-in (but [it requires a subscription](https://universal-sign-in.com/)) +- [@react-oauth/google](https://github.com/MomenSherif/react-oauth#googlelogin) - that supports Web (so it's not a good option for mobile, but it works) +- Relying on the [signInWithOAuth](https://supabase.com/docs/reference/javascript/auth-signinwithoauth) function of the Supabase Auth - that also supports iOS, Android and Web (__useful also to manage any other OAuth provider__) + + + +The [GN Google Sign In Free](https://react-native-google-signin.github.io/docs/install#public-version-free) doesn't support neither iOS nor Android, as [it doesn't allow to pass a custom nonce](https://github.com/react-native-google-signin/google-signin/issues/1176) to the sign-in request. + + + + +For either option, you'll need to obtain a Web Client ID from the Google Cloud Engine, as explained in the [Google Sign In](https://supabase.com/docs/guides/auth/social-login/auth-google?queryGroups=platform&platform=react-native#react-native) guide. + +In this guide we will only use the [@react-oauth/google@latest](https://github.com/MomenSherif/react-oauth#googlelogin) option for the Web, and the [signInWithOAuth](https://supabase.com/docs/reference/javascript/auth-signinwithoauth) for the mobile platforms. + +Before proceeding, you'll need to add a new environment variable to the `.env` file: + +```bash +EXPO_PUBLIC_GOOGLE_AUTH_WEB_CLIENT_ID="YOUR_GOOGLE_AUTH_WEB_CLIENT_ID" +``` + + + + + Install the `@react-oauth/google` library: + + ```bash + npx expo install @react-oauth/google + ``` + + Enable the `expo-web-browser` plugin in `app.json`: + + <$CodeTabs> + + ```json name=app.json + { + "expo": { + ... + "plugins": { + ... + [ + "expo-web-browser", + { + "experimentalLauncherActivity": false + } + ] + ... + }, + ... + } + } + ``` + + + + Then create the iOS specific button component `GoogleSignInButton.web`: + + <$CodeTabs> + ```tsx name=components/social-auth-buttons/google/GoogleSignInButton.web.tsx + + import { supabase } from '@/lib/supabase'; + import { CredentialResponse, GoogleLogin, GoogleOAuthProvider } from '@react-oauth/google'; + import { SignInWithIdTokenCredentials } from '@supabase/supabase-js'; + import { useEffect, useState } from 'react'; + + import 'react-native-get-random-values'; + + export default function GoogleSignInButton() { + + // Generate secure, random values for state and nonce + const [nonce, setNonce] = useState(''); + const [sha256Nonce, setSha256Nonce] = useState(''); + + async function onGoogleButtonSuccess(authRequestResponse: CredentialResponse) { + console.debug('Google sign in successful:', { authRequestResponse }) + if (authRequestResponse.clientId && authRequestResponse.credential) { + const signInWithIdTokenCredentials: SignInWithIdTokenCredentials = { + provider: 'google', + token: authRequestResponse.credential, + nonce: nonce, + } + + const { data, error } = await supabase.auth.signInWithIdToken(signInWithIdTokenCredentials) + + if (error) { + console.error('Error signing in with Apple:', error) + } + + if (data) { + console.log('Apple sign in successful:', data) + } + } + } + + function onGoogleButtonFailure() { + console.error('Error signing in with Google') + } + + useEffect(() => { + function generateNonce(): string { + const array = new Uint32Array(1); + window.crypto.getRandomValues(array); + return array[0].toString(); + }; + + async function generateSha256Nonce(nonce: string): Promise { + const buffer = await window.crypto.subtle.digest('sha-256', new TextEncoder().encode(nonce)); + const array = Array.from(new Uint8Array(buffer)); + return array.map(b => b.toString(16).padStart(2, '0')).join(''); + } + + let nonce = generateNonce(); + setNonce(nonce); + + generateSha256Nonce(nonce) + .then((sha256Nonce) => { setSha256Nonce(sha256Nonce) }); + }, []); + + return ( + + + + ); + } + + ``` + + + + Test the authentication in your browser using the tunneled HTTPS URL: + + ```bash + npx expo start --tunnel + ``` + + + + To allow the Google Sign In to work, as we did before for Apple, you'll need to register the tunneled URL (e.g., `https://arnrer1-anonymous-8081.exp.direct`) obtained to the Authorized JavaScript origins list of your [Google Cloud Console's OAuth 2.0 Client IDs](https://console.cloud.google.com/auth/clients/) configuration. + + + + + + + + + Then create the mobile generic button component `GoogleSignInButton.tsx`: + + <$CodeTabs> + + ```tsx name=components/social-auth-buttons/google/GoogleSignInButton.tsx + import { supabase } from '@/lib/supabase'; + import { useEffect } from 'react'; + import { TouchableOpacity } from 'react-native'; + + import { expo } from '@/app.json'; + import { Text } from '@react-navigation/elements'; + import { Image } from 'expo-image'; + import * as WebBrowser from "expo-web-browser"; + + WebBrowser.maybeCompleteAuthSession(); + + export default function GoogleSignInButton() { + + function extractParamsFromUrl(url: string) { + const parsedUrl = new URL(url); + const hash = parsedUrl.hash.substring(1); // Remove the leading '#' + const params = new URLSearchParams(hash); + + return { + access_token: params.get("access_token"), + expires_in: parseInt(params.get("expires_in") || "0"), + refresh_token: params.get("refresh_token"), + token_type: params.get("token_type"), + provider_token: params.get("provider_token"), + code: params.get("code"), + }; + }; + + async function onSignInButtonPress() { + console.debug('onSignInButtonPress - start'); + const res = await supabase.auth.signInWithOAuth({ + provider: "google", + options: { + redirectTo: `${expo.scheme}://google-auth`, + queryParams: { prompt: "consent" }, + skipBrowserRedirect: true, + }, + }); + + const googleOAuthUrl = res.data.url; + + if (!googleOAuthUrl) { + console.error("no oauth url found!"); + return; + } + + const result = await WebBrowser.openAuthSessionAsync( + googleOAuthUrl, + `${expo.scheme}://google-auth`, + { showInRecents: true }, + ).catch((err) => { + console.error('onSignInButtonPress - openAuthSessionAsync - error', { err }) + console.log(err); + }); + + console.debug('onSignInButtonPress - openAuthSessionAsync - result', { result }); + + if (result && result.type === "success") { + console.debug('onSignInButtonPress - openAuthSessionAsync - success'); + const params = extractParamsFromUrl(result.url); + console.debug('onSignInButtonPress - openAuthSessionAsync - success', { params }); + + if (params.access_token && params.refresh_token) { + console.debug('onSignInButtonPress - setSession'); + const { data, error } = await supabase.auth.setSession({ + access_token: params.access_token, + refresh_token: params.refresh_token, + }); + console.debug('onSignInButtonPress - setSession - success', { data, error }); + return; + } else { + console.error('onSignInButtonPress - setSession - failed'); + // sign in/up failed + } + } else { + console.error('onSignInButtonPress - openAuthSessionAsync - failed'); + } + } + + // to warm up the browser + useEffect(() => { + WebBrowser.warmUpAsync(); + + return () => { + WebBrowser.coolDownAsync(); + }; + }, []); + + return ( + + + + Sign in with Google + + + ); + } + + ``` + + + + + Finally, update the iOS and Android projects by running the Expo prebuild command: + + ```bash + npx expo prebuild --clean + ``` + + Now test the application on both iOS and Android: + + ```bash + npx expo run:ios && npx expo run:android + ``` + + You should see the login screen with the Google authentication button. + + ![Supabase Social Auth example](/docs/img/supabase-expo-social-auth-tabs.png) + + + diff --git a/apps/docs/public/img/supabase-expo-social-auth-login.png b/apps/docs/public/img/supabase-expo-social-auth-login.png new file mode 100644 index 00000000000..aecbe3f1378 Binary files /dev/null and b/apps/docs/public/img/supabase-expo-social-auth-login.png differ diff --git a/apps/docs/public/img/supabase-expo-social-auth-tabs.png b/apps/docs/public/img/supabase-expo-social-auth-tabs.png new file mode 100644 index 00000000000..e9dd42918ea Binary files /dev/null and b/apps/docs/public/img/supabase-expo-social-auth-tabs.png differ diff --git a/apps/docs/public/img/supabase-expo-social-auth.png b/apps/docs/public/img/supabase-expo-social-auth.png deleted file mode 100644 index c70b292672e..00000000000 Binary files a/apps/docs/public/img/supabase-expo-social-auth.png and /dev/null differ diff --git a/examples/auth/expo-social-auth/.env.template b/examples/auth/expo-social-auth/.env.template index b1ade721bfb..fd2c50b3e06 100644 --- a/examples/auth/expo-social-auth/.env.template +++ b/examples/auth/expo-social-auth/.env.template @@ -2,3 +2,4 @@ EXPO_PUBLIC_SUPABASE_URL="" EXPO_PUBLIC_SUPABASE_ANON_KEY="" EXPO_PUBLIC_APPLE_AUTH_SERVICE_ID="" EXPO_PUBLIC_APPLE_AUTH_REDIRECT_URI="" +EXPO_PUBLIC_GOOGLE_AUTH_WEB_CLIENT_ID="" diff --git a/examples/auth/expo-social-auth/app.json b/examples/auth/expo-social-auth/app.json index 0d3224fcb22..fead7204417 100644 --- a/examples/auth/expo-social-auth/app.json +++ b/examples/auth/expo-social-auth/app.json @@ -38,7 +38,13 @@ } ], "expo-secure-store", - "expo-apple-authentication" + "expo-apple-authentication", + [ + "expo-web-browser", + { + "experimentalLauncherActivity": false + } + ] ], "experiments": { "typedRoutes": true diff --git a/examples/auth/expo-social-auth/app/login.tsx b/examples/auth/expo-social-auth/app/login.tsx index 97f69b69932..27ad9522df9 100644 --- a/examples/auth/expo-social-auth/app/login.tsx +++ b/examples/auth/expo-social-auth/app/login.tsx @@ -3,14 +3,18 @@ import { Platform, StyleSheet } from 'react-native'; import { ThemedText } from '@/components/ThemedText'; import { ThemedView } from '@/components/ThemedView'; + import AppleSignInButton from '@/components/social-auth-buttons/apple/AppleSignInButton'; import ExpoAppleSignInButton from '@/components/social-auth-buttons/apple/ExpoAppleSignInButton'; +import GoogleSignInButton from '@/components/social-auth-buttons/google/GoogleSignInButton'; +import { Image } from 'expo-image'; export default function LoginScreen() { return ( <> + Login Try to navigate to home screen! @@ -26,6 +30,7 @@ export default function LoginScreen() { )} {Platform.OS !== 'ios' && ()} + @@ -38,12 +43,14 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', padding: 20, - }, - link: { - marginTop: 15, - paddingVertical: 15, + gap: 20, }, socialAuthButtonsContainer: { + display: 'flex', gap: 10, }, + image: { + width: 100, + height: 100, + }, }); diff --git a/examples/auth/expo-social-auth/assets/supabase-logo-icon.svg b/examples/auth/expo-social-auth/assets/supabase-logo-icon.svg new file mode 100644 index 00000000000..ad802ac16ae --- /dev/null +++ b/examples/auth/expo-social-auth/assets/supabase-logo-icon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/examples/auth/expo-social-auth/components/social-auth-buttons/google/GoogleSignInButton.tsx b/examples/auth/expo-social-auth/components/social-auth-buttons/google/GoogleSignInButton.tsx new file mode 100644 index 00000000000..f502a75e641 --- /dev/null +++ b/examples/auth/expo-social-auth/components/social-auth-buttons/google/GoogleSignInButton.tsx @@ -0,0 +1,126 @@ +import { supabase } from '@/lib/supabase'; +import { useEffect } from 'react'; +import { TouchableOpacity } from 'react-native'; + +import { expo } from '@/app.json'; +import { Text } from '@react-navigation/elements'; +import { Image } from 'expo-image'; +import * as WebBrowser from "expo-web-browser"; + +WebBrowser.maybeCompleteAuthSession(); + +export default function GoogleSignInButton() { + + function extractParamsFromUrl(url: string) { + const parsedUrl = new URL(url); + const hash = parsedUrl.hash.substring(1); // Remove the leading '#' + const params = new URLSearchParams(hash); + + return { + access_token: params.get("access_token"), + expires_in: parseInt(params.get("expires_in") || "0"), + refresh_token: params.get("refresh_token"), + token_type: params.get("token_type"), + provider_token: params.get("provider_token"), + code: params.get("code"), + }; + }; + + async function onSignInButtonPress() { + console.debug('onSignInButtonPress - start'); + const res = await supabase.auth.signInWithOAuth({ + provider: "google", + options: { + redirectTo: `${expo.scheme}://google-auth`, + queryParams: { prompt: "consent" }, + skipBrowserRedirect: true, + }, + }); + + const googleOAuthUrl = res.data.url; + + if (!googleOAuthUrl) { + console.error("no oauth url found!"); + return; + } + + const result = await WebBrowser.openAuthSessionAsync( + googleOAuthUrl, + `${expo.scheme}://google-auth`, + { showInRecents: true }, + ).catch((err) => { + console.error('onSignInButtonPress - openAuthSessionAsync - error', { err }) + console.log(err); + }); + + console.debug('onSignInButtonPress - openAuthSessionAsync - result', { result }); + + if (result && result.type === "success") { + console.debug('onSignInButtonPress - openAuthSessionAsync - success'); + const params = extractParamsFromUrl(result.url); + console.debug('onSignInButtonPress - openAuthSessionAsync - success', { params }); + + if (params.access_token && params.refresh_token) { + console.debug('onSignInButtonPress - setSession'); + const { data, error } = await supabase.auth.setSession({ + access_token: params.access_token, + refresh_token: params.refresh_token, + }); + console.debug('onSignInButtonPress - setSession - success', { data, error }); + return; + } else { + console.error('onSignInButtonPress - setSession - failed'); + // sign in/up failed + } + } else { + console.error('onSignInButtonPress - openAuthSessionAsync - failed'); + } + } + + // to warm up the browser + useEffect(() => { + WebBrowser.warmUpAsync(); + + return () => { + WebBrowser.coolDownAsync(); + }; + }, []); + + return ( + + + + Sign in with Google + + + ); +} diff --git a/examples/auth/expo-social-auth/components/social-auth-buttons/google/GoogleSignInButton.web.tsx b/examples/auth/expo-social-auth/components/social-auth-buttons/google/GoogleSignInButton.web.tsx new file mode 100644 index 00000000000..11bd8879b98 --- /dev/null +++ b/examples/auth/expo-social-auth/components/social-auth-buttons/google/GoogleSignInButton.web.tsx @@ -0,0 +1,73 @@ +import { supabase } from '@/lib/supabase'; +import { CredentialResponse, GoogleLogin, GoogleOAuthProvider } from '@react-oauth/google'; +import { SignInWithIdTokenCredentials } from '@supabase/supabase-js'; +import { useEffect, useState } from 'react'; + +import 'react-native-get-random-values'; + +export default function GoogleSignInButton() { + + // Generate secure, random values for state and nonce + const [nonce, setNonce] = useState(''); + const [sha256Nonce, setSha256Nonce] = useState(''); + + async function onGoogleButtonSuccess(authRequestResponse: CredentialResponse) { + console.debug('Google sign in successful:', { authRequestResponse }) + if (authRequestResponse.clientId && authRequestResponse.credential) { + const signInWithIdTokenCredentials: SignInWithIdTokenCredentials = { + provider: 'google', + token: authRequestResponse.credential, + nonce: nonce, + } + + const { data, error } = await supabase.auth.signInWithIdToken(signInWithIdTokenCredentials) + + if (error) { + console.error('Error signing in with Apple:', error) + } + + if (data) { + console.log('Apple sign in successful:', data) + } + } + } + + function onGoogleButtonFailure() { + console.error('Error signing in with Google') + } + + useEffect(() => { + function generateNonce(): string { + const array = new Uint32Array(1); + window.crypto.getRandomValues(array); + return array[0].toString(); + }; + + async function generateSha256Nonce(nonce: string): Promise { + const buffer = await window.crypto.subtle.digest('sha-256', new TextEncoder().encode(nonce)); + const array = Array.from(new Uint8Array(buffer)); + return array.map(b => b.toString(16).padStart(2, '0')).join(''); + } + + let nonce = generateNonce(); + setNonce(nonce); + + generateSha256Nonce(nonce) + .then((sha256Nonce) => { setSha256Nonce(sha256Nonce) }); + }, []); + + return ( + + + + ); +} \ No newline at end of file diff --git a/examples/auth/expo-social-auth/package-lock.json b/examples/auth/expo-social-auth/package-lock.json index d57565197b7..5145f3032a2 100644 --- a/examples/auth/expo-social-auth/package-lock.json +++ b/examples/auth/expo-social-auth/package-lock.json @@ -14,6 +14,7 @@ "@react-navigation/bottom-tabs": "^7.3.10", "@react-navigation/elements": "^2.3.8", "@react-navigation/native": "^7.1.6", + "@react-oauth/google": "^0.12.2", "@supabase/supabase-js": "^2.55.0", "expo": "~53.0.20", "expo-apple-authentication": "~7.2.4", @@ -3128,6 +3129,16 @@ "nanoid": "^3.3.11" } }, + "node_modules/@react-oauth/google": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.12.2.tgz", + "integrity": "sha512-d1GVm2uD4E44EJft2RbKtp8Z1fp/gK8Lb6KHgs3pHlM0PxCXGLaq8LLYQYENnN4xPWO1gkL4apBtlPKzpLvZwg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", diff --git a/examples/auth/expo-social-auth/package.json b/examples/auth/expo-social-auth/package.json index 1568d7d8209..111aa8f117d 100644 --- a/examples/auth/expo-social-auth/package.json +++ b/examples/auth/expo-social-auth/package.json @@ -17,8 +17,10 @@ "@react-navigation/bottom-tabs": "^7.3.10", "@react-navigation/elements": "^2.3.8", "@react-navigation/native": "^7.1.6", + "@react-oauth/google": "^0.12.2", "@supabase/supabase-js": "^2.55.0", "expo": "~53.0.20", + "expo-apple-authentication": "~7.2.4", "expo-blur": "~14.1.5", "expo-constants": "~17.1.7", "expo-font": "~13.3.2", @@ -44,8 +46,7 @@ "react-native-url-polyfill": "^2.0.0", "react-native-web": "~0.20.0", "react-native-webview": "13.13.5", - "uuid": "^11.1.0", - "expo-apple-authentication": "~7.2.4" + "uuid": "^11.1.0" }, "devDependencies": { "@babel/core": "^7.25.2",