refactor: 优化 SSO 和 OAuth 重定向流程,改进参数处理和回调机制

This commit is contained in:
wood chen 2025-02-21 17:43:21 +08:00
parent f9c912e8c5
commit 1c0aa7e65f
3 changed files with 44 additions and 16 deletions

View File

@ -6,6 +6,23 @@
> shadcn安装组件的命令,举例: npx shadcn@latest add button > shadcn安装组件的命令,举例: npx shadcn@latest add button
本项目是一个中间项目, 用于允许用户通过oauth2.0认证的方式接入本项目, 但是实际用户信息是本项目通过SSO连接到Discourse论坛获取的.
原始项目地址: https://github.com/Tuluobo/discourse-connect
本项目主要是进行了:
1. 前端页面内容补充
2. 管理员的相关管理页面和统计页面
3. 本系统用户可以查看自己应用的统计信息
4. Navbar的导航菜单添加
5. 本系统用户可以限制自己应用的允许授权使用者
6. 首页说明的优化
整体流程应该是这样的:
用户登录接入应用 - 接入应用通过oauth2.0向本系统发起授权请求 - 本系统向Discourse论坛发起SSO请求 - 用户在Discourse论坛中进行登录 - Discourse论坛重定向到本系统, 并附带sso和sig参数 - 本系统通过sso和sig参数向Discourse论坛发起获取用户信息请求 - 本系统通过oauth2.0向接入应用发起回调请求, 并附带用户信息 - 接入应用通过oauth2.0向本系统发起获取用户信息请求 - 本系统通过oauth2.0向Discourse论坛发起获取用户信息请求 - 本系统返回用户信息给接入应用
## 项目概述 ## 项目概述
本项目提供了一个 OAuth 认证系统,允许其他应用程序使用 Discourse 论坛的用户账号进行身份验证。这样可以让用户使用他们已有的 Discourse 账号登录到您的应用程序,无需创建新的账号。 本项目提供了一个 OAuth 认证系统,允许其他应用程序使用 Discourse 论坛的用户账号进行身份验证。这样可以让用户使用他们已有的 Discourse 账号登录到您的应用程序,无需创建新的账号。

View File

@ -1,10 +1,11 @@
"use server"; "use server";
import { cookies } from "next/headers";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { signIn as nextSignIn } from "@/auth"; import { signIn as nextSignIn } from "@/auth";
export async function signIn(data: Record<string, any>) { export async function signIn(data: Record<string, any>) {
const { sso, sig, returnTo } = data; const { sso, sig } = data;
// 先进行 SSO 登录 // 先进行 SSO 登录
await nextSignIn("credentials", { sso, sig }); await nextSignIn("credentials", { sso, sig });
@ -13,17 +14,17 @@ export async function signIn(data: Record<string, any>) {
const params = new URLSearchParams(atob(sso)); const params = new URLSearchParams(atob(sso));
const returnSsoUrl = params.get("return_sso_url"); const returnSsoUrl = params.get("return_sso_url");
if (!returnSsoUrl) { if (returnSsoUrl) {
// 如果没有 return_sso_url检查是否有 OAuth 参数 // 解析 return_sso_url 中的参数
const searchParams = new URLSearchParams(returnTo); const returnUrl = new URL(returnSsoUrl);
if (searchParams.has("client_id")) { const hasOAuthParams = returnUrl.searchParams.has("client_id");
// 如果是 OAuth 流程,重定向到授权页面
redirect(`/oauth/authorize${returnTo}`); if (hasOAuthParams) {
// 如果 URL 中包含 OAuth 参数,直接重定向(此时应该是 /authorize 页面)
redirect(returnSsoUrl);
} }
// 如果都没有,重定向到仪表板
redirect("/dashboard");
} }
// 如果有 return_sso_url重定向回 SSO 提供者 // 如果没有有效的重定向 URL默认到仪表板
redirect(returnSsoUrl); redirect("/dashboard");
} }

View File

@ -15,12 +15,22 @@ export async function POST(req: Request) {
const url = new URL(referer); const url = new URL(referer);
const searchParams = url.searchParams.toString(); const searchParams = url.searchParams.toString();
// 如果是从OAuth授权页面来的保留OAuth参数 // 保存原始的 OAuth 参数到 cookie用于后续重定向
const return_url = searchParams if (searchParams) {
? `${hostUrl}/q58/callback?oauth=${btoa(searchParams)}` cookies().set("oauth_params", searchParams, {
: `${hostUrl}/authorize`; maxAge: 60 * 10, // 10分钟过期
path: "/",
});
}
const sso = btoa(`nonce=${nonce}&return_sso_url=${return_url}`); // 设置 SSO 回调地址,将 OAuth 参数编码后传递
const return_url = searchParams
? `${hostUrl}/authorize?${searchParams}` // 直接将 OAuth 参数附加到回调 URL
: `${hostUrl}/dashboard`; // 如果没有 OAuth 参数,回到仪表板
const sso = btoa(
`nonce=${nonce}&return_sso_url=${encodeURIComponent(return_url)}`,
);
const sig = hmacSHA256(sso, clientSecret).toString(Hex); const sig = hmacSHA256(sso, clientSecret).toString(Hex);
cookies().set(AUTH_NONCE, nonce, { maxAge: 60 * 10 }); cookies().set(AUTH_NONCE, nonce, { maxAge: 60 * 10 });