@arikajs/session 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +429 -0
- package/dist/Contracts/SessionDriver.d.ts +33 -0
- package/dist/Contracts/SessionDriver.d.ts.map +1 -0
- package/dist/Contracts/SessionDriver.js +3 -0
- package/dist/Contracts/SessionDriver.js.map +1 -0
- package/dist/CookieSessionId.d.ts +35 -0
- package/dist/CookieSessionId.d.ts.map +1 -0
- package/dist/CookieSessionId.js +129 -0
- package/dist/CookieSessionId.js.map +1 -0
- package/dist/Drivers/DatabaseDriver.d.ts +19 -0
- package/dist/Drivers/DatabaseDriver.d.ts.map +1 -0
- package/dist/Drivers/DatabaseDriver.js +90 -0
- package/dist/Drivers/DatabaseDriver.js.map +1 -0
- package/dist/Drivers/FileDriver.d.ts +18 -0
- package/dist/Drivers/FileDriver.d.ts.map +1 -0
- package/dist/Drivers/FileDriver.js +170 -0
- package/dist/Drivers/FileDriver.js.map +1 -0
- package/dist/Drivers/MemoryDriver.d.ts +16 -0
- package/dist/Drivers/MemoryDriver.d.ts.map +1 -0
- package/dist/Drivers/MemoryDriver.js +53 -0
- package/dist/Drivers/MemoryDriver.js.map +1 -0
- package/dist/Drivers/RedisDriver.d.ts +20 -0
- package/dist/Drivers/RedisDriver.d.ts.map +1 -0
- package/dist/Drivers/RedisDriver.js +54 -0
- package/dist/Drivers/RedisDriver.js.map +1 -0
- package/dist/Middleware/StartSession.d.ts +19 -0
- package/dist/Middleware/StartSession.d.ts.map +1 -0
- package/dist/Middleware/StartSession.js +109 -0
- package/dist/Middleware/StartSession.js.map +1 -0
- package/dist/Session.d.ts +88 -0
- package/dist/Session.d.ts.map +1 -0
- package/dist/Session.js +232 -0
- package/dist/Session.js.map +1 -0
- package/dist/SessionManager.d.ts +51 -0
- package/dist/SessionManager.d.ts.map +1 -0
- package/dist/SessionManager.js +117 -0
- package/dist/SessionManager.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
## Arika Session
|
|
2
|
+
|
|
3
|
+
`@arikajs/session` provides a powerful, secure, and driver-based session management system for the ArikaJS framework.
|
|
4
|
+
|
|
5
|
+
It answers one critical question: **"How do we persist user state across requests?"**
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
req.session.set('user_id', 42);
|
|
9
|
+
req.session.get('user_id'); // 42
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
### Status
|
|
15
|
+
|
|
16
|
+
- **Stage**: Experimental / v0.x
|
|
17
|
+
- **Scope**:
|
|
18
|
+
- Driver-based session storage (file, memory, redis, database)
|
|
19
|
+
- Flash session data (one-request messages)
|
|
20
|
+
- Cookie-based session identifiers
|
|
21
|
+
- Automatic session lifecycle middleware
|
|
22
|
+
- Session regeneration for security
|
|
23
|
+
- Lazy session loading
|
|
24
|
+
- Write optimization
|
|
25
|
+
- Session locking for concurrency protection
|
|
26
|
+
|
|
27
|
+
- **Design**:
|
|
28
|
+
- Framework-agnostic session manager
|
|
29
|
+
- Pluggable storage drivers
|
|
30
|
+
- Middleware-driven lifecycle
|
|
31
|
+
- Config-driven architecture
|
|
32
|
+
- Environment variable support
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## ✨ Purpose
|
|
37
|
+
|
|
38
|
+
HTTP is stateless by design.
|
|
39
|
+
Sessions provide a mechanism to persist data across requests.
|
|
40
|
+
|
|
41
|
+
Typical use cases include:
|
|
42
|
+
- Authentication state
|
|
43
|
+
- Flash messages
|
|
44
|
+
- Shopping carts
|
|
45
|
+
- CSRF tokens
|
|
46
|
+
- Multi-step forms
|
|
47
|
+
- User preferences
|
|
48
|
+
|
|
49
|
+
`@arikajs/session` provides a centralized and scalable session system for modern Node.js applications.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 🚀 Features
|
|
54
|
+
|
|
55
|
+
- **Driver-based storage** — Switch between file, memory, Redis, or database drivers.
|
|
56
|
+
- **Flash session support** — Store temporary messages for the next request.
|
|
57
|
+
- **Automatic middleware lifecycle** — Sessions start and persist automatically.
|
|
58
|
+
- **Cookie-based session IDs** — Lightweight and secure.
|
|
59
|
+
- **Session locking** — Prevent race conditions in concurrent requests.
|
|
60
|
+
- **Lazy session loading** — Sessions load only when accessed.
|
|
61
|
+
- **Write optimization** — Avoid unnecessary storage writes.
|
|
62
|
+
- **Session regeneration** — Protect against session fixation attacks.
|
|
63
|
+
- **Environment configuration** — Configure using `.env`.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 📦 Installation
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install @arikajs/session
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 🧠 Core Concepts
|
|
76
|
+
|
|
77
|
+
### 1️⃣ Sessions
|
|
78
|
+
|
|
79
|
+
A session is a key-value store associated with a user request.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
req.session.set('name', 'Prakash');
|
|
83
|
+
req.session.set('role', 'admin');
|
|
84
|
+
|
|
85
|
+
req.session.get('name'); // "Prakash"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Session data persists between requests using a session ID stored in cookies.
|
|
89
|
+
|
|
90
|
+
### 2️⃣ Flash Data
|
|
91
|
+
|
|
92
|
+
Flash data exists for one request only.
|
|
93
|
+
Commonly used for UI notifications such as login success messages.
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
req.session.flash('success', 'Login successful');
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Next request:
|
|
100
|
+
```ts
|
|
101
|
+
req.session.get('success'); // "Login successful"
|
|
102
|
+
```
|
|
103
|
+
After the response completes, the flash data is automatically removed.
|
|
104
|
+
|
|
105
|
+
### 3️⃣ Session Regeneration
|
|
106
|
+
|
|
107
|
+
Session IDs should be regenerated after authentication.
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
await req.session.regenerate();
|
|
111
|
+
```
|
|
112
|
+
This prevents session fixation attacks.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## ⚙️ Configuration
|
|
117
|
+
|
|
118
|
+
Sessions are configured via `config/session.ts`.
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
export default {
|
|
122
|
+
driver: process.env.SESSION_DRIVER || 'file',
|
|
123
|
+
lifetime: Number(process.env.SESSION_LIFETIME) || 120,
|
|
124
|
+
cookie: process.env.SESSION_COOKIE || 'arika_session',
|
|
125
|
+
path: process.env.SESSION_PATH || './storage/sessions',
|
|
126
|
+
secure: false,
|
|
127
|
+
httpOnly: true,
|
|
128
|
+
locking: true,
|
|
129
|
+
lockTimeout: 10
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Environment Variables
|
|
134
|
+
|
|
135
|
+
```env
|
|
136
|
+
SESSION_DRIVER=file
|
|
137
|
+
SESSION_LIFETIME=120
|
|
138
|
+
SESSION_COOKIE=arika_session
|
|
139
|
+
SESSION_PATH=./storage/sessions
|
|
140
|
+
SESSION_LOCKING=true
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 🧩 Middleware Support
|
|
146
|
+
|
|
147
|
+
Sessions are automatically initialized via middleware.
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { StartSession } from '@arikajs/session';
|
|
151
|
+
|
|
152
|
+
app.use(StartSession);
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The middleware:
|
|
156
|
+
1. Reads session ID from cookies
|
|
157
|
+
2. Loads session data from the configured driver
|
|
158
|
+
3. Attaches session to the request
|
|
159
|
+
4. Persists changes after response
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 🗂 Session API
|
|
164
|
+
|
|
165
|
+
**Set Value**
|
|
166
|
+
```ts
|
|
167
|
+
req.session.set('cart_items', 3);
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Get Value**
|
|
171
|
+
```ts
|
|
172
|
+
req.session.get('cart_items');
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Check Existence**
|
|
176
|
+
```ts
|
|
177
|
+
req.session.has('cart_items');
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Remove Value**
|
|
181
|
+
```ts
|
|
182
|
+
req.session.forget('cart_items');
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Destroy Session**
|
|
186
|
+
```ts
|
|
187
|
+
await req.session.destroy();
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## 💾 Storage Drivers
|
|
193
|
+
|
|
194
|
+
`@arikajs/session` supports multiple storage drivers.
|
|
195
|
+
|
|
196
|
+
| Driver | Description |
|
|
197
|
+
| :--- | :--- |
|
|
198
|
+
| `memory` | In-memory storage (development only) |
|
|
199
|
+
| `file` | Filesystem-based sessions |
|
|
200
|
+
| `redis` | Redis distributed session storage |
|
|
201
|
+
| `database` | SQL-based session storage |
|
|
202
|
+
|
|
203
|
+
Example configuration:
|
|
204
|
+
```ts
|
|
205
|
+
export default {
|
|
206
|
+
driver: 'redis'
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Drivers implement a common interface:
|
|
211
|
+
```ts
|
|
212
|
+
interface SessionDriver {
|
|
213
|
+
load(sessionId: string): Promise<any>;
|
|
214
|
+
save(sessionId: string, data: any): Promise<void>;
|
|
215
|
+
destroy(sessionId: string): Promise<void>;
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## 🔐 Session Locking (Concurrency Protection)
|
|
222
|
+
|
|
223
|
+
Modern applications may issue multiple concurrent requests from the same user.
|
|
224
|
+
|
|
225
|
+
Example:
|
|
226
|
+
```text
|
|
227
|
+
Browser
|
|
228
|
+
├─ Request A → update-profile
|
|
229
|
+
└─ Request B → add-to-cart
|
|
230
|
+
```
|
|
231
|
+
Without protection, both requests may overwrite session data.
|
|
232
|
+
|
|
233
|
+
Session locking ensures:
|
|
234
|
+
1. Request A → acquires lock
|
|
235
|
+
2. Request B → waits
|
|
236
|
+
3. Request A → releases lock
|
|
237
|
+
4. Request B → proceeds
|
|
238
|
+
|
|
239
|
+
Configuration:
|
|
240
|
+
```ts
|
|
241
|
+
export default {
|
|
242
|
+
locking: true,
|
|
243
|
+
lockTimeout: 10
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Recommended drivers:
|
|
248
|
+
|
|
249
|
+
| Driver | Locking Support |
|
|
250
|
+
| :--- | :--- |
|
|
251
|
+
| Redis | Excellent |
|
|
252
|
+
| Database | Good |
|
|
253
|
+
| File | Basic |
|
|
254
|
+
| Memory | Not recommended |
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## ⚡ Lazy Session Loading
|
|
259
|
+
|
|
260
|
+
Sessions load only when accessed.
|
|
261
|
+
|
|
262
|
+
Example:
|
|
263
|
+
```ts
|
|
264
|
+
GET /health → session NOT loaded
|
|
265
|
+
GET /dashboard → session loaded
|
|
266
|
+
req.session.get('user_id');
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Benefits:
|
|
270
|
+
- Reduced I/O
|
|
271
|
+
- Faster responses
|
|
272
|
+
- Lower memory usage
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## 🧾 Write Optimization
|
|
277
|
+
|
|
278
|
+
Session storage writes occur only when data changes.
|
|
279
|
+
|
|
280
|
+
Example:
|
|
281
|
+
```text
|
|
282
|
+
Request → session accessed
|
|
283
|
+
→ no changes
|
|
284
|
+
→ skip write
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
But when modified:
|
|
288
|
+
```ts
|
|
289
|
+
req.session.set('cart', items)
|
|
290
|
+
```
|
|
291
|
+
Then storage write occurs.
|
|
292
|
+
|
|
293
|
+
Benefits:
|
|
294
|
+
- Reduced Redis calls
|
|
295
|
+
- Reduced disk writes
|
|
296
|
+
- Better scalability
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## 🧠 Automatic Garbage Collection
|
|
301
|
+
|
|
302
|
+
Expired sessions must be periodically cleaned.
|
|
303
|
+
|
|
304
|
+
Example strategies:
|
|
305
|
+
|
|
306
|
+
| Driver | Cleanup |
|
|
307
|
+
| :--- | :--- |
|
|
308
|
+
| File | Directory cleanup |
|
|
309
|
+
| Redis | Native TTL |
|
|
310
|
+
| Database | Scheduled cleanup |
|
|
311
|
+
|
|
312
|
+
Configuration:
|
|
313
|
+
```ts
|
|
314
|
+
export default {
|
|
315
|
+
gcProbability: 0.01
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
Meaning 1% of requests trigger cleanup.
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## 🔁 Session ID Rotation
|
|
323
|
+
|
|
324
|
+
Session IDs can be rotated for additional security.
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
await req.session.regenerate();
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Use cases:
|
|
331
|
+
- After login
|
|
332
|
+
- After role escalation
|
|
333
|
+
- After sensitive actions
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## 🧩 Custom Drivers
|
|
338
|
+
|
|
339
|
+
Developers can implement custom session drivers.
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
class MongoSessionDriver {
|
|
343
|
+
async load(id) { }
|
|
344
|
+
async save(id, data) { }
|
|
345
|
+
async destroy(id) { }
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Register driver:
|
|
350
|
+
```ts
|
|
351
|
+
SessionManager.extend('mongo', () => new MongoSessionDriver());
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## ⚡ Performance
|
|
357
|
+
|
|
358
|
+
`@arikajs/session` is optimized for high-throughput applications.
|
|
359
|
+
|
|
360
|
+
- Lazy session loading
|
|
361
|
+
- Mutation-based writes
|
|
362
|
+
- Efficient cookie parsing
|
|
363
|
+
- Driver abstraction
|
|
364
|
+
- Request lifecycle integration
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## 🏗 Architecture
|
|
369
|
+
|
|
370
|
+
```text
|
|
371
|
+
session/
|
|
372
|
+
├── src/
|
|
373
|
+
│ ├── Contracts
|
|
374
|
+
│ │ └── SessionDriver.ts
|
|
375
|
+
│ ├── Drivers
|
|
376
|
+
│ │ ├── FileDriver.ts
|
|
377
|
+
│ │ ├── MemoryDriver.ts
|
|
378
|
+
│ │ ├── RedisDriver.ts
|
|
379
|
+
│ │ └── DatabaseDriver.ts
|
|
380
|
+
│ ├── Middleware
|
|
381
|
+
│ │ └── StartSession.ts
|
|
382
|
+
│ ├── Session.ts
|
|
383
|
+
│ ├── SessionManager.ts
|
|
384
|
+
│ ├── CookieSessionId.ts
|
|
385
|
+
│ ├── index.ts
|
|
386
|
+
│ └── utils.ts
|
|
387
|
+
├── tests/
|
|
388
|
+
├── package.json
|
|
389
|
+
├── tsconfig.json
|
|
390
|
+
└── README.md
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
## 🔌 Integration with ArikaJS
|
|
396
|
+
|
|
397
|
+
| Package | Responsibility |
|
|
398
|
+
| :--- | :--- |
|
|
399
|
+
| `@arikajs/http` | Request & response lifecycle |
|
|
400
|
+
| `@arikajs/router` | Route matching |
|
|
401
|
+
| `@arikajs/session` | Session state management |
|
|
402
|
+
| `@arikajs/auth` | Authentication |
|
|
403
|
+
| `@arikajs/authorization` | Access control |
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## 📌 Design Philosophy
|
|
408
|
+
|
|
409
|
+
- **Stateless by default** — Sessions only when needed.
|
|
410
|
+
- **Driver abstraction** — Swap storage without changing code.
|
|
411
|
+
- **Security-first** — Built-in protection against fixation attacks.
|
|
412
|
+
- **Performance-focused** — Lazy loading and write optimization.
|
|
413
|
+
- **Framework-aligned** — Works seamlessly with ArikaJS middleware lifecycle.
|
|
414
|
+
|
|
415
|
+
> "Sessions maintain continuity in a stateless world."
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## 🔄 Versioning & Stability
|
|
420
|
+
|
|
421
|
+
- Current version: **v0.x** (Experimental)
|
|
422
|
+
- APIs may change until **v1.0**
|
|
423
|
+
- Semantic versioning will be adopted after stabilization.
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## 📜 License
|
|
428
|
+
|
|
429
|
+
`@arikajs/session` is open-sourced software licensed under the **MIT License**.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session driver contract.
|
|
3
|
+
* All session storage backends must implement this interface.
|
|
4
|
+
*/
|
|
5
|
+
export interface SessionDriver {
|
|
6
|
+
/**
|
|
7
|
+
* Read session data by session ID. Returns null if not found or expired.
|
|
8
|
+
*/
|
|
9
|
+
load(sessionId: string): Promise<Record<string, any> | null>;
|
|
10
|
+
/**
|
|
11
|
+
* Persist session data with a TTL in seconds.
|
|
12
|
+
*/
|
|
13
|
+
save(sessionId: string, data: Record<string, any>, ttlSeconds: number): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Delete a session by ID.
|
|
16
|
+
*/
|
|
17
|
+
destroy(sessionId: string): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Run garbage collection to delete expired sessions.
|
|
20
|
+
* @param probability 0.0 – 1.0 likelihood of running GC on this call
|
|
21
|
+
*/
|
|
22
|
+
gc(maxLifetimeSeconds: number): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Optional: Acquire a lock for this session (for concurrency protection).
|
|
25
|
+
* Should return true if lock was acquired, false otherwise.
|
|
26
|
+
*/
|
|
27
|
+
acquireLock?(sessionId: string, timeoutSeconds: number): Promise<boolean>;
|
|
28
|
+
/**
|
|
29
|
+
* Optional: Release a previously acquired session lock.
|
|
30
|
+
*/
|
|
31
|
+
releaseLock?(sessionId: string): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=SessionDriver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionDriver.d.ts","sourceRoot":"","sources":["../../src/Contracts/SessionDriver.ts"],"names":[],"mappings":"AACA;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC1B;;OAEG;IACH,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IAE7D;;OAEG;IACH,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtF;;OAEG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1C;;;OAGG;IACH,EAAE,CAAC,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9C;;;OAGG;IACH,WAAW,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE1E;;OAEG;IACH,WAAW,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionDriver.js","sourceRoot":"","sources":["../../src/Contracts/SessionDriver.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handles reading and writing the session ID cookie.
|
|
3
|
+
*/
|
|
4
|
+
export declare class CookieSessionId {
|
|
5
|
+
private readonly cookieName;
|
|
6
|
+
private readonly secret;
|
|
7
|
+
constructor(cookieName: string, secret: string);
|
|
8
|
+
/**
|
|
9
|
+
* Generate a cryptographically random session ID.
|
|
10
|
+
*/
|
|
11
|
+
generate(): string;
|
|
12
|
+
/**
|
|
13
|
+
* Read the session ID from incoming cookie headers.
|
|
14
|
+
* Returns null if not found or signature is invalid.
|
|
15
|
+
*/
|
|
16
|
+
read(cookieHeader: string): string | null;
|
|
17
|
+
/**
|
|
18
|
+
* Produce a Set-Cookie header value for the session ID.
|
|
19
|
+
*/
|
|
20
|
+
write(sessionId: string, options: {
|
|
21
|
+
lifetime: number;
|
|
22
|
+
path: string;
|
|
23
|
+
secure: boolean;
|
|
24
|
+
httpOnly: boolean;
|
|
25
|
+
sameSite: string;
|
|
26
|
+
}): string;
|
|
27
|
+
/**
|
|
28
|
+
* Produce a Set-Cookie header that clears the session cookie.
|
|
29
|
+
*/
|
|
30
|
+
clear(path?: string): string;
|
|
31
|
+
private sign;
|
|
32
|
+
private verify;
|
|
33
|
+
private parseCookies;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=CookieSessionId.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CookieSessionId.d.ts","sourceRoot":"","sources":["../src/CookieSessionId.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,qBAAa,eAAe;IAEpB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM;IAGnC;;OAEG;IACI,QAAQ,IAAI,MAAM;IAIzB;;;OAGG;IACI,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAOhD;;OAEG;IACI,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE;QACrC,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE,OAAO,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;KACpB,GAAG,MAAM;IAaV;;OAEG;IACI,KAAK,CAAC,IAAI,GAAE,MAAY,GAAG,MAAM;IAIxC,OAAO,CAAC,IAAI;IAKZ,OAAO,CAAC,MAAM;IAgBd,OAAO,CAAC,YAAY;CAYvB"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.CookieSessionId = void 0;
|
|
37
|
+
const crypto = __importStar(require("node:crypto"));
|
|
38
|
+
const SESSION_ID_LENGTH = 40; // 40 hex chars = 20 bytes of entropy
|
|
39
|
+
/**
|
|
40
|
+
* Handles reading and writing the session ID cookie.
|
|
41
|
+
*/
|
|
42
|
+
class CookieSessionId {
|
|
43
|
+
constructor(cookieName, secret) {
|
|
44
|
+
this.cookieName = cookieName;
|
|
45
|
+
this.secret = secret;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Generate a cryptographically random session ID.
|
|
49
|
+
*/
|
|
50
|
+
generate() {
|
|
51
|
+
return crypto.randomBytes(20).toString('hex');
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Read the session ID from incoming cookie headers.
|
|
55
|
+
* Returns null if not found or signature is invalid.
|
|
56
|
+
*/
|
|
57
|
+
read(cookieHeader) {
|
|
58
|
+
const cookies = this.parseCookies(cookieHeader);
|
|
59
|
+
const raw = cookies[this.cookieName];
|
|
60
|
+
if (!raw)
|
|
61
|
+
return null;
|
|
62
|
+
return this.verify(raw);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Produce a Set-Cookie header value for the session ID.
|
|
66
|
+
*/
|
|
67
|
+
write(sessionId, options) {
|
|
68
|
+
const signed = this.sign(sessionId);
|
|
69
|
+
const parts = [
|
|
70
|
+
`${this.cookieName}=${encodeURIComponent(signed)}`,
|
|
71
|
+
`Path=${options.path}`,
|
|
72
|
+
`Max-Age=${options.lifetime}`,
|
|
73
|
+
`SameSite=${options.sameSite}`,
|
|
74
|
+
];
|
|
75
|
+
if (options.httpOnly)
|
|
76
|
+
parts.push('HttpOnly');
|
|
77
|
+
if (options.secure)
|
|
78
|
+
parts.push('Secure');
|
|
79
|
+
return parts.join('; ');
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Produce a Set-Cookie header that clears the session cookie.
|
|
83
|
+
*/
|
|
84
|
+
clear(path = '/') {
|
|
85
|
+
return `${this.cookieName}=; Path=${path}; Max-Age=0; HttpOnly; SameSite=Lax`;
|
|
86
|
+
}
|
|
87
|
+
sign(value) {
|
|
88
|
+
const mac = crypto.createHmac('sha256', this.secret).update(value).digest('base64url');
|
|
89
|
+
return `${value}.${mac}`;
|
|
90
|
+
}
|
|
91
|
+
verify(signed) {
|
|
92
|
+
const dot = signed.lastIndexOf('.');
|
|
93
|
+
if (dot === -1)
|
|
94
|
+
return null;
|
|
95
|
+
const value = signed.slice(0, dot);
|
|
96
|
+
const mac = signed.slice(dot + 1);
|
|
97
|
+
const expected = crypto.createHmac('sha256', this.secret).update(value).digest('base64url');
|
|
98
|
+
try {
|
|
99
|
+
if (!crypto.timingSafeEqual(Buffer.from(mac, 'utf8'), Buffer.from(expected, 'utf8'))) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
parseCookies(header) {
|
|
109
|
+
const result = {};
|
|
110
|
+
if (!header)
|
|
111
|
+
return result;
|
|
112
|
+
for (const pair of header.split(';')) {
|
|
113
|
+
const idx = pair.indexOf('=');
|
|
114
|
+
if (idx === -1)
|
|
115
|
+
continue;
|
|
116
|
+
const key = pair.slice(0, idx).trim();
|
|
117
|
+
const val = pair.slice(idx + 1).trim();
|
|
118
|
+
try {
|
|
119
|
+
result[key] = decodeURIComponent(val);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
result[key] = val;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
exports.CookieSessionId = CookieSessionId;
|
|
129
|
+
//# sourceMappingURL=CookieSessionId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CookieSessionId.js","sourceRoot":"","sources":["../src/CookieSessionId.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SessionDriver } from '../Contracts/SessionDriver';
|
|
2
|
+
/**
|
|
3
|
+
* DatabaseDriver — stores sessions using a SQL database table.
|
|
4
|
+
* Depends on the application's database instance implementing basic
|
|
5
|
+
* table(), where(), update(), insert(), and delete() methods.
|
|
6
|
+
*/
|
|
7
|
+
export declare class DatabaseDriver implements SessionDriver {
|
|
8
|
+
private readonly database;
|
|
9
|
+
private readonly table;
|
|
10
|
+
private readonly connection?;
|
|
11
|
+
constructor(database: any, table?: string, connection?: string | undefined);
|
|
12
|
+
load(sessionId: string): Promise<Record<string, any> | null>;
|
|
13
|
+
save(sessionId: string, data: Record<string, any>, ttlSeconds: number): Promise<void>;
|
|
14
|
+
destroy(sessionId: string): Promise<void>;
|
|
15
|
+
gc(maxLifetimeSeconds: number): Promise<void>;
|
|
16
|
+
acquireLock(sessionId: string, timeoutSeconds: number): Promise<boolean>;
|
|
17
|
+
releaseLock(sessionId: string): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=DatabaseDriver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatabaseDriver.d.ts","sourceRoot":"","sources":["../../src/Drivers/DatabaseDriver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE3D;;;;GAIG;AACH,qBAAa,cAAe,YAAW,aAAa;IAE5C,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;gBAFX,QAAQ,EAAE,GAAG,EACb,KAAK,GAAE,MAAmB,EAC1B,UAAU,CAAC,EAAE,MAAM,YAAA;IAG3B,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IAmB5D,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoCrF,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzC,EAAE,CAAC,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ7C,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOxE,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG7D"}
|