Enhance authentication and debugging experience by adding detailed logging for cookie checks, session management, and user redirection. Update middleware to log authentication cookie status and user login state. Refactor login and debug pages to use hard redirects for improved reliability and include session data display. Implement custom cookie handling in Supabase client for better session management.
This commit is contained in:
2
.env.local
Normal file
2
.env.local
Normal file
@@ -0,0 +1,2 @@
|
||||
# Override the port to match what's in .env
|
||||
PORT=3007
|
||||
@@ -7,8 +7,11 @@ export async function GET(request: NextRequest) {
|
||||
const requestUrl = new URL(request.url);
|
||||
const code = requestUrl.searchParams.get('code');
|
||||
|
||||
console.log('Auth callback received:', { url: request.url, hasCode: !!code });
|
||||
|
||||
// 如果没有code参数,则重定向到登录页面
|
||||
if (!code) {
|
||||
console.log('没有找到code参数,重定向到登录页面');
|
||||
return NextResponse.redirect(new URL('/login', request.url));
|
||||
}
|
||||
|
||||
@@ -18,9 +21,42 @@ export async function GET(request: NextRequest) {
|
||||
const supabaseRouteHandler = createRouteHandlerClient({ cookies: () => cookieStore });
|
||||
|
||||
// 交换code获取会话
|
||||
await supabaseRouteHandler.auth.exchangeCodeForSession(code);
|
||||
console.log('开始交换code获取会话');
|
||||
const { data, error } = await supabaseRouteHandler.auth.exchangeCodeForSession(code);
|
||||
|
||||
// 直接重定向到首页,避免中间跳转
|
||||
if (error) {
|
||||
console.error('交换会话时出错:', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.log('成功获取会话,用户:', data.session?.user.email);
|
||||
|
||||
// 检查会话是否成功创建
|
||||
if (data.session) {
|
||||
console.log('会话创建成功:', {
|
||||
userId: data.session.user.id,
|
||||
email: data.session.user.email,
|
||||
expiresAt: data.session.expires_at ? new Date(data.session.expires_at * 1000).toISOString() : 'unknown'
|
||||
});
|
||||
|
||||
// 设置额外的cookie以确保客户端能检测到登录状态
|
||||
// 使用Next.js的Response来设置cookie
|
||||
const response = NextResponse.redirect(new URL('/', request.url));
|
||||
response.cookies.set({
|
||||
name: 'sb-auth-token',
|
||||
value: 'true',
|
||||
path: '/',
|
||||
maxAge: 60 * 60 * 24 * 7, // 7 days
|
||||
sameSite: 'lax',
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
httpOnly: false,
|
||||
});
|
||||
console.log('设置了备用cookie: sb-auth-token');
|
||||
return response;
|
||||
}
|
||||
|
||||
// 优先使用应用程序根路径重定向
|
||||
console.log('重定向到首页');
|
||||
return NextResponse.redirect(new URL('/', request.url));
|
||||
} catch (error) {
|
||||
console.error('Auth callback error:', error);
|
||||
|
||||
@@ -8,6 +8,8 @@ export default function DebugPage() {
|
||||
const { user, session, isLoading } = useAuth();
|
||||
const [cookies, setCookies] = useState<Record<string, string>>({});
|
||||
const [rawCookies, setRawCookies] = useState('');
|
||||
const [sessionData, setSessionData] = useState<{ session: any; user: any } | null>(null);
|
||||
const [redirectTarget, setRedirectTarget] = useState('/analytics');
|
||||
|
||||
useEffect(() => {
|
||||
// 获取所有cookie
|
||||
@@ -22,14 +24,46 @@ export default function DebugPage() {
|
||||
|
||||
// 测试supabase会话
|
||||
const testSession = async () => {
|
||||
const { data, error } = await supabase.auth.getSession();
|
||||
console.log('Debug page - Supabase session:', data);
|
||||
if (error) console.error('Debug page - Session error:', error);
|
||||
try {
|
||||
console.log('正在获取Supabase会话');
|
||||
const { data, error } = await supabase.auth.getSession();
|
||||
console.log('Supabase session result:', { data, error });
|
||||
|
||||
if (error) {
|
||||
console.error('Session error:', error);
|
||||
} else {
|
||||
setSessionData(data);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('获取会话出错:', err);
|
||||
}
|
||||
};
|
||||
|
||||
testSession();
|
||||
}, []);
|
||||
|
||||
const refreshSession = async () => {
|
||||
try {
|
||||
console.log('手动刷新会话');
|
||||
const { data, error } = await supabase.auth.refreshSession();
|
||||
console.log('刷新结果:', { data, error });
|
||||
alert('会话刷新完成,请查看控制台日志');
|
||||
|
||||
if (!error && data.session) {
|
||||
window.location.reload();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('刷新会话出错:', err);
|
||||
alert('刷新会话出错: ' + String(err));
|
||||
}
|
||||
};
|
||||
|
||||
const forceRedirect = () => {
|
||||
if (redirectTarget) {
|
||||
window.location.href = redirectTarget;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container mx-auto p-8">
|
||||
<h1 className="text-3xl font-bold mb-6">认证调试页面</h1>
|
||||
@@ -42,9 +76,24 @@ export default function DebugPage() {
|
||||
<p>用户邮箱: {user?.email || '未登录'}</p>
|
||||
<p>用户ID: {user?.id || '未登录'}</p>
|
||||
<p>会话有效: {session ? '是' : '否'}</p>
|
||||
<p>会话过期时间: {session?.expires_at ? new Date(session.expires_at * 1000).toLocaleString() : '无会话'}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-100 p-6 rounded-lg mb-6">
|
||||
<h2 className="text-xl font-semibold mb-4">Supabase 会话数据</h2>
|
||||
<pre className="bg-gray-200 p-4 rounded text-xs overflow-auto max-h-60">
|
||||
{sessionData ? JSON.stringify(sessionData, null, 2) : '加载中...'}
|
||||
</pre>
|
||||
|
||||
<button
|
||||
onClick={refreshSession}
|
||||
className="mt-4 px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600"
|
||||
>
|
||||
刷新会话
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-100 p-6 rounded-lg mb-6">
|
||||
<h2 className="text-xl font-semibold mb-4">Cookies 信息</h2>
|
||||
<div className="space-y-2">
|
||||
@@ -67,6 +116,25 @@ export default function DebugPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-gray-100 p-6 rounded-lg mb-6">
|
||||
<h2 className="text-xl font-semibold mb-4">手动重定向</h2>
|
||||
<div className="flex space-x-2 items-center">
|
||||
<input
|
||||
type="text"
|
||||
value={redirectTarget}
|
||||
onChange={(e) => setRedirectTarget(e.target.value)}
|
||||
className="flex-1 px-3 py-2 border border-gray-300 rounded"
|
||||
placeholder="/analytics"
|
||||
/>
|
||||
<button
|
||||
onClick={forceRedirect}
|
||||
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
||||
>
|
||||
强制重定向
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-4">
|
||||
<button
|
||||
onClick={() => window.location.href = '/login'}
|
||||
@@ -76,8 +144,13 @@ export default function DebugPage() {
|
||||
</button>
|
||||
<button
|
||||
onClick={async () => {
|
||||
await supabase.auth.signOut();
|
||||
window.location.reload();
|
||||
try {
|
||||
await supabase.auth.signOut();
|
||||
alert('已登出,刷新页面中...');
|
||||
window.location.reload();
|
||||
} catch (err) {
|
||||
alert('登出出错: ' + String(err));
|
||||
}
|
||||
}}
|
||||
className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
|
||||
>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, Suspense } from 'react';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { useAuth } from '@/lib/auth';
|
||||
|
||||
@@ -20,7 +20,6 @@ function MessageHandler({ setMessage }: { setMessage: (message: { type: string,
|
||||
}
|
||||
|
||||
export default function LoginPage() {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const { signIn, signInWithGoogle, user } = useAuth();
|
||||
|
||||
@@ -43,13 +42,21 @@ export default function LoginPage() {
|
||||
// 如果用户已登录,重定向到原始页面或首页
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
if (redirectUrl) {
|
||||
router.push(redirectUrl);
|
||||
} else {
|
||||
router.push('/');
|
||||
}
|
||||
console.log('用户已登录,准备重定向', { redirectUrl });
|
||||
|
||||
// 添加短暂延时确保状态更新完成
|
||||
setTimeout(() => {
|
||||
if (redirectUrl) {
|
||||
// 使用硬重定向替代router.push
|
||||
console.log('重定向到原始URL:', redirectUrl);
|
||||
window.location.href = redirectUrl;
|
||||
} else {
|
||||
console.log('重定向到首页');
|
||||
window.location.href = '/';
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
}, [user, router, redirectUrl]);
|
||||
}, [user, redirectUrl]);
|
||||
|
||||
const handleEmailSignIn = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
23
app/page.tsx
23
app/page.tsx
@@ -1,31 +1,38 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useAuth } from '@/lib/auth';
|
||||
|
||||
export default function Home() {
|
||||
const router = useRouter();
|
||||
const { user, isLoading } = useAuth();
|
||||
|
||||
// 添加调试日志
|
||||
console.log('根页面状态:', { isLoading, userAuthenticated: !!user });
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading) {
|
||||
console.log('准备从根页面重定向', { isLoggedIn: !!user });
|
||||
|
||||
// 使用硬重定向确保页面完全刷新
|
||||
if (user) {
|
||||
// 已登录用户重定向到分析页面
|
||||
router.push('/analytics');
|
||||
console.log('用户已登录,重定向到分析页面');
|
||||
window.location.href = '/analytics';
|
||||
} else {
|
||||
// 未登录用户重定向到登录页面
|
||||
router.push('/login');
|
||||
console.log('用户未登录,重定向到登录页面');
|
||||
window.location.href = '/login';
|
||||
}
|
||||
}
|
||||
}, [user, isLoading, router]);
|
||||
}, [isLoading, user]);
|
||||
|
||||
// 显示加载指示器
|
||||
// 显示加载指示器,并包含状态信息
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen">
|
||||
<div className="text-center">
|
||||
<div className="animate-spin rounded-full h-16 w-16 border-t-2 border-b-2 border-blue-500 mx-auto"></div>
|
||||
<p className="mt-4 text-lg text-gray-700">正在加载...</p>
|
||||
<p className="mt-2 text-sm text-gray-500">
|
||||
状态: {isLoading ? '检查登录中' : (user ? '已登录' : '未登录')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
11
lib/auth.tsx
11
lib/auth.tsx
@@ -94,10 +94,19 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
return { error };
|
||||
}
|
||||
|
||||
// 登录成功,设置会话和用户信息
|
||||
console.log('登录成功,用户信息:', data.user?.email);
|
||||
setSession(data.session);
|
||||
setUser(data.user);
|
||||
|
||||
router.push('/analytics');
|
||||
// 使用硬重定向代替router.push,确保页面完全刷新
|
||||
console.log('准备重定向到分析页面');
|
||||
|
||||
// 添加短暂延时确保状态已更新,然后重定向
|
||||
setTimeout(() => {
|
||||
window.location.href = '/analytics';
|
||||
}, 100);
|
||||
|
||||
return {};
|
||||
} catch (error) {
|
||||
console.error('登录过程出错:', error);
|
||||
|
||||
@@ -33,12 +33,47 @@ try {
|
||||
console.error('JWT解码失败:', error);
|
||||
}
|
||||
|
||||
// 创建自定义cookie处理逻辑
|
||||
const customStorage = {
|
||||
getItem: (key: string): string | null => {
|
||||
if (typeof document === 'undefined') return null;
|
||||
const cookie = document.cookie
|
||||
.split(';')
|
||||
.find((c) => c.trim().startsWith(`${key}=`));
|
||||
return cookie ? cookie.split('=')[1] : null;
|
||||
},
|
||||
setItem: (key: string, value: string): void => {
|
||||
if (typeof document === 'undefined') return;
|
||||
|
||||
// 获取当前主机和端口,处理localhost上的不同端口情况
|
||||
const host = typeof window !== 'undefined' ? window.location.hostname : '';
|
||||
|
||||
// 设置cookie,对localhost使用通用domain
|
||||
document.cookie = `${key}=${value}; path=/; max-age=${60 * 60 * 24 * 7}; samesite=lax; domain=${host}`;
|
||||
|
||||
console.log(`Cookie ${key} 已设置,domain=${host}`);
|
||||
},
|
||||
removeItem: (key: string): void => {
|
||||
if (typeof document === 'undefined') return;
|
||||
|
||||
// 获取当前主机和端口,处理localhost上的不同端口情况
|
||||
const host = typeof window !== 'undefined' ? window.location.hostname : '';
|
||||
|
||||
// 删除cookie,对localhost使用通用domain
|
||||
document.cookie = `${key}=; path=/; max-age=0; samesite=lax; domain=${host}`;
|
||||
|
||||
console.log(`Cookie ${key} 已移除`);
|
||||
},
|
||||
};
|
||||
|
||||
// 创建Supabase客户端
|
||||
export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
|
||||
auth: {
|
||||
persistSession: true,
|
||||
autoRefreshToken: true,
|
||||
detectSessionInUrl: true,
|
||||
storageKey: 'sb-auth-token',
|
||||
storage: customStorage
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -29,12 +29,23 @@ export function middleware(request: NextRequest) {
|
||||
const allCookies = Object.fromEntries(request.cookies.getAll().map(c => [c.name, c.value]));
|
||||
console.log('[Middleware] 所有Cookie:', JSON.stringify(allCookies));
|
||||
|
||||
// 检查各个认证cookie
|
||||
const accessToken = request.cookies.get('sb-access-token');
|
||||
const refreshToken = request.cookies.get('sb-refresh-token');
|
||||
const providerToken = request.cookies.get('sb-provider-token');
|
||||
const authToken = request.cookies.get('supabase-auth-token');
|
||||
const customAuthToken = request.cookies.get('sb-auth-token');
|
||||
|
||||
console.log('[Middleware] 认证Cookie详情:', {
|
||||
'sb-access-token': accessToken ? '存在' : '不存在',
|
||||
'sb-refresh-token': refreshToken ? '存在' : '不存在',
|
||||
'sb-provider-token': providerToken ? '存在' : '不存在',
|
||||
'supabase-auth-token': authToken ? '存在' : '不存在',
|
||||
'sb-auth-token': customAuthToken ? '存在' : '不存在'
|
||||
});
|
||||
|
||||
// 检查用户是否登录
|
||||
const supabaseCookie = request.cookies.get('sb-access-token') ||
|
||||
request.cookies.get('sb-refresh-token') ||
|
||||
request.cookies.get('sb-provider-token') ||
|
||||
request.cookies.get('supabase-auth-token');
|
||||
const isLoggedIn = !!supabaseCookie;
|
||||
const isLoggedIn = !!(accessToken || refreshToken || providerToken || authToken || customAuthToken);
|
||||
console.log(`[Middleware] 用户是否登录: ${isLoggedIn}`);
|
||||
|
||||
// 如果是公开路径但已登录,重定向到首页
|
||||
|
||||
Reference in New Issue
Block a user