feat: Restore OAuth parameter preservation during authentication flow

This commit is contained in:
wood chen 2025-02-21 19:49:55 +08:00
parent d83f60b0a9
commit 32aaf27b2e
4 changed files with 64 additions and 26 deletions

View File

@ -4,6 +4,7 @@ import { getClientByClientId } from "@/lib/dto/client";
import { getCurrentUser } from "@/lib/session"; import { getCurrentUser } from "@/lib/session";
import { Authorizing } from "@/components/auth/authorizing"; import { Authorizing } from "@/components/auth/authorizing";
import { ErrorCard } from "@/components/auth/error-card"; import { ErrorCard } from "@/components/auth/error-card";
import { SaveOAuthParams } from "@/components/auth/save-oauth-params";
export interface AuthorizeParams { export interface AuthorizeParams {
scope?: string; scope?: string;
@ -21,7 +22,9 @@ export default async function OAuthAuthorization({
// 检查用户是否已登录 // 检查用户是否已登录
const user = await getCurrentUser(); const user = await getCurrentUser();
if (!user?.id) { if (!user?.id) {
redirect("/sign-in"); return (
<SaveOAuthParams searchParams={searchParams} redirectTo="/sign-in" />
);
} }
// 验证必要的参数 // 验证必要的参数

View File

@ -0,0 +1,38 @@
"use client";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
export default function AuthorizePage() {
const router = useRouter();
useEffect(() => {
// 检查是否有待处理的 OAuth 请求
const pendingOAuth = localStorage.getItem("oauth_pending");
if (pendingOAuth) {
try {
const params = JSON.parse(pendingOAuth);
// 清除存储的 OAuth 参数
localStorage.removeItem("oauth_pending");
// 重定向到 OAuth 授权页面
const searchParams = new URLSearchParams(params);
router.push(`/oauth/authorize?${searchParams.toString()}`);
} catch (error) {
console.error("Failed to process OAuth params:", error);
router.push("/dashboard");
}
} else {
// 如果没有待处理的 OAuth 请求,直接进入仪表板
router.push("/dashboard");
}
}, [router]);
return (
<div className="flex min-h-screen items-center justify-center">
<div className="text-center">
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent"></div>
<p className="text-sm text-muted-foreground">...</p>
</div>
</div>
);
}

View File

@ -0,0 +1,21 @@
"use client";
import { useEffect } from "react";
import { redirect } from "next/navigation";
import { AuthorizeParams } from "@/app/(oauth)/oauth/authorize/page";
export function SaveOAuthParams({
searchParams,
redirectTo,
}: {
searchParams: AuthorizeParams;
redirectTo: string;
}) {
useEffect(() => {
localStorage.setItem("oauth_pending", JSON.stringify(searchParams));
window.location.href = redirectTo;
}, [searchParams, redirectTo]);
return null;
}

View File

@ -1,7 +1,7 @@
"use client"; "use client";
import * as React from "react"; import * as React from "react";
import { useRouter, useSearchParams } from "next/navigation"; import { useRouter } from "next/navigation";
import { Loader2, MessageCircleCode } from "lucide-react"; import { Loader2, MessageCircleCode } from "lucide-react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@ -19,39 +19,15 @@ export function UserAuthForm({
const [isLoading, setIsLoading] = React.useState<boolean>(false); const [isLoading, setIsLoading] = React.useState<boolean>(false);
const router = useRouter(); const router = useRouter();
const { toast } = useToast(); const { toast } = useToast();
const searchParams = useSearchParams();
const signIn = () => { const signIn = () => {
React.startTransition(async () => { React.startTransition(async () => {
try { try {
const body: Record<string, any> = {};
// 如果存在 OAuth 参数,添加到请求体
if (searchParams?.has("client_id")) {
const oauthParams = new URLSearchParams();
[
"client_id",
"redirect_uri",
"response_type",
"state",
"scope",
].forEach((param) => {
const value = searchParams.get(param);
if (value) {
oauthParams.append(param, value);
}
});
if (oauthParams.toString()) {
body.oauth_params = oauthParams.toString();
}
}
const response = await fetch("/api/auth/q58", { const response = await fetch("/api/auth/q58", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify(body),
}); });
if (!response.ok || response.status !== 200) { if (!response.ok || response.status !== 200) {