fix proxy csrf handling for suggestion requests
This commit is contained in:
@@ -1,8 +1,22 @@
|
|||||||
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
|
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
|
||||||
const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
|
||||||
|
|
||||||
export const fetchCSRFToken = async () => {
|
type FetchCSRFTokenOptions = {
|
||||||
const csrfTokenFetch = await fetch(`${serverEndpoint}/csrf/`);
|
fetch?: typeof fetch;
|
||||||
|
sessionId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchCSRFToken = async (options: FetchCSRFTokenOptions = {}) => {
|
||||||
|
const fetchFn = options.fetch ?? fetch;
|
||||||
|
const headers = new Headers();
|
||||||
|
|
||||||
|
if (options.sessionId) {
|
||||||
|
headers.set('Cookie', `sessionid=${options.sessionId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const csrfTokenFetch = await fetchFn(`${serverEndpoint}/csrf/`, {
|
||||||
|
headers
|
||||||
|
});
|
||||||
if (csrfTokenFetch.ok) {
|
if (csrfTokenFetch.ok) {
|
||||||
const csrfToken = await csrfTokenFetch.json();
|
const csrfToken = await csrfTokenFetch.json();
|
||||||
return csrfToken.csrfToken;
|
return csrfToken.csrfToken;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ async function handleRequest(
|
|||||||
const path = params.path;
|
const path = params.path;
|
||||||
let targetUrl = `${endpoint}/api/${path}`;
|
let targetUrl = `${endpoint}/api/${path}`;
|
||||||
|
|
||||||
// Ensure the path ends with a trailing slash
|
// Preserve global proxy contract for mutating requests.
|
||||||
if (requreTrailingSlash && !targetUrl.endsWith('/')) {
|
if (requreTrailingSlash && !targetUrl.endsWith('/')) {
|
||||||
targetUrl += '/';
|
targetUrl += '/';
|
||||||
}
|
}
|
||||||
@@ -53,27 +53,38 @@ async function handleRequest(
|
|||||||
|
|
||||||
const headers = new Headers(request.headers);
|
const headers = new Headers(request.headers);
|
||||||
|
|
||||||
// Delete existing csrf cookie by setting an expired date
|
const isMutatingRequest = !['GET', 'HEAD', 'OPTIONS'].includes(request.method.toUpperCase());
|
||||||
cookies.delete('csrftoken', { path: '/' });
|
const sessionId = cookies.get('sessionid');
|
||||||
|
let csrfToken: string | null = null;
|
||||||
|
|
||||||
// Generate a new csrf token (using your existing fetchCSRFToken function)
|
if (isMutatingRequest) {
|
||||||
const csrfToken = await fetchCSRFToken();
|
csrfToken = await fetchCSRFToken({ fetch, sessionId });
|
||||||
if (!csrfToken) {
|
if (!csrfToken) {
|
||||||
return json({ error: 'CSRF token is missing or invalid' }, { status: 400 });
|
return json({ error: 'CSRF token is missing or invalid' }, { status: 400 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the new csrf token in both headers and cookies
|
const cookieParts: string[] = [];
|
||||||
const sessionId = cookies.get('sessionid');
|
if (csrfToken) cookieParts.push(`csrftoken=${csrfToken}`);
|
||||||
const cookieHeader = `csrftoken=${csrfToken}` + (sessionId ? `; sessionid=${sessionId}` : '');
|
if (sessionId) cookieParts.push(`sessionid=${sessionId}`);
|
||||||
|
const cookieHeader = cookieParts.join('; ');
|
||||||
|
|
||||||
|
const forwardedHeaders = {
|
||||||
|
...Object.fromEntries(headers)
|
||||||
|
} as Record<string, string>;
|
||||||
|
|
||||||
|
if (csrfToken) {
|
||||||
|
forwardedHeaders['X-CSRFToken'] = csrfToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cookieHeader) {
|
||||||
|
forwardedHeaders['Cookie'] = cookieHeader;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(targetUrl, {
|
const response = await fetch(targetUrl, {
|
||||||
method: request.method,
|
method: request.method,
|
||||||
headers: {
|
headers: forwardedHeaders,
|
||||||
...Object.fromEntries(headers),
|
|
||||||
'X-CSRFToken': csrfToken,
|
|
||||||
Cookie: cookieHeader
|
|
||||||
},
|
|
||||||
body:
|
body:
|
||||||
request.method !== 'GET' && request.method !== 'HEAD' ? await request.text() : undefined,
|
request.method !== 'GET' && request.method !== 'HEAD' ? await request.text() : undefined,
|
||||||
credentials: 'include' // This line ensures cookies are sent with the request
|
credentials: 'include' // This line ensures cookies are sent with the request
|
||||||
|
|||||||
Reference in New Issue
Block a user