preserve auth headers in json proxies

This commit is contained in:
2569718930@qq.com
2026-06-11 14:04:17 +08:00
parent 5b65da6049
commit d314aa04d7
8 changed files with 59 additions and 16 deletions
@@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
import {
applyAuthResponseCookies,
buildBackendRequestHeaders,
buildJsonBackendRequestHeaders,
} from "@/lib/backend-auth";
import {
buildProxyExceptionResponse,
@@ -22,10 +23,7 @@ export async function POST(req: NextRequest) {
const body = await req.text();
const res = await fetch(`${API_BASE}/api/auth/referral/apply`, {
method: "POST",
headers: {
...auth.headers,
"Content-Type": "application/json",
},
headers: buildJsonBackendRequestHeaders(auth.headers),
body,
cache: "no-store",
});
+2 -2
View File
@@ -1,5 +1,5 @@
import { NextRequest, NextResponse } from "next/server";
import { applyAuthResponseCookies, buildBackendRequestHeaders } from "@/lib/backend-auth";
import { applyAuthResponseCookies, buildBackendRequestHeaders, buildJsonBackendRequestHeaders } from "@/lib/backend-auth";
import { buildProxyExceptionResponse } from "@/lib/api-proxy";
import { requireOpsProxyAuth } from "@/lib/ops-proxy-auth";
@@ -28,7 +28,7 @@ export async function PUT(req: NextRequest) {
if (authError) return authError;
const body = await req.text();
const res = await fetch(BACKEND, { method: "PUT", headers: { ...auth.headers, "Content-Type": "application/json" }, body, cache: "no-store" });
const res = await fetch(BACKEND, { method: "PUT", headers: buildJsonBackendRequestHeaders(auth.headers), body, cache: "no-store" });
const raw = await res.text();
const response = new NextResponse(raw, { status: res.status, headers: { "Content-Type": "application/json", "Cache-Control": "no-store" } });
return applyAuthResponseCookies(response, auth.response);
@@ -1,5 +1,5 @@
import { NextRequest, NextResponse } from "next/server";
import { applyAuthResponseCookies, buildBackendRequestHeaders } from "@/lib/backend-auth";
import { applyAuthResponseCookies, buildBackendRequestHeaders, buildJsonBackendRequestHeaders } from "@/lib/backend-auth";
import { buildProxyExceptionResponse } from "@/lib/api-proxy";
import { requireOpsProxyAuth } from "@/lib/ops-proxy-auth";
@@ -28,7 +28,7 @@ export async function PUT(req: NextRequest) {
if (authError) return authError;
const body = await req.text();
const res = await fetch(BACKEND, { method: "PUT", headers: { ...auth.headers, "Content-Type": "application/json" }, body, cache: "no-store" });
const res = await fetch(BACKEND, { method: "PUT", headers: buildJsonBackendRequestHeaders(auth.headers), body, cache: "no-store" });
const raw = await res.text();
const response = new NextResponse(raw, { status: res.status, headers: { "Content-Type": "application/json", "Cache-Control": "no-store" } });
return applyAuthResponseCookies(response, auth.response);
@@ -1,5 +1,5 @@
import { NextRequest, NextResponse } from "next/server";
import { applyAuthResponseCookies, buildBackendRequestHeaders } from "@/lib/backend-auth";
import { applyAuthResponseCookies, buildBackendRequestHeaders, buildJsonBackendRequestHeaders } from "@/lib/backend-auth";
import { buildProxyExceptionResponse } from "@/lib/api-proxy";
import { requireOpsProxyAuth } from "@/lib/ops-proxy-auth";
@@ -14,9 +14,9 @@ export async function POST(req: NextRequest) {
if (authError) return authError;
const body = await req.text();
const headers: Record<string, string> = { ...auth.headers as Record<string, string>, "Content-Type": "application/json" };
const headers = buildJsonBackendRequestHeaders(auth.headers);
if (ENTITLEMENT_TOKEN) {
headers.Authorization = `Bearer ${ENTITLEMENT_TOKEN}`;
headers.set("Authorization", `Bearer ${ENTITLEMENT_TOKEN}`);
}
const res = await fetch(`${API_BASE}/api/ops/subscriptions/extend`, {
method: "POST", headers, body, cache: "no-store",
@@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
import {
applyAuthResponseCookies,
buildBackendRequestHeaders,
buildJsonBackendRequestHeaders,
} from "@/lib/backend-auth";
import { buildProxyExceptionResponse } from "@/lib/api-proxy";
import { requireOpsProxyAuth } from "@/lib/ops-proxy-auth";
@@ -166,7 +167,7 @@ export async function POST(req: NextRequest) {
const res = await fetch(`${API_BASE}/api/ops/subscriptions/grant`, {
method: "POST",
headers: { ...(auth.headers as Record<string, string>), "Content-Type": "application/json" },
headers: buildJsonBackendRequestHeaders(auth.headers),
body,
cache: "no-store",
});
@@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
import {
applyAuthResponseCookies,
buildBackendRequestHeaders,
buildJsonBackendRequestHeaders,
} from "@/lib/backend-auth";
import { buildProxyExceptionResponse } from "@/lib/api-proxy";
import { requireOpsProxyAuth } from "@/lib/ops-proxy-auth";
@@ -24,10 +25,7 @@ export async function POST(req: NextRequest) {
const body = await req.text();
const res = await fetch(`${API_BASE}/api/ops/users/grant-points`, {
method: "POST",
headers: {
...auth.headers,
"Content-Type": "application/json",
},
headers: buildJsonBackendRequestHeaders(auth.headers),
body,
cache: "no-store",
});
@@ -0,0 +1,40 @@
import fs from "node:fs";
import path from "node:path";
function assert(condition: unknown, message: string): asserts condition {
if (!condition) throw new Error(message);
}
function collectRouteFiles(dir: string): string[] {
return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) return collectRouteFiles(fullPath);
return entry.name === "route.ts" ? [fullPath] : [];
});
}
export function runTests() {
const projectRoot = process.cwd();
const apiRoot = path.join(projectRoot, "app", "api");
const unsafeRoutes = collectRouteFiles(apiRoot)
.filter((routePath) =>
/\.\.\.\s*(?:\(\s*)?auth\.headers/.test(fs.readFileSync(routePath, "utf8")),
)
.map((routePath) => path.relative(projectRoot, routePath));
assert(
unsafeRoutes.length === 0,
`backend auth Headers must not be object-spread because that drops authorization headers: ${unsafeRoutes.join(", ")}`,
);
const helperSource = fs.readFileSync(
path.join(projectRoot, "lib", "backend-auth.ts"),
"utf8",
);
assert(
helperSource.includes("buildJsonBackendRequestHeaders") &&
helperSource.includes("new Headers(headers)") &&
helperSource.includes('result.set("Content-Type", "application/json")'),
"backend auth must expose a safe JSON header builder",
);
}
+6
View File
@@ -125,6 +125,12 @@ export function applyAuthResponseCookies(
return target;
}
export function buildJsonBackendRequestHeaders(headers: HeadersInit) {
const result = new Headers(headers);
result.set("Content-Type", "application/json");
return result;
}
export function requireBackendAuthUser(auth: BackendHeaderBuildResult) {
if (auth.authUserId) return null;
return applyAuthResponseCookies(