mirror of
https://github.com/woodchen-ink/Q58Connect.git
synced 2025-07-18 14:01: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 { Suspense } from "react";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
import { discourseCallbackVerify } from "@/lib/discourse/verify";
|
import { discourseCallbackVerify } from "@/lib/discourse/verify";
|
||||||
import { findAuthorization } from "@/lib/dto/authorization";
|
import { findAuthorization } from "@/lib/dto/authorization";
|
||||||
import { getClientByClientId } from "@/lib/dto/client";
|
import { getClientByClientId } from "@/lib/dto/client";
|
||||||
import { getAuthorizeUrl } from "@/lib/oauth/authorize-url";
|
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> {
|
export interface DiscourseCallbackParams extends Record<string, string> {
|
||||||
sig: string;
|
sig: string;
|
||||||
@ -26,7 +27,7 @@ export default async function DiscourseCallbackPage({
|
|||||||
throw new Error("Client Id invalid (code: -1004).");
|
throw new Error("Client Id invalid (code: -1004).");
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify discourse callback
|
// verify discourse callback and create/update user
|
||||||
const user = await discourseCallbackVerify(
|
const user = await discourseCallbackVerify(
|
||||||
searchParams.sso,
|
searchParams.sso,
|
||||||
searchParams.sig,
|
searchParams.sig,
|
||||||
@ -34,23 +35,19 @@ export default async function DiscourseCallbackPage({
|
|||||||
|
|
||||||
// check authorization
|
// check authorization
|
||||||
const authorization = await findAuthorization(user.id, client.id);
|
const authorization = await findAuthorization(user.id, client.id);
|
||||||
let redirectUrl: string | undefined;
|
|
||||||
|
|
||||||
if (authorization) {
|
if (authorization) {
|
||||||
redirectUrl = await getAuthorizeUrl(oauthParams);
|
// 如果已经授权,直接重定向到应用
|
||||||
|
const redirectUrl = await getAuthorizeUrl(oauthParams);
|
||||||
|
redirect(redirectUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果未授权,显示授权页面
|
||||||
return (
|
return (
|
||||||
<main className="flex min-h-screen flex-col items-center justify-center">
|
<main className="flex min-h-screen flex-col items-center justify-center">
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<CallbackHandler
|
<AuthorizationCard client={client} oauthParams={searchParams.oauth} />
|
||||||
client={client}
|
|
||||||
user={user}
|
|
||||||
oauthParams={searchParams.oauth}
|
|
||||||
hasAuthorization={!!authorization}
|
|
||||||
redirectUrl={redirectUrl}
|
|
||||||
/>
|
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
@ -9,47 +9,16 @@ export default {
|
|||||||
providers: [
|
providers: [
|
||||||
Credentials({
|
Credentials({
|
||||||
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" },
|
sso: { type: "text" },
|
||||||
sig: { type: "text" },
|
sig: { type: "text" },
|
||||||
},
|
},
|
||||||
async authorize(credentials) {
|
async authorize(credentials) {
|
||||||
if (!credentials) return null;
|
if (!credentials?.sso || !credentials?.sig) return null;
|
||||||
|
|
||||||
// 如果是 SSO 登录
|
return await discourseCallbackVerify(
|
||||||
if (credentials.sso && credentials.sig) {
|
credentials.sso as string,
|
||||||
return await discourseCallbackVerify(
|
credentials.sig as string,
|
||||||
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;
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -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