feat: Improve SSO authentication with enhanced error handling and dynamic return URL

This commit is contained in:
wood chen 2025-02-21 20:51:44 +08:00
parent 1edfc035e2
commit 7188c46cd7
3 changed files with 71 additions and 35 deletions

View File

@ -113,6 +113,14 @@ headers: {
"groups": ["group1", "group2"]
}
## 添加新功能的准则
1. 尽量简单, 尽量少修改代码
2. 不能影响已有的功能
3. 尽量不新增文件
4. 尽量使用已有的组件和函数
5. 前端页面要尽量使用shadcn的组件
## 许可证
本项目采用 MIT 许可证。详情请见 [LICENSE](LICENSE) 文件。

View File

@ -9,14 +9,36 @@ const hostUrl = process.env.NEXT_PUBLIC_HOST_URL as string;
const discourseHost = process.env.DISCOURSE_HOST as string;
const clientSecret = process.env.DISCOURSE_SECRET as string;
export async function POST(_req: Request) {
const nonce = WordArray.random(16).toString();
const return_url = `${hostUrl}/authorize`;
const sso = btoa(`nonce=${nonce}&return_sso_url=${return_url}`);
const sig = hmacSHA256(sso, clientSecret).toString(Hex);
export async function POST(req: Request) {
try {
const nonce = WordArray.random(16).toString();
let return_url = `${hostUrl}/authorize`;
cookies().set(AUTH_NONCE, nonce, { maxAge: 60 * 10 });
return Response.json({
sso_url: `${discourseHost}/session/sso_provider?sso=${sso}&sig=${sig}`,
});
// 尝试从请求中获取 return_url
try {
const body = await req.json();
if (body.return_url) {
return_url = body.return_url;
}
} catch (error) {
console.error("Failed to parse request body:", error);
}
const sso = btoa(`nonce=${nonce}&return_sso_url=${return_url}`);
const sig = hmacSHA256(sso, clientSecret).toString(Hex);
cookies().set(AUTH_NONCE, nonce, {
maxAge: 60 * 10,
path: "/",
httpOnly: true,
secure: process.env.NODE_ENV === "production",
});
return Response.json({
sso_url: `${discourseHost}/session/sso_provider?sso=${sso}&sig=${sig}`,
});
} catch (error) {
console.error("SSO 处理错误:", error);
return Response.json({ error: "处理登录请求时发生错误" }, { status: 500 });
}
}

View File

@ -1,7 +1,7 @@
"use client";
import * as React from "react";
import { useRouter } from "next/navigation";
import { useRouter, useSearchParams } from "next/navigation";
import { Loader2, MessageCircleCode } from "lucide-react";
import { cn } from "@/lib/utils";
@ -19,33 +19,39 @@ export function UserAuthForm({
const [isLoading, setIsLoading] = React.useState<boolean>(false);
const router = useRouter();
const { toast } = useToast();
const searchParams = useSearchParams();
const signIn = () => {
React.startTransition(async () => {
try {
const response = await fetch("/api/auth/q58", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok || response.status !== 200) {
throw new Error(response.statusText || "登录请求失败");
}
const data: DiscourseData = await response.json();
router.push(data.sso_url);
} catch (error) {
setIsLoading(false);
toast({
variant: "destructive",
title: "内部服务异常",
description: error instanceof Error ? error.message : "登录请求失败",
});
async function signIn() {
try {
const body: Record<string, any> = {};
const callbackUrl = searchParams?.get("callbackUrl");
if (callbackUrl) {
body.return_url = callbackUrl;
}
});
};
const response = await fetch("/api/auth/q58", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!response.ok) {
throw new Error("登录请求失败");
}
const data = await response.json();
window.location.href = data.sso_url;
} catch (error) {
console.error("登录错误:", error);
toast({
title: "错误",
description: "登录过程中发生错误,请稍后重试",
variant: "destructive",
});
}
}
return (
<div className={cn("grid gap-3", className)} {...props}>