diff --git a/README-google-auth.md b/README-google-auth.md new file mode 100644 index 0000000..059efce --- /dev/null +++ b/README-google-auth.md @@ -0,0 +1,50 @@ +# 配置 Google 登录功能 + +为了启用 Google 登录功能,您需要在 Supabase 和 Google Cloud Platform 进行配置。 + +## 步骤 1: 创建 Google OAuth 客户端 + +1. 访问 [Google Cloud Console](https://console.cloud.google.com/) +2. 创建一个新项目或选择现有项目 +3. 在左侧菜单中导航到 "API 和服务" > "OAuth 同意屏幕" +4. 选择用户类型(外部或内部),然后点击"创建" +5. 填写必要的信息(应用名称、用户支持电子邮件等)并保存 +6. 导航到 "API 和服务" > "凭据" +7. 点击"创建凭据" > "OAuth 客户端 ID" +8. 应用类型选择 "Web 应用" +9. 名称中输入您的应用名称 +10. 添加以下已获授权的重定向 URI: + - `https://mwwvqwevplndzvmqmrxa.supabase.co/auth/v1/callback` +11. 点击"创建" +12. 复制生成的 "客户端 ID" 和 "客户端密钥" + +## 步骤 2: 在 Supabase 中配置 Google 提供商 + +1. 登录 [Supabase 仪表板](https://app.supabase.com) +2. 选择您的项目 +3. 导航到 "身份验证" > "提供商" +4. 找到 Google 提供商并启用它 +5. 粘贴您刚才获取的 "客户端 ID" 和 "客户端密钥" +6. 保存配置 + +## 步骤 3: 更新重定向 URL(如有需要) + +如果您的应用需要在登录后重定向到特定页面,请确保在 Google Cloud Console 和 Supabase 中配置了正确的重定向 URL。 + +在 Supabase 中: +1. 导航到 "身份验证" > "URL 配置" +2. 添加您的前端 URL 到站点 URL 字段中 +3. 设置重定向 URL(通常是您的前端 URL) + +## 测试 + +1. 在您的应用中,尝试使用 Google 登录 +2. 验证认证流程,确保可以成功登录并重定向到应用 +3. 检查 Supabase 中的用户数据,确认新用户已创建 + +## 故障排除 + +- 确保重定向 URI 完全匹配 +- 确保 OAuth 同意屏幕已正确配置 +- 查看 Supabase 和应用程序中的日志以获取详细的错误信息 +- 如果遇到 CORS 错误,检查您的站点 URL 配置 \ No newline at end of file diff --git a/app/auth/callback/route.ts b/app/auth/callback/route.ts new file mode 100644 index 0000000..a974dfd --- /dev/null +++ b/app/auth/callback/route.ts @@ -0,0 +1,17 @@ +import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'; +import { cookies } from 'next/headers'; +import { NextRequest, NextResponse } from 'next/server'; + +export async function GET(request: NextRequest) { + const requestUrl = new URL(request.url); + const code = requestUrl.searchParams.get('code'); + + if (code) { + const cookieStore = cookies(); + const supabase = createRouteHandlerClient({ cookies: () => cookieStore }); + await supabase.auth.exchangeCodeForSession(code); + } + + // URL to redirect to after sign in process completes + return NextResponse.redirect(new URL('/analytics', request.url)); +} \ No newline at end of file diff --git a/app/login/page.tsx b/app/login/page.tsx index 6403930..8becb03 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -21,7 +21,7 @@ function MessageHandler({ setMessage }: { setMessage: (message: { type: string, export default function LoginPage() { const router = useRouter(); - const { signIn, user } = useAuth(); + const { signIn, signInWithGoogle, user } = useAuth(); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); @@ -67,6 +67,28 @@ export default function LoginPage() { } }; + const handleGoogleSignIn = async () => { + try { + setIsLoading(true); + setMessage({ type: '', content: '' }); + + const { error } = await signInWithGoogle(); + + if (error) { + throw new Error(error.message); + } + + // Google OAuth will handle the redirect + } catch (error) { + console.error('Google login error:', error); + setMessage({ + type: 'error', + content: error instanceof Error ? error.message : 'Failed to sign in with Google' + }); + setIsLoading(false); + } + }; + return (
{/* Wrap the component using useSearchParams in Suspense */} @@ -101,7 +123,31 @@ export default function LoginPage() {
)} -
+ {/* Google Sign In Button */} + + +
+
+ or +
+
+ +
diff --git a/app/register/page.tsx b/app/register/page.tsx index 42060d3..1839b3f 100644 --- a/app/register/page.tsx +++ b/app/register/page.tsx @@ -12,44 +12,44 @@ export default function RegisterPage() { const [isLoading, setIsLoading] = useState(false); const { signUp, signInWithGoogle } = useAuth(); - // 处理注册表单提交 + // Handle registration form submission const handleSubmit = async (e: FormEvent) => { e.preventDefault(); setError(null); - // 验证密码 + // Validate passwords if (password !== confirmPassword) { - setError('两次输入的密码不一致'); + setError('Passwords do not match'); return; } - // 密码强度验证 + // Password strength validation if (password.length < 6) { - setError('密码长度至少为6个字符'); + setError('Password must be at least 6 characters'); return; } setIsLoading(true); try { await signUp(email, password); - // 注册成功后会跳转到登录页面,提示用户验证邮箱 + // After successful registration, redirect to login page with email verification prompt } catch (error) { console.error('Registration error:', error); - setError('注册失败,请稍后再试或使用其他邮箱'); + setError('Registration failed. Please try again later or use a different email'); } finally { setIsLoading(false); } }; - // 处理Google注册/登录 + // Handle Google registration/login const handleGoogleSignIn = async () => { setError(null); try { await signInWithGoogle(); - // 登录流程会重定向到Google,然后回到应用 + // Login flow will redirect to Google and then back to the application } catch (error) { console.error('Google sign in error:', error); - setError('Google登录失败,请稍后再试'); + setError('Google login failed. Please try again later'); } }; @@ -57,13 +57,13 @@ export default function RegisterPage() {
-

注册

+

Register

- 创建您的帐户以访问分析仪表板 + Create your account to access the analytics dashboard

- {/* 错误提示 */} + {/* Error message */} {error && (
{error} @@ -74,7 +74,7 @@ export default function RegisterPage() {
- {isLoading ? '注册中...' : '注册'} + {isLoading ? 'Registering...' : 'Register'}
@@ -137,7 +137,7 @@ export default function RegisterPage() {
- + or
@@ -173,19 +173,19 @@ export default function RegisterPage() { /> - 使用Google账号注册 + Sign up with Google

- 已有账号?{' '} + Already have an account?{' '} - 登录 + Log in

diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 0000000..81f0ee8 --- /dev/null +++ b/middleware.ts @@ -0,0 +1,22 @@ +import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'; +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; + +export async function middleware(req: NextRequest) { + const res = NextResponse.next(); + + // Create a Supabase client configured to use cookies + const supabase = createMiddlewareClient({ req, res }); + + // Refresh session if expired - required for Server Components + await supabase.auth.getSession(); + + return res; +} + +// Specify the paths where this middleware should run +export const config = { + matcher: [ + '/((?!_next/static|_next/image|favicon.ico).*)', + ], +}; \ No newline at end of file