admin管理员组文章数量:1389778
About my architecture: I have a backend with Express.js and a frontend with Next.js.
I have massive problems with my CSRF cookie. I create it in Express.js and send it to Next.js (Client). Here it is cached in a provider so that it is available for other components. However, although I set it, it is not passed as a cookie for requests.
My backend (Express.js) looks like this: server.ts
//...
import csrfProtection from "./middlewares/csrf";
//...
const corsOptions = {
origin: [clientHost],
credentials: true,
allowedHeaders: ["Content-Type", "Authorization"],
methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
}
app.use(cors(corsOptions));
app.use(csrfProtection);
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'"],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"]
}
}));
app.use(helmet.crossOriginEmbedderPolicy({ policy: 'require-corp' }));
app.use(helmet.crossOriginOpenerPolicy({ policy: 'same-origin' }));
app.use(helmet.crossOriginResourcePolicy({ policy: 'same-origin' }));
app.use(helmet.referrerPolicy({ policy: 'no-referrer' }));
app.use(helmet.xssFilter());
app.use(helmet.hsts({ maxAge: 5184000, includeSubDomains: true, preload: true })); // HSTS
app.use(helmet.hidePoweredBy());
app.use(helmet.frameguard({ action: 'deny' }));
app.use(helmet.dnsPrefetchControl({ allow: false })); // deactivate DNS prefetching
app.use(helmet.noSniff());
app.use(helmet.originAgentCluster());
middlewares/csrf.ts
import csurf from "csurf";
import { Request, Response, NextFunction } from "express";
// initialize
const csrfProtection = csurf({
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 3600 * 1000,
},
ignoreMethods: ["GET", "HEAD", "OPTIONS"]
});
export function getCsrfToken(req: Request, res: Response, next: NextFunction) {
res.cookie("csrfToken", req.csrfToken(), {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 3600000,
})
res.json({ csrfToken: req.csrfToken() });
}
export default csrfProtection;
api/router.ts [THIS PART WORKS FINE - ONLY FOR COMPLETENESS]
import { getCsrfToken } from "../middlewares/csrf";
const router = express.Router();
router.get("/csrf-token", getCsrfToken);
export default router;
api/auth/router.ts [THIS PART WORKS FINE - ONLY FOR COMPLETENESS]
import csrfProtection from "../../middlewares/csrf";
const router = express.Router();
router.post("/login",csrfProtection, login);
My frontend (Next.js) looks like this: providers/csrfprovider.ts
"use client";
import { createContext, useContext, useState, useEffect } from "react";
export const CsrfContext = createContext<string | null>(null);
export const CsrfProvider = ({ children }: { children: React.ReactNode }) => {
const [csrfToken, setCsrfToken] = useState<string | null>(null);
useEffect(() => {
const fetchCsrfToken = async () => {
try {
const response = await fetch(`/api/csrf-token`, {
credentials: "include", // Send cookies with the request
});
const data = await response.json();
setCsrfToken(data.csrfToken);
} catch (err) {
console.error("not able to load CSRF-Token", err);
}
};
fetchCsrfToken();
}, []);
return (
<CsrfContext.Provider value={csrfToken}>{children}</CsrfContext.Provider>
)
}
export const useCsrfToken = () => useContext(CsrfContext);
app/api/csrf-token/route.ts
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
export async function GET() {
try {
const response = await fetch(`${process.env.SERVER_HOST}api/csrf-token`, {
credentials: "include",
});
if (!response.ok) {
throw new Error("Failed to fetch CSRF token");
}
const data = await response.json();
const csrfToken = data.csrfToken;
const cookieStore = cookies();
(await cookieStore).set('csrfToken', csrfToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 3600000,
path: "/",
});
return NextResponse.json({ csrfToken: csrfToken });
} catch (error) {
console.error("Error fetching CSRF token:", error);
return NextResponse.json({ error: "Failed to load CSRF token" }, { status: 500 });
}
}
app/api/login/route.ts
import { NextResponse } from "next/server";
export async function POST(request: Request) {
try {
const { username, password, csrfToken } = await request.json();
if (!username || !password || !csrfToken) {
return NextResponse.json(
{ error: "Missing required fields" },
{ status: 400 }
);
}
const loginPayload = {
username,
password,
};
const response = await fetch(`${process.env.SERVER_HOST}api/auth/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": csrfToken,
},
credentials: "include",
body: JSON.stringify(loginPayload),
});
// console.log(response)
if (!response.ok) {
throw new Error("Failed to login");
}
const data = await response.json();
return NextResponse.json(data);
} catch (error) {
console.error("Error during login request:", error);
return NextResponse.json(
{ error: "Failed to process login" },
{ status: 500 }
);
}
}
I log the request in the backend. A request always looks like this:
[...]
Cookies: {},
Query Params: {},
Headers: {[...]"x-csrf-token":"Q6iGi2sq-kP1w3QDCo6jpw8DFkIV-OF-oDy4"},
Why is the sent cookie not returned? why is no cookie transferred even though it is set? I can see in the Development console that the cookie is there.
The CSRF protection logically rejects all requests because the cookie (which is not present) does not match the token in the header.
Can someone help me to better understand and solve the whole thing and the problem?
本文标签:
版权声明:本文标题:reactjs - How can I set a CSRF cookie as a server cookie in NextJS to send it to ExpressJS backend? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744618440a2615874.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论