0

I am doing auth for my React Native app. I have two methods for now, one is email and other is Google sign in. I am managing the auth using a context provider passed to the root layout file.

The page routes to main page if I log in using email but not Google sign in unless I reload the app.

Here are my files:

AuthContext.js

import AsyncStorage from '@react-native-async-storage/async-storage';
import { GoogleSignin } from '@react-native-google-signin/google-signin';
import React, { createContext, useContext, useEffect, useState } from 'react';
import { processSignIn } from '../api/auth';

const AuthContext = createContext(null);


export const AuthProvider = ({ children }) => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [isLoading, setIsLoading] = useState(true);
  const [isSigningIn, setIsSigningIn] = useState(false);

  // Configure Google Signin on mount

  GoogleSignin.configure({
    webClientId: '', // 
    offlineAccess: true,
  });



  const logStorage = async () => {
    const authToken = await AsyncStorage.getItem('auth')
    console.log("LOGGING AUTH_TOKEN", authToken)
  }

  useEffect(() => {
    const loadAuthState = async () => {
      try {
        let auth = await AsyncStorage.getItem('auth');

        // If 'auth' doesn't exist yet (first launch), set it to false
        if (auth === null) {
          await AsyncStorage.setItem('auth', 'false');
          auth = 'false';
        }

        setIsAuthenticated(auth === 'true');
      } catch (err) {
        console.error('Error loading auth state', err);
        setIsAuthenticated(false);
      } finally {
        setIsLoading(false);
      }
      logStorage();
    };

    loadAuthState();
  }, []);

  useEffect(() => {
    console.log("Auth state changed:", isAuthenticated);
  }, [isAuthenticated]);

  // Google Sign-In login
  const signInWithGoogle = async () => {
    if (isSigningIn) return; // Block duplicate sign-ins
    setIsSigningIn(true);

    try {
      // Make sure Play Services is available
      await GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: true });

      // Optional: clear any unfinished sign-in state
      // await GoogleSignin.signOut();

      const userInfo = await GoogleSignin.signIn();
      console.log("Google User Info:", userInfo);
      // Store authentication
      // if (userInfo.type === "success") {
      await AsyncStorage.setItem('auth', 'true');
      await AsyncStorage.setItem('googleUser', JSON.stringify(userInfo));
      setIsAuthenticated(true);
      // }
    } catch (error) {
      console.error('Google Sign-In Error', error);
    } finally {
      setIsSigningIn(false);
    }
   
  };



  const login = async (data) => {
    // await AsyncStorage.setItem('auth', 'true');
    // await AsyncStorage.setItem('firstLogin','true')
    try {
      const res = await processSignIn(data);
      if (res.status == 200) {
        console.log("Login Success")
        console.log(res)
        await AsyncStorage.setItem('auth', 'true')
        setIsAuthenticated(true);
      } else {
        console.log("Login Failed", res.message)
      }
    } catch (error) {
      console.log(error)
    }
  };

  const logout = async () => {
    try {
      // Sign out from Google
      await GoogleSignin.signOut();

      // Clear saved auth state
      await AsyncStorage.removeItem('auth');
      await AsyncStorage.removeItem('googleUser');

      // Update app state so it shows login screen again
      setIsAuthenticated(false);

      console.log('Logged out successfully');
    } catch (error) {
      console.error('Error logging out', error);
    }
  };

  if (isLoading) {
    return null; // Replace with splash screen if needed
  }

  return (
    <AuthContext.Provider value={{ isAuthenticated, login, logout, isLoading, signInWithGoogle }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);

_layout.jsx

import { Redirect, Stack } from 'expo-router';
import { useAuth } from '../utils/authContext';


export default function RootLayout() {
  const { isAuthenticated, isLoading } = useAuth();

  if (!isAuthenticated) {
    return (<Redirect href="/login"/>);
  }
  
  return (

    <Stack screenOptions={{ headerShown: false }}>
        <Stack.Screen name="(drawer)" />
    </Stack>
  );
}
1
  • Does it work if you comment out the line that sets the googleUser in storage? Commented Aug 16 at 15:48

1 Answer 1

0

You are calling GoogleSignin.configure() on every render in the AuthProvider because you placed it within the component.

// AuthContext.jsx
export const AuthProvider = ({ children }) => {
  // ...

  GoogleSignin.configure({
    webClientId: '', 
    offlineAccess: true,
  });

  // ...
}

Placing it into the component directly causes it to run multiple times. You shouldn't do this because you're probably resetting GoogleSignin by configuring it over and over again. In the docs they mention to call this function only once:

Typically, you would call configure only once, soon after your app starts. ~ link

As a quick fix, you can try moving the GoogleSignin.configure() part into a useEffect without any dependency to prevent multiple executions.

// AuthContext.jsx
export const AuthProvider = ({ children }) => {
  // ...
  useEffect(() => {
    GoogleSignin.configure({
      webClientId: '', 
      offlineAccess: true,
    });
  }, []);

  // ...
}

But as a final solution I would suggest you to put it in the App.jsx but outside of the component, so that it gets called only once on app start:

// App.jsx

// initialise GoogleSign in outside of App component
GoogleSignin.configure({
  webClientId: '', 
  offlineAccess: true,
});

export const App = () => {
  // ...
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.