diff --git a/src/session.spec.ts b/src/session.spec.ts
index 86246af..2b7ef4b 100644
--- a/src/session.spec.ts
+++ b/src/session.spec.ts
@@ -323,6 +323,41 @@ describe('session', () => {
});
});
+ it('should pass through non-JSON responses with just the cookie added', async () => {
+ // Set up a custom loader that returns HTML
+ const htmlContent = '
Hello World!
';
+ const customLoader = jest.fn().mockReturnValue(
+ new Response(htmlContent, {
+ headers: {
+ 'Content-Type': 'text/html',
+ 'X-Custom-Header': 'test-value',
+ },
+ }),
+ );
+
+ // Call authkitLoader with the HTML-returning loader
+ const result = await authkitLoader(createLoaderArgs(createMockRequest()), customLoader);
+
+ // Verify we got back a Response, not a DataWithResponseInit
+ assertIsResponse(result);
+
+ // Check that the response body wasn't modified
+ const resultText = await result.clone().text();
+ expect(resultText).toBe(htmlContent);
+
+ // Check that original headers were preserved
+ expect(result.headers.get('Content-Type')).toBe('text/html');
+ expect(result.headers.get('X-Custom-Header')).toBe('test-value');
+
+ // Check that session cookie was added
+ expect(result.headers.get('Set-Cookie')).toBe('session-cookie');
+
+ // Verify that the JSON parsing method was not called
+ const jsonSpy = jest.spyOn(Response.prototype, 'json');
+ expect(jsonSpy).not.toHaveBeenCalled();
+ jsonSpy.mockRestore();
+ });
+
it('should return authorized data with session claims', async () => {
const { data } = await authkitLoader(createLoaderArgs(createMockRequest()));
@@ -371,7 +406,7 @@ describe('session', () => {
const { data, init } = await authkitLoader(createLoaderArgs(createMockRequest()), customLoader);
expect(getHeaderValue(init?.headers, 'Custom-Header')).toBe('test-header');
- expect(getHeaderValue(init?.headers, 'Content-Type')).toBe('application/json; charset=utf-8');
+ expect(getHeaderValue(init?.headers, 'Content-Type')).toBe('application/json');
expect(data).toEqual(
expect.objectContaining({
diff --git a/src/session.ts b/src/session.ts
index c21fa2b..e134b52 100644
--- a/src/session.ts
+++ b/src/session.ts
@@ -14,7 +14,7 @@ import { sealData, unsealData } from 'iron-session';
import { createRemoteJWKSet, decodeJwt, jwtVerify } from 'jose';
import { getConfig } from './config.js';
import { configureSessionStorage, getSessionStorage } from './sessionStorage.js';
-import { isResponse, isRedirect } from './utils.js';
+import { isResponse, isRedirect, isJsonResponse } from './utils.js';
// must be a type since this is a subtype of response
// interfaces must conform to the types they extend
@@ -309,15 +309,17 @@ async function handleAuthLoader(
}
const newResponse = new Response(loaderResult.body, loaderResult);
- const responseData = await newResponse.json();
- // Set the content type in case the user returned a Response instead of the
- // json helper method
- newResponse.headers.set('Content-Type', 'application/json; charset=utf-8');
if (session) {
newResponse.headers.append('Set-Cookie', session.headers['Set-Cookie']);
}
+ if (!isJsonResponse(newResponse)) {
+ return newResponse;
+ }
+
+ const responseData = await newResponse.json();
+
return data({ ...responseData, ...auth }, newResponse);
}
diff --git a/src/utils.ts b/src/utils.ts
index a869cbe..29b742d 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -37,6 +37,17 @@ export function isResponse(response: unknown): response is Response {
return response instanceof Response;
}
+/**
+ * Returns true if the response is a JSON response.
+ * This is determined by checking if the Content-Type header includes 'application/json'.
+ * @param res - The response to check.
+ * @returns True if the response is a JSON response.
+ */
+export function isJsonResponse(res: Response): boolean {
+ const contentType = res.headers.get('Content-Type')?.toLowerCase();
+ return !!contentType?.includes('application/json');
+}
+
/**
* Checks if the data is a DataWithResponseInit object.
* @param data - The data to check.