mirror of
https://github.com/better-auth/better-auth.git
synced 2026-05-23 23:52:05 -05:00
fix: respect returnHeaders and returnStatus options in auth.api endpoints
Fixes #6520 This change ensures returnHeaders and returnStatus options are properly respected in auth.api endpoints, fixing a bug where responses were always wrapped regardless of these options. Changes: - Set returnHeaders and returnStatus defaults to false instead of true - Fix type assertion to allow undefined headers - Add null safety check before accessing result.headers - Change null to undefined for consistency with optional types - Add comprehensive test cases for all return format combinations
This commit is contained in:
@@ -369,6 +369,214 @@ describe("session", async () => {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("should return session data directly without wrapping when returnHeaders is not set", async () => {
|
||||
const context = await auth.$context;
|
||||
await runWithEndpointContext(
|
||||
{
|
||||
context,
|
||||
} as unknown as GenericEndpointContext,
|
||||
async () => {
|
||||
const signInRes = await auth.api.signInEmail({
|
||||
body: {
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
},
|
||||
returnHeaders: true,
|
||||
});
|
||||
|
||||
const signInHeaders = new Headers();
|
||||
signInHeaders.set("cookie", signInRes.headers.getSetCookie()[0]!);
|
||||
|
||||
const sessionRes = await auth.api.getSession({
|
||||
headers: signInHeaders,
|
||||
});
|
||||
|
||||
// Should return session data directly, not wrapped in { response: ... }
|
||||
expect(sessionRes).toHaveProperty("user");
|
||||
expect(sessionRes).toHaveProperty("session");
|
||||
// @ts-expect-error: response property should not exist
|
||||
expect(sessionRes.response).toBeUndefined();
|
||||
// @ts-expect-error: headers property should not exist
|
||||
expect(sessionRes.headers).toBeUndefined();
|
||||
// @ts-expect-error: status property should not exist
|
||||
expect(sessionRes.status).toBeUndefined();
|
||||
|
||||
expect(sessionRes?.user).toBeDefined();
|
||||
expect(sessionRes?.session).toBeDefined();
|
||||
expect(sessionRes?.user.email).toBe(testUser.email);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("should return status without headers when only returnStatus is true", async () => {
|
||||
const context = await auth.$context;
|
||||
await runWithEndpointContext(
|
||||
{
|
||||
context,
|
||||
} as unknown as GenericEndpointContext,
|
||||
async () => {
|
||||
const signInRes = await auth.api.signInEmail({
|
||||
body: {
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
},
|
||||
returnHeaders: true,
|
||||
});
|
||||
|
||||
const signInHeaders = new Headers();
|
||||
signInHeaders.set("cookie", signInRes.headers.getSetCookie()[0]!);
|
||||
|
||||
const sessionRes = await auth.api.getSession({
|
||||
headers: signInHeaders,
|
||||
returnStatus: true,
|
||||
});
|
||||
|
||||
expect(sessionRes).toHaveProperty("response");
|
||||
expect(sessionRes).toHaveProperty("status");
|
||||
expect(sessionRes.response).toHaveProperty("user");
|
||||
expect(sessionRes.response).toHaveProperty("session");
|
||||
// @ts-expect-error: headers property should not exist
|
||||
expect(sessionRes.headers).toBeUndefined();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("should respect explicit returnHeaders: false", async () => {
|
||||
const context = await auth.$context;
|
||||
await runWithEndpointContext(
|
||||
{
|
||||
context,
|
||||
} as unknown as GenericEndpointContext,
|
||||
async () => {
|
||||
const signInRes = await auth.api.signInEmail({
|
||||
body: {
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
},
|
||||
returnHeaders: true,
|
||||
});
|
||||
|
||||
const signInHeaders = new Headers();
|
||||
signInHeaders.set("cookie", signInRes.headers.getSetCookie()[0]!);
|
||||
|
||||
const sessionRes = await auth.api.getSession({
|
||||
headers: signInHeaders,
|
||||
returnHeaders: false,
|
||||
returnStatus: false,
|
||||
});
|
||||
|
||||
expect(sessionRes).toHaveProperty("user");
|
||||
expect(sessionRes).toHaveProperty("session");
|
||||
// @ts-expect-error: response property should not exist
|
||||
expect(sessionRes.response).toBeUndefined();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("should return both headers and status when both options are true", async () => {
|
||||
const context = await auth.$context;
|
||||
await runWithEndpointContext(
|
||||
{
|
||||
context,
|
||||
} as unknown as GenericEndpointContext,
|
||||
async () => {
|
||||
const signInRes = await auth.api.signInEmail({
|
||||
body: {
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
},
|
||||
returnHeaders: true,
|
||||
returnStatus: true,
|
||||
});
|
||||
|
||||
expect(signInRes).toHaveProperty("headers");
|
||||
expect(signInRes).toHaveProperty("response");
|
||||
expect(signInRes).toHaveProperty("status");
|
||||
expect(signInRes.response).toHaveProperty("user");
|
||||
expect(signInRes.response).toHaveProperty("session");
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("should use APIError statusCode when asResponse is true", async () => {
|
||||
const context = await auth.$context;
|
||||
await runWithEndpointContext(
|
||||
{
|
||||
context,
|
||||
} as unknown as GenericEndpointContext,
|
||||
async () => {
|
||||
const errorRes = await auth.api.signInEmail(
|
||||
{
|
||||
body: {
|
||||
email: "wrong@example.com",
|
||||
password: "wrongpassword",
|
||||
},
|
||||
asResponse: true,
|
||||
},
|
||||
{
|
||||
onError: (context) => {
|
||||
expect(context.response).toBeInstanceOf(Response);
|
||||
expect(context.response.status).toBe(401);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(errorRes.error).toBeDefined();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("should include APIError statusCode when returnStatus is true", async () => {
|
||||
const context = await auth.$context;
|
||||
await runWithEndpointContext(
|
||||
{
|
||||
context,
|
||||
} as unknown as GenericEndpointContext,
|
||||
async () => {
|
||||
const errorRes = await auth.api.signInEmail({
|
||||
body: {
|
||||
email: "wrong@example.com",
|
||||
password: "wrongpassword",
|
||||
},
|
||||
returnStatus: true,
|
||||
});
|
||||
|
||||
expect(errorRes.error).toBeDefined();
|
||||
expect(errorRes.error?.status).toBe(401);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle different APIError status codes correctly", async () => {
|
||||
const context = await auth.$context;
|
||||
await runWithEndpointContext(
|
||||
{
|
||||
context,
|
||||
} as unknown as GenericEndpointContext,
|
||||
async () => {
|
||||
const unauthorizedRes = await auth.api.signInEmail({
|
||||
body: {
|
||||
email: "wrong@example.com",
|
||||
password: "wrongpassword",
|
||||
},
|
||||
returnStatus: true,
|
||||
returnHeaders: true,
|
||||
});
|
||||
expect(unauthorizedRes.error?.status).toBe(401);
|
||||
|
||||
const badRequestRes = await auth.api.signInEmail({
|
||||
body: {
|
||||
email: "invalid-email",
|
||||
password: "password",
|
||||
},
|
||||
returnStatus: true,
|
||||
returnHeaders: true,
|
||||
});
|
||||
expect(badRequestRes.error?.status).toBe(400);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("session storage", async () => {
|
||||
|
||||
@@ -106,8 +106,8 @@ export function toAuthEndpoints<
|
||||
}
|
||||
|
||||
internalContext.asResponse = false;
|
||||
internalContext.returnHeaders = true;
|
||||
internalContext.returnStatus = true;
|
||||
internalContext.returnHeaders = context?.returnHeaders ?? false;
|
||||
internalContext.returnStatus = context?.returnStatus ?? false;
|
||||
const result = (await runWithEndpointContext(internalContext, () =>
|
||||
(endpoint as any)(internalContext as any),
|
||||
).catch((e: any) => {
|
||||
@@ -119,12 +119,12 @@ export function toAuthEndpoints<
|
||||
return {
|
||||
response: e,
|
||||
status: e.statusCode,
|
||||
headers: e.headers ? new Headers(e.headers) : null,
|
||||
headers: e.headers ? new Headers(e.headers) : undefined,
|
||||
};
|
||||
}
|
||||
throw e;
|
||||
})) as {
|
||||
headers: Headers;
|
||||
headers: Headers | undefined;
|
||||
response: any;
|
||||
status: number;
|
||||
};
|
||||
@@ -134,46 +134,58 @@ export function toAuthEndpoints<
|
||||
return result;
|
||||
}
|
||||
|
||||
internalContext.context.returned = result.response;
|
||||
internalContext.context.responseHeaders = result.headers;
|
||||
if (result) {
|
||||
internalContext.context.returned = result.response;
|
||||
if (result.headers) {
|
||||
internalContext.context.responseHeaders = result.headers;
|
||||
}
|
||||
}
|
||||
|
||||
const after = await runAfterHooks(internalContext, afterHooks);
|
||||
|
||||
// Use result or fallback to after response
|
||||
let responseData = result?.response;
|
||||
let headers = result?.headers;
|
||||
let status = result?.status;
|
||||
|
||||
if (after.response) {
|
||||
result.response = after.response;
|
||||
responseData = after.response;
|
||||
}
|
||||
|
||||
if (
|
||||
result.response instanceof APIError &&
|
||||
responseData instanceof APIError &&
|
||||
shouldPublishLog(authContext.logger.level, "debug")
|
||||
) {
|
||||
// inherit stack from errorStack if debug mode is enabled
|
||||
result.response.stack = result.response.errorStack;
|
||||
responseData.stack = responseData.errorStack;
|
||||
}
|
||||
|
||||
if (result.response instanceof APIError && !context?.asResponse) {
|
||||
throw result.response;
|
||||
if (responseData instanceof APIError && !context?.asResponse) {
|
||||
throw responseData;
|
||||
}
|
||||
|
||||
const response = context?.asResponse
|
||||
? toResponse(result.response, {
|
||||
headers: result.headers,
|
||||
status: result.status,
|
||||
? toResponse(responseData, {
|
||||
headers: headers,
|
||||
status:
|
||||
responseData instanceof APIError
|
||||
? responseData.statusCode
|
||||
: (status ?? 200),
|
||||
})
|
||||
: context?.returnHeaders
|
||||
? context?.returnStatus
|
||||
? {
|
||||
headers: result.headers,
|
||||
response: result.response,
|
||||
status: result.status,
|
||||
headers: headers,
|
||||
response: responseData,
|
||||
status: status ?? 200,
|
||||
}
|
||||
: {
|
||||
headers: result.headers,
|
||||
response: result.response,
|
||||
headers: headers,
|
||||
response: responseData,
|
||||
}
|
||||
: context?.returnStatus
|
||||
? { response: result.response, status: result.status }
|
||||
: result.response;
|
||||
? { response: responseData, status: status ?? 200 }
|
||||
: responseData;
|
||||
return response;
|
||||
});
|
||||
};
|
||||
@@ -257,15 +269,15 @@ async function runAfterHooks(
|
||||
}
|
||||
return {
|
||||
response: e,
|
||||
headers: e.headers ? new Headers(e.headers) : null,
|
||||
headers: e.headers ? new Headers(e.headers) : undefined,
|
||||
};
|
||||
}
|
||||
throw e;
|
||||
})) as {
|
||||
response: any;
|
||||
headers: Headers;
|
||||
};
|
||||
if (result.headers) {
|
||||
headers?: Headers;
|
||||
} | void;
|
||||
if (result && result.headers) {
|
||||
result.headers.forEach((value, key) => {
|
||||
if (!context.context.responseHeaders) {
|
||||
context.context.responseHeaders = new Headers({
|
||||
@@ -280,7 +292,7 @@ async function runAfterHooks(
|
||||
}
|
||||
});
|
||||
}
|
||||
if (result.response) {
|
||||
if (result && result.response) {
|
||||
context.context.returned = result.response;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user