Skip to content

Commit af2ccea

Browse files
authored
feat(core): add default cache control headers for GET endpoints (#11667)
* feat(core): add default cache control header for GET session * feat(core): add default cache control header for GET CSRF
1 parent ec57440 commit af2ccea

File tree

6 files changed

+70
-4
lines changed

6 files changed

+70
-4
lines changed

docs/pages/getting-started/session-management/get-session.mdx

+5
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,8 @@ app.get("/", (req, res) => {
205205
</Code>
206206

207207
If you'd like to extend your session with more fields from your OAuth provider, for example, please check out our ["extending the session" guide](/guides/extending-the-session).
208+
209+
<Callout>
210+
By default, GET requests to the session endpoint will automatically return the
211+
headers to prevent caching.
212+
</Callout>

packages/core/src/lib/actions/session.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,14 @@ export async function session(
2424

2525
const response: ResponseInternal<Session | null> = {
2626
body: null,
27-
headers: { "Content-Type": "application/json" },
27+
headers: {
28+
"Content-Type": "application/json",
29+
...(!isUpdate && {
30+
"Cache-Control": "private, no-cache, no-store",
31+
Expires: "0",
32+
Pragma: "no-cache",
33+
}),
34+
},
2835
cookies,
2936
}
3037

packages/core/src/lib/pages/index.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,12 @@ export default function renderPage(params: RenderPageParams) {
5656
csrf(skip: boolean, options: InternalOptions, cookies: Cookie[]) {
5757
if (!skip) {
5858
return {
59-
headers: { "Content-Type": "application/json" },
59+
headers: {
60+
"Content-Type": "application/json",
61+
"Cache-Control": "private, no-cache, no-store",
62+
Expires: "0",
63+
Pragma: "no-cache",
64+
},
6065
body: { csrfToken: options.csrfToken },
6166
cookies,
6267
}
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
2+
3+
import {
4+
makeAuthRequest,
5+
testConfig,
6+
assertNoCacheResponseHeaders,
7+
} from "../utils.js"
8+
9+
describe("assert GET CSRF action", () => {
10+
beforeEach(() => {
11+
vi.resetAllMocks()
12+
})
13+
afterEach(() => {
14+
vi.restoreAllMocks()
15+
})
16+
it("shoud return CSRF token with no cache headers", async () => {
17+
const authConfig = testConfig()
18+
const { response } = await makeAuthRequest({
19+
action: "csrf",
20+
config: authConfig,
21+
})
22+
assertNoCacheResponseHeaders(response)
23+
const body = await response.json()
24+
25+
expect(body.csrfToken).toBeDefined()
26+
})
27+
})

packages/core/test/actions/session.test.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
testConfig,
1515
AUTH_SECRET,
1616
SESSION_COOKIE_NAME,
17+
assertNoCacheResponseHeaders,
1718
} from "../utils.js"
1819

1920
const { parse: parseCookie } = cookie
@@ -94,6 +95,8 @@ describe("assert GET session action", () => {
9495
session: expectedSession,
9596
token: expectedToken,
9697
})
98+
99+
assertNoCacheResponseHeaders(response)
97100
})
98101

99102
it("should return null if no JWT session in the requests cookies", async () => {
@@ -102,6 +105,8 @@ describe("assert GET session action", () => {
102105
})
103106
const actual = await response.json()
104107
expect(actual).toEqual(null)
108+
109+
assertNoCacheResponseHeaders(response)
105110
})
106111

107112
it("should return null if JWT session is invalid", async () => {
@@ -113,6 +118,8 @@ describe("assert GET session action", () => {
113118
})
114119
const actual = await response.json()
115120
expect(actual).toEqual(null)
121+
122+
assertNoCacheResponseHeaders(response)
116123
})
117124

118125
it("should throw invalid JWT error if salt is invalid", async () => {
@@ -132,8 +139,10 @@ describe("assert GET session action", () => {
132139
})
133140
const actual = await response.json()
134141

135-
expect(logger.error).toHaveBeenCalledOnce()
136142
expect(actual).toEqual(null)
143+
expect(logger.error).toHaveBeenCalledOnce()
144+
145+
assertNoCacheResponseHeaders(response)
137146
})
138147
})
139148
describe("Database strategy", () => {
@@ -207,6 +216,8 @@ describe("assert GET session action", () => {
207216
email: expectedUser.email,
208217
})
209218
expect(actualBodySession.expires).toEqual(currentExpires.toISOString())
219+
220+
assertNoCacheResponseHeaders(response)
210221
})
211222

212223
it("should return null in the response, and delete the session", async () => {
@@ -259,6 +270,8 @@ describe("assert GET session action", () => {
259270

260271
expect(actualSessionToken).toEqual("")
261272
expect(actualBodySession).toEqual(null)
273+
274+
assertNoCacheResponseHeaders(response)
262275
})
263276
})
264277
})

packages/core/test/utils.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { vi } from "vitest"
1+
import { expect, vi } from "vitest"
22
import { Auth, createActionURL } from "../src"
33

44
import type { Adapter } from "../src/adapters"
@@ -93,3 +93,12 @@ export async function makeAuthRequest(params: {
9393
logger: config.logger,
9494
}
9595
}
96+
97+
export const assertNoCacheResponseHeaders = (response: Response) => {
98+
expect(response.headers.get("Content-Type")).toEqual("application/json")
99+
expect(response.headers.get("Cache-Control")).toEqual(
100+
"private, no-cache, no-store"
101+
)
102+
expect(response.headers.get("Expires")).toEqual("0")
103+
expect(response.headers.get("Pragma")).toEqual("no-cache")
104+
}

0 commit comments

Comments
 (0)