Overview
I am developing an app in React Native using Expo, and I am trying to implement authentication with Google. However, when I try to log in, I get an error and cannot complete the authentication flow, Despite having configured google cloud with the Oauth2 and the consent screen and having made a build and run in an emulator the app with the command “npx expo run:android”
Things that i made:
- I set up the google play console, created the OAuth consent screen with the publication type in Test, some test users, and created OAuth 2.0 credentials.
- In the OAuth2.0 I added the package name that corresponds with my expo.android.package and the SHA-1 certificate Fingerprint, pulled it using
eas credentials
command
,then in the Custom URI scheme, I enabled the checkbox, otherwise it wouldn’t work directly for me. - I implemented the google auth functionality with the library
expo-auth-session
with the providerproviders/google
- I have created a build using the command
npx expo run:android
to run the app on an emulator, as I’ve seen in several videos
A small aside
Before I had the authentication working, but now it doesn’t work anymore, so I have generated new credentials with the “eas credentials” and added the new sha1key in the new OAuth2 credential, actually using (1), as Default.
Result:
Even though the browser opens, allows me to select a user, and provides the requested data,
Once it closes, I don’t receive the user’s information for some reason.
And the console.log “response” is always null…
My code of the log-in Page:
import React, {useContext, useEffect, useState} from 'react';
import {Form, H4, Spinner, Input, View, Text, H6} from 'tamagui'
import {Button} from "tamagui.config"
import {Pressable, SafeAreaView, Platform, Image, StyleSheet} from "react-native";
import {loadUser, login, register} from "~/services/AuthService";
import * as WebBrowser from "expo-web-browser";
import * as Google from "expo-auth-session/providers/google";
import {getUserInfo} from "~/services/GoogleAuthService";
import {useAuth} from "~/contexts/NewAuthContext";
import {useRouter} from "expo-router";
WebBrowser.maybeCompleteAuthSession();
// @ts-ignore
const Login = () => {
const { signIn } = useAuth();
const router = useRouter()
const [isLoadingLogin, setIsLoadingLogin] = useState(false)
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const [errors, setErrors] = useState({})
// const {setUser} = useContext(AuthContext);
const [request, response, promptAsync] = Google.useAuthRequest({
androidClientId: "....CLIENT_ID.....",
})
useEffect(() => {
handleSingInWithGoogle()
}, [response])
async function handleSingInWithGoogle(){
// Here the response is always "null"....
console.log("response", response)
if(response?.type === "success"){
// It never happens because it is always null...
console.log("response success", response)
const googleAccessToken = response.authentication.accessToken
const googleUserData = await getUserInfo(googleAccessToken)
try {
await register({
"email": googleUserData.email,
"name": googleUserData.given_name,
"surname": googleUserData.family_name,
"picture": googleUserData.picture,
"google_id": googleUserData.id,
"register_type": "google_auth",
"device_name": `${Platform.OS} - ${Platform.Version}`
})
const user = await loadUser();
signIn(user)
} catch (e) {
console.error("error login-in with google")
}
}
}
function validateForm() {
const newErrors = {};
if (!email.trim()) {
newErrors.email = "empty";
}
if (!password.trim()) {
newErrors.password = "empty";
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
}
async function submitLoginForm(){
try {
setIsLoadingLogin(true)
// Solo continúa si la validación es exitosa
if (!validateForm()) {
return; // Detiene la ejecución si hay errores
}
await login({
email,
password,
device_name: `${Platform.OS} - ${Platform.Version}`
});
const user = await loadUser();
console.info("login function user : ", user)
signIn(user)
} catch(e){
if (e.response.status === 422){
setErrors({password: "credentials not found"});
}
} finally {
setIsLoadingLogin(false)
}
}
function redirectRegister() {
setErrors({})
router.push("/(auth)/register")
}
return (
<SafeAreaView style={{flex: 1, backgroundColor: "#e3e3e3"}}>
<Form
display="flex"
justifyContent="center"
alignItems="center"
gap="$2"
height={550}
onSubmit={() => submitLoginForm()}
backgroundColor="#e3e3e3"
padding="$10"
>
<H4>Log-in</H4>
<View minWidth={350} marginVertical={12} flex={1} gap={10}>
<View marginBottom={10}>
<Input
placeholder="Email"
value={email}
onChangeText={(text) => {
setEmail(text);
// Limpia el error cuando el usuario empieza a escribir
if (errors.email) {
setErrors(prev => ({ ...prev, email: null }));
}
}}
keyboardType="email-address"
autoCapitalize="none"
/>
{errors.email && <Text style={styles.errorText}>El email no puede estar vacio</Text>}
</View>
<View>
<Input
placeholder="Contraseña"
value={password}
onChangeText={setPassword}
secureTextEntry={true}
autoCapitalize="none"
/>
{errors.password && errors.password !== 'credentials not found' && <Text style={styles.errorText}>La contraseña no puede estar vacía</Text>}
{errors.password && errors.password === 'credentials not found' && <Text style={styles.errorText}>No existe ningun registro con ese email y contraseña</Text>}
</View>
<View>
<Pressable
onPress={() => promptAsync()}
style={({ pressed }) => ({
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
padding: 10,
borderRadius: 30,
backgroundColor: pressed ? '#f0f0f0' : '#ffffff',
borderWidth: 1,
borderColor: '#dcdcdc',
})}
>
<Text paddingRight={9} fontWeight={600} fontSize={15.5}>
Entrar con Google
</Text>
<Image
source={require('~/assets/images/google-logo.png')}
style={{
width: 22,
height: 22,
}}
/>
</Pressable>
</View>
<Form.Trigger asChild disabled={isLoadingLogin}>
<Button theme="blue" icon={isLoadingLogin ? () => <Spinner /> : undefined}>
{isLoadingLogin ? 'Iniciando sesión...' : 'Iniciar sesión'}
</Button>
</Form.Trigger>
</View>
</Form>
</SafeAreaView>
)
}
export default Login;