React Native Auth Context 无法与 Google 重定向正常正常工作

问题描述 投票:0回答:1

我正在使用

expo-auth-session
库在我的 React Native 项目中实现 Google 身份验证。

问题是,在选择用户并接受同意屏幕后,当redirectBack恰好返回Google响应令牌时,管理身份验证的AuthContext似乎配置错误。我相信重定向会导致视图重新加载,并且响应未正确处理。

为什么我认为问题出在 AuthContext 上? 因为如果我将所有登录逻辑移至主 _layout.tsx 文件中并删除 AuthContext,它就会正常工作并正确返回令牌。

奇怪的是登录工作正常...(使用我的后端),我认为这可能与谷歌的重定向有关

这是我的应用程序目录结构:

enter image description here

好吧,这就是

app/_layout.tsx
(入口点)

function RootLayoutNavigation() {
  const tamaguiConfig = createTamagui(config);

  return (
    <TamaguiProvider config={tamaguiConfig}>
      <Theme name="light">
        <AuthProvider>
          <SafeAreaView style={{ flex: 1 }}>
            <StatusBar barStyle="default" backgroundColor="#f2f2f2" translucent />
            <Slot />
          </SafeAreaView>
        </AuthProvider>
      </Theme>
    </TamaguiProvider>
  );
}

为了管理身份验证,我创建了一个上下文(我怀疑它配置错误,可能是问题的根源)

AuthContext.tsx

import React, {useContext, createContext, type PropsWithChildren, useEffect, useState} from 'react';
import {loadUser, login, logout} from "~/services/AuthService";
import {useRouter, useSegments} from "expo-router";
import {getToken} from "~/services/TokenService";
import {ActivityIndicator} from "react-native";
import {View} from "tamagui";

type User = any;

interface AuthContextType {
  user: User | null;
  signIn: (userData: User) => void;
  signOut: () => Promise<void>;
  isLoading: boolean;
  isAuthenticated: boolean;
}

const AuthContext = createContext<AuthContextType>({
  user: null,
  signIn: () => {},
  signOut: async () => {},
  isLoading: true,
  isAuthenticated: false,
});

function useProtectedRoute(user: User | null) {
  const segments = useSegments();
  const router = useRouter();
  console.log("segments", segments)

  useEffect(() => {
    if (segments.length === 0) return;

    const inAuthGroup = segments[0] === '(auth)';

    if (!user && !inAuthGroup) {
      console.log("entra 1")
      router.replace('/(auth)/login');
    } else if (user && inAuthGroup) {
      console.log("entra 2")
      router.replace('/(drawer)');
    }

  }, [user, segments]);
}

export function AuthProvider({ children }) {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  useProtectedRoute(user);

  useEffect(() => {
    async function loadSession() {
      try {
        const token = await getToken();
        if (token) {
          const userData = await loadUser();
          setUser(userData);
          setIsAuthenticated(true);
        }
      } catch (error) {
        console.error('Error loading session:', error);
      } finally {
        setIsLoading(false);
      }
    }

    loadSession();
  }, []);

  const signIn = (userData: User) => {
    setUser(userData);
    setIsAuthenticated(true);
  };

  const signOut = async () => {
    try {
      await logout();
      setUser(null);
      setIsAuthenticated(false);
    } catch (error) {
      console.error('Error during logout:', error);
    }
  };

  return (
    <AuthContext.Provider value={{ user, signIn, signOut, isLoading, isAuthenticated }}>
      {!isLoading ? children : (
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
          <ActivityIndicator size="large" />
        </View>
      )}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
}

还有登录页面

login.tsx

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 config = {
    androidClientId: "client-id",
    webClientId: "client-id 2",
    expoClientId: "client-id 3"
  }

  const [request, response, promptAsync] = Google.useAuthRequest(config);


  React.useEffect(() => {
    handleEffect()
  }, [response])

  async function handleEffect(){
    // Here is always null.
    console.warn("handle Effect() - token " + response)
  }


  return (
    <SafeAreaView style={{flex: 1, backgroundColor: "#e3e3e3"}}>
        <H4>Log-in</H4>
        <Text>{JSON.stringify(response)}</Text> // Also there is always null.

        <View minWidth={350} marginVertical={12} flex={1} gap={10}>
          <View marginBottom={10}>
            <Input
              placeholder="Email"
              value={email}
              onChangeText={(text) => {
                setEmail(text);
              }}
              keyboardType="email-address"
              autoCapitalize="none"
            />
          </View>
          <View>
            <Input
              placeholder="Contraseña"
              value={password}
              onChangeText={setPassword}
              secureTextEntry={true}
              autoCapitalize="none"
            />
          </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>
        </View>
    </SafeAreaView>
  )
}
android reactjs react-native expo google-oauth
1个回答
0
投票

问题出现是因为我在Google.useAuthRequest中没有redirectUri参数。

const config = {
  androidClientId: "client-id",
  webClientId: "client-id 2",
  expoClientId: "client-id 3",
  redirectUri: "schema:///login"  - Need this line of code.

这样,一旦重定向到您的应用程序,令牌就会返回到视图。

就我而言,尽管一切都配置良好,但发生的情况是,在重定向回来时,由于我配置 AuthContext 的方式,令牌没有返回到登录并且没有捕获它。

令人好奇的是,如果我直接在应用程序的入口点实现此逻辑

app/_layout.tsx
它可以正常工作,但一定是通过我创建的 AuthContext 的某些移动,有必要很好地管理重定向问题。

Expo Auth 会话库文档: https://docs.expo.dev/versions/latest/sdk/auth-session/

© www.soinside.com 2019 - 2024. All rights reserved.