-
Notifications
You must be signed in to change notification settings - Fork 454
Expand file tree
/
Copy pathabstract-session-store.ts
More file actions
170 lines (148 loc) · 4.6 KB
/
abstract-session-store.ts
File metadata and controls
170 lines (148 loc) · 4.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import type { SessionData, SessionDataStore } from "../../types/index.js";
import {
CookieOptions,
ReadonlyRequestCookies,
RequestCookies,
ResponseCookies
} from "../cookies.js";
export interface SessionCookieOptions {
/**
* The name of the session cookie.
*
* Default: `__session`.
*/
name?: string;
/**
* The sameSite attribute of the session cookie.
*
* Default: `lax`.
*/
sameSite?: "strict" | "lax" | "none";
/**
* The secure attribute of the session cookie.
*
* Default: depends on the protocol of the application's base URL. If the protocol is `https`, then `true`, otherwise `false`.
*/
secure?: boolean;
/**
* The path attribute of the session cookie. Will be set to '/' by default.
*/
path?: string;
/**
* Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.3|Domain Set-Cookie attribute}. By default, no
* domain is set, and most clients will consider the cookie to apply to only
* the current domain.
*/
domain?: string;
/**
* The transient attribute of the session cookie. When true, the cookie will not persist beyond the current session.
*/
transient?: boolean;
}
export interface SessionConfiguration {
/**
* A boolean indicating whether rolling sessions should be used or not.
*
* When enabled, the session will continue to be extended as long as it is used within the inactivity duration.
* Once the upper bound, set via the `absoluteDuration`, has been reached, the session will no longer be extended.
*
* Default: `true`.
*/
rolling?: boolean;
/**
* The absolute duration after which the session will expire. The value must be specified in seconds..
*
* Once the absolute duration has been reached, the session will no longer be extended.
*
* Default: 3 days.
*/
absoluteDuration?: number;
/**
* The duration of inactivity after which the session will expire. The value must be specified in seconds.
*
* The session will be extended as long as it was active before the inactivity duration has been reached.
*
* Default: 1 day.
*/
inactivityDuration?: number;
/**
* The options for the session cookie.
*/
cookie?: SessionCookieOptions;
}
export interface SessionStoreOptions extends SessionConfiguration {
secret: string;
store?: SessionDataStore;
cookieOptions?: SessionCookieOptions;
}
const SESSION_COOKIE_NAME = "__session";
export abstract class AbstractSessionStore {
public secret: string;
public sessionCookieName: string;
private rolling: boolean;
private absoluteDuration: number;
private inactivityDuration: number;
public store?: SessionDataStore;
public cookieConfig: CookieOptions;
constructor({
secret,
rolling = true,
absoluteDuration = 60 * 60 * 24 * 3, // 3 days in seconds
inactivityDuration = 60 * 60 * 24 * 1, // 1 day in seconds
store,
cookieOptions
}: SessionStoreOptions) {
this.secret = secret;
this.rolling = rolling;
this.absoluteDuration = absoluteDuration;
this.inactivityDuration = inactivityDuration;
this.store = store;
this.sessionCookieName = cookieOptions?.name ?? SESSION_COOKIE_NAME;
this.cookieConfig = {
httpOnly: true,
sameSite: cookieOptions?.sameSite ?? "lax",
secure: cookieOptions?.secure ?? false,
path: cookieOptions?.path ?? "/",
domain: cookieOptions?.domain,
transient: cookieOptions?.transient
};
}
abstract get(
reqCookies: RequestCookies | ReadonlyRequestCookies
): Promise<SessionData | null>;
/**
* save adds the encrypted session cookie as a `Set-Cookie` header. If the `iat` property
* is present on the session, then it will be used to compute the `maxAge` cookie value.
*/
abstract set(
reqCookies: RequestCookies | ReadonlyRequestCookies,
resCookies: ResponseCookies,
session: SessionData,
isNew?: boolean
): Promise<void>;
abstract delete(
reqCookies: RequestCookies | ReadonlyRequestCookies,
resCookies: ResponseCookies
): Promise<void>;
/**
* epoch returns the time since unix epoch in seconds.
*/
epoch() {
return (Date.now() / 1000) | 0;
}
/**
* calculateMaxAge calculates the max age of the session based on createdAt and the rolling and absolute durations.
*/
calculateMaxAge(createdAt: number) {
if (!this.rolling) {
return this.absoluteDuration;
}
const updatedAt = this.epoch();
const expiresAt = Math.min(
updatedAt + this.inactivityDuration,
createdAt + this.absoluteDuration
);
const maxAge = expiresAt - this.epoch();
return maxAge > 0 ? maxAge : 0;
}
}