fix(better-auth): preserve response headers for early before hook returns

This commit is contained in:
Saša Šijak
2026-03-09 11:10:02 +01:00
parent b8b8b1379e
commit 0cbdf5fb2d
2 changed files with 70 additions and 7 deletions

View File

@@ -199,6 +199,15 @@ describe("before hook", async () => {
return { response: true };
},
),
responseHeaders: createAuthEndpoint(
"/response-headers",
{
method: "POST",
},
async (c) => {
return { response: true };
},
),
};
const authContext = init({
@@ -207,6 +216,15 @@ describe("before hook", async () => {
if (c.path === "/json") {
return { before: true };
}
if (c.path === "/response-headers") {
return new Response(JSON.stringify({ before: true }), {
status: 201,
headers: {
"content-type": "application/json",
"x-hook": "before",
},
});
}
return new Response(JSON.stringify({ before: true }));
}),
},
@@ -222,6 +240,39 @@ describe("before hook", async () => {
const response = await authEndpoints.json();
expect(response).toMatchObject({ before: true });
});
it("should not leak request headers into early before responses", async () => {
const response = await authEndpoints.responseHeaders({
asResponse: true,
headers: new Headers({
"content-length": "999",
"x-request": "leak-me-not",
}),
});
expect(response.status).toBe(201);
expect(response.headers.get("x-hook")).toBe("before");
expect(response.headers.get("x-request")).toBeNull();
expect(response.headers.get("content-length")).toBeNull();
await expect(response.json()).resolves.toMatchObject({ before: true });
});
it("should return response headers and status for early before responses", async () => {
const result = await authEndpoints.responseHeaders({
returnHeaders: true,
returnStatus: true,
headers: new Headers({
"content-length": "999",
"x-request": "leak-me-not",
}),
});
expect(result.status).toBe(201);
expect(result.headers.get("x-hook")).toBe("before");
expect(result.headers.get("x-request")).toBeNull();
expect(result.headers.get("content-length")).toBeNull();
expect(result.response).toBeInstanceOf(Response);
});
});
});

View File

@@ -160,16 +160,28 @@ export function toAuthEndpoints<const E extends Record<string, Endpoint>>(
internalContext = defuReplaceArrays(rest, internalContext);
} else if (before) {
/* Return before hook response if it's anything other than a context return */
return context?.asResponse
? toResponse(before, {
headers: context?.headers,
})
: context?.returnHeaders
const response = toResponse(before);
if (context?.asResponse) {
return response;
}
if (context?.returnHeaders) {
return context?.returnStatus
? {
headers: context?.headers,
headers: response.headers,
response: before,
status: response.status,
}
: before;
: {
headers: response.headers,
response: before,
};
}
return context?.returnStatus
? {
response: before,
status: response.status,
}
: before;
}
internalContext.asResponse = false;