Skip to content
This repository was archived by the owner on Dec 12, 2023. It is now read-only.

Commit 47a6bb6

Browse files
committed
Add rolling option
1 parent 5da359f commit 47a6bb6

File tree

3 files changed

+42
-15
lines changed

3 files changed

+42
-15
lines changed

src/module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ const defaults: FilledModuleOptions = {
2323
options: {}
2424
},
2525
domain: null,
26-
ipPinning: false as boolean|SessionIpPinningOptions
26+
ipPinning: false as boolean|SessionIpPinningOptions,
27+
rolling: false
2728
},
2829
api: {
2930
isEnabled: true,

src/runtime/server/middleware/session/index.ts

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,26 @@ import { SessionExpired } from './exceptions'
88
import { useRuntimeConfig } from '#imports'
99

1010
const SESSION_COOKIE_NAME = 'sessionId'
11-
const safeSetCookie = (event: H3Event, name: string, value: string) => setCookie(event, name, value, {
12-
// Max age of cookie in seconds
13-
maxAge: useRuntimeConfig().session.session.expiryInSeconds,
14-
// Only send cookie via HTTPs to mitigate man-in-the-middle attacks
15-
secure: true,
16-
// Only send cookie via HTTP requests, do not allow access of cookie from JS to mitigate XSS attacks
17-
httpOnly: true,
18-
// Do not send cookies on many cross-site requests to mitigates CSRF and cross-site attacks, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#lax
19-
sameSite: useRuntimeConfig().session.session.cookieSameSite as SameSiteOptions,
20-
// Set cookie for subdomain
21-
domain: useRuntimeConfig().session.session.domain
22-
})
11+
const safeSetCookie = (event: H3Event, name: string, value: string, date: Date) => {
12+
const sessionConfig = useRuntimeConfig().session.session
13+
const expirationDate = sessionConfig.expiryInSeconds ? date : undefined
14+
if (expirationDate) {
15+
expirationDate.setSeconds(expirationDate.getSeconds() + sessionConfig.expiryInSeconds)
16+
}
17+
18+
setCookie(event, name, value, {
19+
// Set cookie expiration date to now + expiryInSeconds
20+
expires: expirationDate,
21+
// Only send cookie via HTTPs to mitigate man-in-the-middle attacks
22+
secure: true,
23+
// Only send cookie via HTTP requests, do not allow access of cookie from JS to mitigate XSS attacks
24+
httpOnly: true,
25+
// Do not send cookies on many cross-site requests to mitigates CSRF and cross-site attacks, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#lax
26+
sameSite: sessionConfig.cookieSameSite as SameSiteOptions,
27+
// Set cookie for subdomain
28+
domain: sessionConfig.domain
29+
})
30+
}
2331

2432
const checkSessionExpirationTime = (session: Session, sessionExpiryInSeconds: number) => {
2533
const now = dayjs()
@@ -61,15 +69,16 @@ export const deleteSession = async (event: H3Event) => {
6169
const newSession = async (event: H3Event) => {
6270
const runtimeConfig = useRuntimeConfig()
6371
const sessionOptions = runtimeConfig.session.session
72+
const now = new Date()
6473

6574
// (Re-)Set cookie
6675
const sessionId = nanoid(sessionOptions.idLength)
67-
safeSetCookie(event, SESSION_COOKIE_NAME, sessionId)
76+
safeSetCookie(event, SESSION_COOKIE_NAME, sessionId, now)
6877

6978
// Store session data in storage
7079
const session: Session = {
7180
id: sessionId,
72-
createdAt: new Date(),
81+
createdAt: now,
7382
ip: sessionOptions.ipPinning ? await getHashedIpAddress(event) : undefined
7483
}
7584
await setStorageSession(sessionId, session)
@@ -113,14 +122,24 @@ const getSession = async (event: H3Event): Promise<null | Session> => {
113122
return session
114123
}
115124

125+
const touchSession = (session: Session, event: H3Event) => {
126+
const now = new Date()
127+
session.createdAt = now
128+
safeSetCookie(event, SESSION_COOKIE_NAME, session.id, now)
129+
}
130+
116131
function isSession (shape: unknown): shape is Session {
117132
return typeof shape === 'object' && !!shape && 'id' in shape && 'createdAt' in shape
118133
}
119134

120135
const ensureSession = async (event: H3Event) => {
136+
const sessionConfig = useRuntimeConfig().session.session
137+
121138
let session = await getSession(event)
122139
if (!session) {
123140
session = await newSession(event)
141+
} else if (sessionConfig.rolling) {
142+
touchSession(session, event)
124143
}
125144

126145
event.context.sessionId = session.id

src/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ export interface SessionOptions {
8282
* @type {SessionIpPinningOptions|boolean}
8383
*/
8484
ipPinning: SessionIpPinningOptions|boolean,
85+
/**
86+
* Force the session identifier cookie to be set on every response. The expiration is reset to the original expiryInSeconds, resetting the expiration countdown.
87+
* @default false
88+
* @example true
89+
* @type boolean
90+
*/
91+
rolling: boolean
8592
}
8693

8794
export interface ApiOptions {

0 commit comments

Comments
 (0)