refactor: Streamline Discourse OAuth authentication and authorization flow

This commit is contained in:
wood chen 2025-02-23 05:46:13 +08:00
parent ee0efb325e
commit 64dc17005f
3 changed files with 13 additions and 123 deletions

View File

@ -1,10 +1,11 @@
import { Suspense } from "react";
import { redirect } from "next/navigation";
import { discourseCallbackVerify } from "@/lib/discourse/verify";
import { findAuthorization } from "@/lib/dto/authorization";
import { getClientByClientId } from "@/lib/dto/client";
import { getAuthorizeUrl } from "@/lib/oauth/authorize-url";
import { CallbackHandler } from "@/components/auth/callback-handler";
import { AuthorizationCard } from "@/components/auth/authorization-card";
export interface DiscourseCallbackParams extends Record<string, string> {
sig: string;
@ -26,7 +27,7 @@ export default async function DiscourseCallbackPage({
throw new Error("Client Id invalid (code: -1004).");
}
// verify discourse callback
// verify discourse callback and create/update user
const user = await discourseCallbackVerify(
searchParams.sso,
searchParams.sig,
@ -34,23 +35,19 @@ export default async function DiscourseCallbackPage({
// check authorization
const authorization = await findAuthorization(user.id, client.id);
let redirectUrl: string | undefined;
if (authorization) {
redirectUrl = await getAuthorizeUrl(oauthParams);
// 如果已经授权,直接重定向到应用
const redirectUrl = await getAuthorizeUrl(oauthParams);
redirect(redirectUrl);
}
// 如果未授权,显示授权页面
return (
<main className="flex min-h-screen flex-col items-center justify-center">
<div className="flex items-center justify-center">
<Suspense>
<CallbackHandler
client={client}
user={user}
oauthParams={searchParams.oauth}
hasAuthorization={!!authorization}
redirectUrl={redirectUrl}
/>
<AuthorizationCard client={client} oauthParams={searchParams.oauth} />
</Suspense>
</div>
</main>

View File

@ -9,47 +9,16 @@ export default {
providers: [
Credentials({
credentials: {
id: { type: "text" },
username: { type: "text" },
email: { type: "text" },
name: { type: "text" },
avatarUrl: { type: "text" },
role: { type: "text" },
moderator: { type: "text" },
groups: { type: "text" },
sso: { type: "text" },
sig: { type: "text" },
},
async authorize(credentials) {
if (!credentials) return null;
if (!credentials?.sso || !credentials?.sig) return null;
// 如果是 SSO 登录
if (credentials.sso && credentials.sig) {
return await discourseCallbackVerify(
credentials.sso as string,
credentials.sig as string,
);
}
// 如果是直接传入用户数据
if (credentials.id && credentials.username && credentials.email) {
return {
id: credentials.id as string,
username: credentials.username as string,
email: credentials.email as string,
name: (credentials.name as string) || null,
avatarUrl: (credentials.avatarUrl as string) || null,
role: ((credentials.role as string) || "USER") as UserRole,
moderator: credentials.moderator === "true",
groups: credentials.groups
? JSON.parse(credentials.groups as string)
: [],
createdAt: new Date(),
updatedAt: new Date(),
};
}
return null;
return await discourseCallbackVerify(
credentials.sso as string,
credentials.sig as string,
);
},
}),
],

View File

@ -1,76 +0,0 @@
"use client";
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { signIn } from "@/auth";
import { Client } from "@prisma/client";
import { AuthorizationCard } from "./authorization-card";
interface CallbackHandlerProps {
client: Client;
user: any;
oauthParams: string;
hasAuthorization: boolean;
redirectUrl?: string;
}
export function CallbackHandler({
client,
user,
oauthParams,
hasAuthorization,
redirectUrl,
}: CallbackHandlerProps) {
const router = useRouter();
const [error, setError] = useState<string | null>(null);
const [isProcessing, setIsProcessing] = useState(true);
useEffect(() => {
async function handleAuth() {
try {
// 设置用户会话
await signIn("credentials", {
id: user.id,
username: user.username,
email: user.email,
name: user.name,
avatarUrl: user.avatarUrl,
role: user.role,
moderator: user.moderator,
groups: JSON.stringify(user.groups),
redirect: false,
});
// 如果已经授权过,直接重定向
if (hasAuthorization && redirectUrl) {
router.push(redirectUrl);
return;
}
setIsProcessing(false);
} catch (error) {
console.error("Auth error:", error);
setError("认证过程中发生错误");
setIsProcessing(false);
}
}
handleAuth();
}, []);
if (error) {
return <div className="text-red-500">{error}</div>;
}
if (isProcessing) {
return (
<div className="flex items-center justify-center">
<div className="h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent"></div>
<span className="ml-2">...</span>
</div>
);
}
return <AuthorizationCard client={client} oauthParams={oauthParams} />;
}