mirror of
https://github.com/woodchen-ink/Q58Connect.git
synced 2025-07-18 05:51:55 +08:00
refactor: Streamline Discourse OAuth authentication and authorization flow
This commit is contained in:
parent
ee0efb325e
commit
64dc17005f
@ -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>
|
||||
|
@ -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,
|
||||
);
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
@ -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} />;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user