mirror of
https://github.com/woodchen-ink/Q58Connect.git
synced 2025-07-17 21:41:55 +08:00
feat: Replace localStorage with secure cookies for OAuth state management
This commit is contained in:
parent
a40234e6c8
commit
67b434e692
@ -29,10 +29,12 @@
|
||||
"@radix-ui/react-switch": "^1.1.0",
|
||||
"@radix-ui/react-tabs": "^1.1.3",
|
||||
"@radix-ui/react-toast": "^1.2.1",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"framer-motion": "^11.5.4",
|
||||
"js-cookie": "^3.0.5",
|
||||
"lucide-react": "^0.437.0",
|
||||
"next": "14.2.7",
|
||||
"next-auth": "5.0.0-beta.20",
|
||||
|
15
pnpm-lock.yaml
generated
15
pnpm-lock.yaml
generated
@ -44,6 +44,9 @@ dependencies:
|
||||
'@radix-ui/react-toast':
|
||||
specifier: ^1.2.1
|
||||
version: 1.2.6(@types/react-dom@18.3.5)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@types/js-cookie':
|
||||
specifier: ^3.0.6
|
||||
version: 3.0.6
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.0
|
||||
version: 0.7.1
|
||||
@ -56,6 +59,9 @@ dependencies:
|
||||
framer-motion:
|
||||
specifier: ^11.5.4
|
||||
version: 11.18.2(react-dom@18.3.1)(react@18.3.1)
|
||||
js-cookie:
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5
|
||||
lucide-react:
|
||||
specifier: ^0.437.0
|
||||
version: 0.437.0(react@18.3.1)
|
||||
@ -1295,6 +1301,10 @@ packages:
|
||||
resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==}
|
||||
dev: true
|
||||
|
||||
/@types/js-cookie@3.0.6:
|
||||
resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==}
|
||||
dev: false
|
||||
|
||||
/@types/json5@0.0.29:
|
||||
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
||||
dev: true
|
||||
@ -2962,6 +2972,11 @@ packages:
|
||||
resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==}
|
||||
dev: false
|
||||
|
||||
/js-cookie@3.0.5:
|
||||
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
|
||||
engines: {node: '>=14'}
|
||||
dev: false
|
||||
|
||||
/js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
import { useEffect } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Cookies from "js-cookie";
|
||||
import { MessageCircleCode } from "lucide-react";
|
||||
|
||||
import { UserAuthorize } from "@/components/auth/user-authorize";
|
||||
@ -15,18 +16,19 @@ export default function AuthPage({ searchParams }: Props) {
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
// 检查是否有待处理的 OAuth 请求
|
||||
const pendingOAuth = localStorage.getItem("oauth_pending");
|
||||
if (pendingOAuth) {
|
||||
// 检查是否有 OAuth 状态参数
|
||||
const oauthState = Cookies.get("oauth_state");
|
||||
if (oauthState) {
|
||||
try {
|
||||
const params = JSON.parse(pendingOAuth);
|
||||
// 清除存储的 OAuth 参数
|
||||
localStorage.removeItem("oauth_pending");
|
||||
// 重定向到 OAuth 授权页面
|
||||
// 解码 OAuth 参数
|
||||
const params = JSON.parse(atob(oauthState));
|
||||
// 删除 cookie
|
||||
Cookies.remove("oauth_state", { path: "/" });
|
||||
// 构建重定向 URL
|
||||
const searchParams = new URLSearchParams(params);
|
||||
router.push(`/oauth/authorize?${searchParams.toString()}`);
|
||||
} catch (error) {
|
||||
console.error("Failed to process OAuth params:", error);
|
||||
console.error("Failed to process OAuth state:", error);
|
||||
}
|
||||
}
|
||||
}, [router]);
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { getClientByClientId } from "@/lib/dto/client";
|
||||
import { getCurrentUser } from "@/lib/session";
|
||||
import { Authorizing } from "@/components/auth/authorizing";
|
||||
import { ErrorCard } from "@/components/auth/error-card";
|
||||
import { SaveOAuthParams } from "@/components/auth/save-oauth-params";
|
||||
|
||||
export interface AuthorizeParams {
|
||||
scope?: string;
|
||||
@ -22,9 +22,15 @@ export default async function OAuthAuthorization({
|
||||
// 检查用户是否已登录
|
||||
const user = await getCurrentUser();
|
||||
if (!user?.id) {
|
||||
return (
|
||||
<SaveOAuthParams searchParams={searchParams} redirectTo="/sign-in" />
|
||||
);
|
||||
// 将 OAuth 参数保存到 cookie
|
||||
const encodedParams = btoa(JSON.stringify(searchParams));
|
||||
cookies().set("oauth_state", encodedParams, {
|
||||
maxAge: 60 * 10, // 10分钟过期
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
});
|
||||
redirect("/sign-in");
|
||||
}
|
||||
|
||||
// 验证必要的参数
|
||||
|
@ -1,21 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { AuthorizeParams } from "@/app/(oauth)/oauth/authorize/page";
|
||||
|
||||
export function SaveOAuthParams({
|
||||
searchParams,
|
||||
redirectTo,
|
||||
}: {
|
||||
searchParams: AuthorizeParams;
|
||||
redirectTo: string;
|
||||
}) {
|
||||
useEffect(() => {
|
||||
localStorage.setItem("oauth_pending", JSON.stringify(searchParams));
|
||||
window.location.href = redirectTo;
|
||||
}, [searchParams, redirectTo]);
|
||||
|
||||
return null;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user