因此,我试图使用OAuth签名(在这种情况下是导师)。我正在使用本地Postgres数据库和Zustand用于状态管理。用户应在成功登录后导航到一个称为“/仪表板”的受保护路线,该路由由包装器保护,该包装器检查AUTH状态是否已对true设置为true drime。 这是我的标志:tsx:
import { useState } from 'react';
import { Link } from 'react-router-dom';
import { Button } from '@/components/ui/button';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
import { Mail, Lock, Loader2 } from 'lucide-react';
import { supabase } from "@/lib/supabase";
import { useAuthStore } from '@/store/authStore';
import { FcGoogle } from "react-icons/fc";
import { FaLinkedin } from "react-icons/fa";
export default function SignIn() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [isLoading, setIsLoading] = useState(false);
const setUser = useAuthStore((state) => state.setUser);
const navigate = useNavigate();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
try {
const { data } = await axios.post("http://localhost:5000/auth/signin", {
email,
password,
});
setUser(data.mentor); // Store mentor data in Zustand
navigate("/dashboard");
} catch (error: any) {
alert(error.response?.data?.error || "Sign-in failed");
} finally {
setIsLoading(false);
}
};
const handleOAuthLogin = async (provider: "google" | "linkedin_oidc") => {
try {
setIsLoading(true);
const { data, error } = await supabase.auth.signInWithOAuth({ provider });
console.log("oAuth : ", data);
if (error) {
alert(error.message);
return;
}
// Wait for the OAuth process to complete
supabase.auth.onAuthStateChange(async (event, session) => {
if (event === "SIGNED_IN" && session?.user) {
try {
const { data: mentor } = await axios.get(`http://localhost:5000/auth/mentor/${session.user.id}`);
setUser(mentor); // Update Zustand store
navigate("/dashboard"); // Redirect to dashboard
} catch (err) {
console.error("Error fetching mentor profile:", err);
alert("Failed to fetch user profile");
}
}
});
} catch (err) {
console.error(err);
} finally {
setIsLoading(false);
}
};
return (
<div className="min-h-screen flex items-center justify-center p-4">
<div className="w-full max-w-md">
<Card className="border-none shadow-xl">
<CardHeader className="space-y-3">
<CardTitle className="text-3xl font-bold text-center">Welcome back</CardTitle>
<CardDescription className="text-center text-gray-600">
Enter your email and password to access your account
</CardDescription>
</CardHeader>
<form onSubmit={handleSubmit}>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<div className="relative">
<Mail className="absolute left-3 top-3 h-5 w-5 text-gray-400" />
<Input
id="email"
type="email"
value={email}
placeholder="[email protected]"
className="pl-10"
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="password">Password</Label>
<div className="relative">
<Lock className="absolute left-3 top-3 h-5 w-5 text-gray-400" />
<Input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Enter your password"
className="pl-10"
required
/>
</div>
</div>
<Button
type="submit"
className="w-full bg-blue-600 hover:bg-blue-700"
disabled={isLoading}
>
{isLoading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Signing in...
</>
) : (
'Sign In'
)}
</Button>
</CardContent>
</form>
<CardFooter className="flex flex-col space-y-4 text-center">
<Button
onClick={() => handleOAuthLogin("google")}
className="w-full bg-white border border-gray-300 hover:bg-gray-100 flex items-center justify-center space-x-2"
disabled={isLoading}
>
{isLoading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Logging in with Google...
</>
) : (
<>
<FcGoogle className="h-5 w-5" />
<span className="text-gray-700">Sign in with Google</span>
</>
)}
</Button>
<Button
onClick={() => handleOAuthLogin("linkedin_oidc")}
className="w-full bg-blue-500 hover:bg-blue-600 text-white flex items-center justify-center space-x-2"
disabled={isLoading}
>
{isLoading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Logging in with LinkedIn...
</>
) : (
<>
<FaLinkedin className="h-5 w-5" />
<span className="text-white">Sign in with LinkedIn</span>
</>
)}
</Button>
<div className="text-sm text-gray-600">
Don't have an account?{' '}
<Link to="/signup" className="font-semibold text-blue-600 hover:text-blue-700">
Sign up
</Link>
</div>
</CardFooter>
</Card>
</div>
</div>
);
}
为了在OAuth登录中更新状态,我正在等待OAuthStateChange事件触发并更改为Signed_in,然后命中的API根据从Oauth会话中检索到的用户ID中获取用户的详细信息:
router.get("/mentor/:id", async (req: any, res: any) => {
const { id } = req.params;
try {
const mentor = await prisma.mentorProfile.findUnique({
where: { mentor_id: id },
});
if (!mentor) return res.status(404).json({ error: "Mentor profile not found" });
return res.status(200).json(mentor);
} catch (err) {
console.error("Error fetching mentor profile:", err);
return res.status(500).json({ error: "Server error", details: err });
}
});
问题是,这个oauthstatatechange似乎花了太多时间,因为发生的事情是:
我选择电子邮件和所有内容,我在DevTools的申请部分中获得
AccessToken在您的thansoauthlogin功能中,您正在启动登录过程后设置OAuth状态更改侦听器。这是有问题的,因为:
将重定向到“/”很可能发生,因为OAuth流程完成了,但是您的应用程序尚未处理已验证的状态。
在这里我要解决这个问题://一次设置auth状态侦听器,在手柄函数之外 //在组件级别的使用效果中添加它
supabase.auth.onAuthStateChange(async (event, session) => {
if (event === "SIGNED_IN" && session?.user) {
try {
const { data: mentor } = await axios.get(`http://localhost:5000/auth/mentor/${session.user.id}`);
setUser(mentor);
navigate("/dashboard");
} catch (err) {
console.error("Error fetching mentor profile:", err);
alert("Failed to fetch user profile");
}
} });
// Clean up the listener when component unmounts return () => {
authListener.subscription.unsubscribe(); }; }, []);`
//然后将您的ashaneauthlogin函数修改为简单
"linkedin_oidc") => { try {
setIsLoading(true);
const { error } = await supabase.auth.signInWithOAuth({
provider,
options: {
redirectTo: window.location.origin // Ensure it redirects back to your app
}
});
if (error) {
alert(error.message);
} } catch (err) {
console.error(err);
alert("Authentication failed"); } finally {
setIsLoading(false); } };`
Another issue might be that you're not handling the OAuth redirect
properly. The OAuth flow typically redirects the user away from your
application and then back after authentication. When the user returns,
you need to check if they're already authenticated. Add this to your
component:
> ```
>
> component mounts const checkUser = async () => {
> const { data: { session } } = await supabase.auth.getSession();
> if (session?.user) {
> try {
> const { data: mentor } = await axios.get(`http://localhost:5000/auth/mentor/${session.user.id}`);
> setUser(mentor);
> navigate("/dashboard");
> } catch (err) {
> console.error("Error fetching mentor profile:", err);
> }
> } };
> checkUser(); }, []); `useEffect(() => { // Check if user is already authenticated when
This approach should:
Set up the auth state listener once when the component mounts Check if
the user is already authenticated when they land on the page (which
handles the redirect back case) Properly clean up listeners to prevent
memory leaks and duplicate event handlers
Don't forget to import useEffect at the top of your file:
` import { useState, useEffect } from 'react'; `