iron-session 2.0.0 → 6.0.3
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 +78 -22
- package/dist/index.js +0 -8
- package/dist/index.js.map +2 -2
- package/dist/index.mjs +0 -8
- package/dist/index.mjs.map +2 -2
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# iron-session [](https://github.com/vvo/iron-session/blob/master/LICENSE)
|
|
1
|
+
# iron-session [](https://github.com/vvo/iron-session/blob/master/LICENSE) [](https://github.com/vvo/iron-session/actions)  [](http://npm-stat.com/charts.html?package=iron-session)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center"><b>⭐️ Featured in the <a href="https://nextjs.org/docs/authentication">Next.js documentation</a></b></p>
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**⚠️ Nov 2021 update**: The library was renamed to `iron-session` and fully rewritten in TypeScript, it includes lots of new features and fixes. Follow the migration guide here: https://github.com/vvo/iron-session/releases/tag/v6.0.0.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
_🛠 Node.js stateless session utility using signed and encrypted cookies to store data. Works with Next.js, Express, NestJs, Fastify, and any Node.js HTTP framework._
|
|
8
8
|
|
|
9
9
|
The session data is stored in encrypted cookies ("seals"). And only your server can decode the session data. There are no session ids, making iron sessions "stateless" from the server point of view.
|
|
10
10
|
|
|
@@ -12,7 +12,7 @@ This strategy of storing session data is the same technique used by **frameworks
|
|
|
12
12
|
|
|
13
13
|
The underlying cryptography library is [iron](https://hapi.dev/module/iron) which was [created by the lead developer of OAuth 2.0](https://hueniversedotcom.wordpress.com/2015/09/19/auth-to-see-the-wizard-or-i-wrote-an-oauth-replacement/).
|
|
14
14
|
|
|
15
|
-
<p align="center"><b>Online demo at <a href="https://iron-session.vercel.app/">https://iron-session.vercel.app</a> 👀</b></p>
|
|
15
|
+
<p align="center"><b>Online demo at <a href="https://iron-session-example.vercel.app/">https://iron-session-example.vercel.app</a> 👀</b></p>
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -29,6 +29,7 @@ _Table of contents:_
|
|
|
29
29
|
- [Magic links](#magic-links)
|
|
30
30
|
- [Impersonation, login as someone else](#impersonation-login-as-someone-else)
|
|
31
31
|
- [Session cookies](#session-cookies)
|
|
32
|
+
- [Firebase usage](#firebase-usage)
|
|
32
33
|
- [API](#api)
|
|
33
34
|
- [ironOptions](#ironoptions)
|
|
34
35
|
- [Next.js: withIronSessionApiRoute(handler, ironOptions)](#nextjs-withironsessionapiroutehandler-ironoptions)
|
|
@@ -37,13 +38,13 @@ _Table of contents:_
|
|
|
37
38
|
- [session.save()](#sessionsave)
|
|
38
39
|
- [session.destroy()](#sessiondestroy)
|
|
39
40
|
- [FAQ](#faq)
|
|
40
|
-
- [Why use pure
|
|
41
|
+
- [Why use pure cookies for sessions?](#why-use-pure-cookies-for-sessions)
|
|
41
42
|
- [What are the drawbacks?](#what-are-the-drawbacks)
|
|
42
43
|
- [How is this different from JWT?](#how-is-this-different-from-jwt)
|
|
43
44
|
- [Project status](#project-status)
|
|
44
45
|
- [Credits](#credits)
|
|
45
|
-
- [
|
|
46
|
-
- [Contributors
|
|
46
|
+
- [References](#references)
|
|
47
|
+
- [Contributors](#contributors)
|
|
47
48
|
|
|
48
49
|
## Installation
|
|
49
50
|
|
|
@@ -81,6 +82,10 @@ export default withIronSessionApiRoute(
|
|
|
81
82
|
{
|
|
82
83
|
cookieName: "myapp_cookiename",
|
|
83
84
|
password: "complex_password_at_least_32_characters_long",
|
|
85
|
+
// secure: true should be used in production (HTTPS) but can't be used in development (HTTP)
|
|
86
|
+
cookieOptions: {
|
|
87
|
+
secure: process.env.NODE_ENV === "production",
|
|
88
|
+
},
|
|
84
89
|
},
|
|
85
90
|
);
|
|
86
91
|
```
|
|
@@ -99,6 +104,10 @@ export default withIronSessionApiRoute(
|
|
|
99
104
|
{
|
|
100
105
|
cookieName: "myapp_cookiename",
|
|
101
106
|
password: "complex_password_at_least_32_characters_long",
|
|
107
|
+
// secure: true should be used in production (HTTPS) but can't be used in development (HTTP)
|
|
108
|
+
cookieOptions: {
|
|
109
|
+
secure: process.env.NODE_ENV === "production",
|
|
110
|
+
},
|
|
102
111
|
},
|
|
103
112
|
);
|
|
104
113
|
```
|
|
@@ -118,6 +127,10 @@ export default withIronSessionApiRoute(
|
|
|
118
127
|
{
|
|
119
128
|
cookieName: "myapp_cookiename",
|
|
120
129
|
password: "complex_password_at_least_32_characters_long",
|
|
130
|
+
// secure: true should be used in production (HTTPS) but can't be used in development (HTTP)
|
|
131
|
+
cookieOptions: {
|
|
132
|
+
secure: process.env.NODE_ENV === "production",
|
|
133
|
+
},
|
|
121
134
|
},
|
|
122
135
|
);
|
|
123
136
|
```
|
|
@@ -148,6 +161,10 @@ export const getServerSideProps = withIronSessionSsr(
|
|
|
148
161
|
{
|
|
149
162
|
cookieName: "myapp_cookiename",
|
|
150
163
|
password: "complex_password_at_least_32_characters_long",
|
|
164
|
+
// secure: true should be used in production (HTTPS) but can't be used in development (HTTP)
|
|
165
|
+
cookieOptions: {
|
|
166
|
+
secure: process.env.NODE_ENV === "production",
|
|
167
|
+
},
|
|
151
168
|
},
|
|
152
169
|
);
|
|
153
170
|
```
|
|
@@ -186,6 +203,10 @@ async function loginRoute(req, res) {
|
|
|
186
203
|
export const ironOptions = {
|
|
187
204
|
cookieName: "myapp_cookiename",
|
|
188
205
|
password: "complex_password_at_least_32_characters_long",
|
|
206
|
+
// secure: true should be used in production (HTTPS) but can't be used in development (HTTP)
|
|
207
|
+
cookieOptions: {
|
|
208
|
+
secure: process.env.NODE_ENV === "production",
|
|
209
|
+
},
|
|
189
210
|
};
|
|
190
211
|
```
|
|
191
212
|
|
|
@@ -203,6 +224,10 @@ import { withIronSessionApiRoute, withIronSessionSsr } from "iron-session/next";
|
|
|
203
224
|
const sessionOptions = {
|
|
204
225
|
password: "complex_password_at_least_32_characters_long",
|
|
205
226
|
cookieName: "myapp_cookiename",
|
|
227
|
+
// secure: true should be used in production (HTTPS) but can't be used in development (HTTP)
|
|
228
|
+
cookieOptions: {
|
|
229
|
+
secure: process.env.NODE_ENV === "production",
|
|
230
|
+
},
|
|
206
231
|
};
|
|
207
232
|
|
|
208
233
|
export function withSessionRoute(handler) {
|
|
@@ -229,6 +254,10 @@ import {
|
|
|
229
254
|
const sessionOptions = {
|
|
230
255
|
password: "complex_password_at_least_32_characters_long",
|
|
231
256
|
cookieName: "myapp_cookiename",
|
|
257
|
+
// secure: true should be used in production (HTTPS) but can't be used in development (HTTP)
|
|
258
|
+
cookieOptions: {
|
|
259
|
+
secure: process.env.NODE_ENV === "production",
|
|
260
|
+
},
|
|
232
261
|
};
|
|
233
262
|
|
|
234
263
|
export function withSessionRoute(handler: NextApiHandler) {
|
|
@@ -252,9 +281,9 @@ export function withSessionSsr<
|
|
|
252
281
|
```ts
|
|
253
282
|
// pages/api/login.ts
|
|
254
283
|
|
|
255
|
-
import {
|
|
284
|
+
import { withSessionRoute } from "lib/withSession";
|
|
256
285
|
|
|
257
|
-
export default
|
|
286
|
+
export default withSessionRoute(loginRoute);
|
|
258
287
|
|
|
259
288
|
async function loginRoute(req, res) {
|
|
260
289
|
// get user from database then:
|
|
@@ -397,14 +426,18 @@ export default async function sendEmailRoute(req, res) {
|
|
|
397
426
|
import { unsealData } from "iron-session";
|
|
398
427
|
import { withIronSessionApiRoute } from "iron-session/next";
|
|
399
428
|
|
|
400
|
-
|
|
429
|
+
export default withIronSessionApiRoute(magicLoginRoute, {
|
|
430
|
+
cookieName: "myapp_cookiename",
|
|
401
431
|
password: "complex_password_at_least_32_characters_long",
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
432
|
+
cookieOptions: {
|
|
433
|
+
secure: process.env.NODE_ENV === "production",
|
|
434
|
+
},
|
|
435
|
+
});
|
|
405
436
|
|
|
406
437
|
async function magicLoginRoute(req, res) {
|
|
407
|
-
const { userId } = await unsealData(req.query.seal,
|
|
438
|
+
const { userId } = await unsealData(req.query.seal, {
|
|
439
|
+
password: "complex_password_at_least_32_characters_long",
|
|
440
|
+
});
|
|
408
441
|
|
|
409
442
|
const user = getUserFromDatabase(userId);
|
|
410
443
|
|
|
@@ -432,7 +465,11 @@ You may want to impersonate your own users, to check how they see your applicati
|
|
|
432
465
|
import { withIronSessionApiRoute } from "iron-session/next";
|
|
433
466
|
|
|
434
467
|
export default withIronSessionApiRoute(impersonateRoute, {
|
|
468
|
+
cookieName: "myapp_cookiename",
|
|
435
469
|
password: "complex_password_at_least_32_characters_long",
|
|
470
|
+
cookieOptions: {
|
|
471
|
+
secure: process.env.NODE_ENV === "production",
|
|
472
|
+
},
|
|
436
473
|
});
|
|
437
474
|
|
|
438
475
|
async function impersonateRoute(req, res) {
|
|
@@ -458,7 +495,11 @@ async function impersonateRoute(req, res) {
|
|
|
458
495
|
import { withIronSessionApiRoute } from "iron-session/next";
|
|
459
496
|
|
|
460
497
|
export default withIronSessionApiRoute(stopImpersonateRoute, {
|
|
498
|
+
cookieName: "myapp_cookiename",
|
|
461
499
|
password: "complex_password_at_least_32_characters_long",
|
|
500
|
+
cookieOptions: {
|
|
501
|
+
secure: process.env.NODE_ENV === "production",
|
|
502
|
+
},
|
|
462
503
|
});
|
|
463
504
|
|
|
464
505
|
async function stopImpersonateRoute(req, res) {
|
|
@@ -490,13 +531,20 @@ export default withIronSessionApiRoute(
|
|
|
490
531
|
{
|
|
491
532
|
cookieName: "myapp_cookiename",
|
|
492
533
|
password: "complex_password_at_least_32_characters_long",
|
|
493
|
-
cookieOptions: {
|
|
534
|
+
cookieOptions: {
|
|
535
|
+
maxAge: undefined,
|
|
536
|
+
secure: process.env.NODE_ENV === "production",
|
|
537
|
+
},
|
|
494
538
|
},
|
|
495
539
|
);
|
|
496
540
|
```
|
|
497
541
|
|
|
498
542
|
Beware, modern browsers might not delete cookies at all using this technique because of [session restoring](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#define_the_lifetime_of_a_cookie).
|
|
499
543
|
|
|
544
|
+
### Firebase usage
|
|
545
|
+
|
|
546
|
+
This library can be used with Firebase, as long as you set the cookie name to `__session` which seems to be the only valid cookie name there.
|
|
547
|
+
|
|
500
548
|
## API
|
|
501
549
|
|
|
502
550
|
### ironOptions
|
|
@@ -537,6 +585,10 @@ export default withIronSessionApiRoute(
|
|
|
537
585
|
{
|
|
538
586
|
cookieName: "myapp_cookiename",
|
|
539
587
|
password: "complex_password_at_least_32_characters_long",
|
|
588
|
+
// secure: true should be used in production (HTTPS) but can't be used in development (HTTP)
|
|
589
|
+
cookieOptions: {
|
|
590
|
+
secure: process.env.NODE_ENV === "production",
|
|
591
|
+
},
|
|
540
592
|
},
|
|
541
593
|
);
|
|
542
594
|
```
|
|
@@ -559,6 +611,10 @@ export const getServerSideProps = withIronSessionSsr(
|
|
|
559
611
|
{
|
|
560
612
|
cookieName: "myapp_cookiename",
|
|
561
613
|
password: "complex_password_at_least_32_characters_long",
|
|
614
|
+
// secure: true should be used in production (HTTPS) but can't be used in development (HTTP)
|
|
615
|
+
cookieOptions: {
|
|
616
|
+
secure: process.env.NODE_ENV === "production",
|
|
617
|
+
},
|
|
562
618
|
},
|
|
563
619
|
);
|
|
564
620
|
```
|
|
@@ -568,7 +624,7 @@ export const getServerSideProps = withIronSessionSsr(
|
|
|
568
624
|
Creates an express middleware that adds a `session` object to the request.
|
|
569
625
|
|
|
570
626
|
```js
|
|
571
|
-
import { ironSession } from "iron-session";
|
|
627
|
+
import { ironSession } from "iron-session/express";
|
|
572
628
|
|
|
573
629
|
app.use(ironSession(ironOptions));
|
|
574
630
|
```
|
|
@@ -585,11 +641,11 @@ await req.session.save();
|
|
|
585
641
|
|
|
586
642
|
Empties the session object and sets the cookie header to be sent once the response is sent. The browser will then remove the cookie automatically.
|
|
587
643
|
|
|
588
|
-
|
|
644
|
+
You don't have to call `req.session.save()` after calling `req.session.destroy()`. The session is saved automatically.
|
|
589
645
|
|
|
590
646
|
## FAQ
|
|
591
647
|
|
|
592
|
-
### Why use pure
|
|
648
|
+
### Why use pure cookies for sessions?
|
|
593
649
|
|
|
594
650
|
This makes your sessions stateless: you do not have to store session data on your server. You do not need another server or service to store session data. This is particularly useful in serverless architectures where you're trying to reduce your backend dependencies.
|
|
595
651
|
|
|
@@ -599,7 +655,7 @@ There are some drawbacks to this approach:
|
|
|
599
655
|
|
|
600
656
|
- you cannot invalidate a seal when needed because there's no state stored on the server-side about them. We consider that the way the cookie is stored reduces the possibility for this eventuality to happen. Also, in most applications the first thing you do when receiving an authenticated request is to validate the user and their rights in your database, which defeats the case where someone would try to use a token while their account was deactivated/deleted. Now if someone steals a user token you should have a process in place to mitigate that: deactivate the user and force a re-login with a flag in your database for example.
|
|
601
657
|
- application not supporting cookies won't work, but you can use [iron-store](https://github.com/vvo/iron-store/) to implement something similar. In the future, we could allow `iron-session` to accept [basic auth](https://tools.ietf.org/html/rfc7617) or bearer token methods too. Open an issue if you're interested.
|
|
602
|
-
- on most browsers, you're limited to 4,096 bytes per cookie. To give you an idea,
|
|
658
|
+
- on most browsers, you're limited to 4,096 bytes per cookie. To give you an idea, an `iron-session` cookie containing `{user: {id: 100}}` is 265 bytes signed and encrypted: still plenty of available cookie space in here.
|
|
603
659
|
- performance: crypto on the server-side could be slow, if that's the case let me know. Also, cookies are sent to every request to your website, even images, so this could be an issue
|
|
604
660
|
|
|
605
661
|
Now that you know the drawbacks, you can decide if they are an issue for your application or not.
|
|
@@ -625,12 +681,12 @@ Thanks to [Hoang Vo](https://github.com/hoangvvo) for advice and guidance while
|
|
|
625
681
|
|
|
626
682
|
Thanks to [hapi](https://hapi.dev/) team for creating [iron](https://github.com/hapijs/iron).
|
|
627
683
|
|
|
628
|
-
##
|
|
684
|
+
## References
|
|
629
685
|
|
|
630
686
|
- https://owasp.org/www-project-cheat-sheets/cheatsheets/Session_Management_Cheat_Sheet.html#cookies
|
|
631
687
|
- https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#samesite-cookie-attribute
|
|
632
688
|
|
|
633
|
-
## Contributors
|
|
689
|
+
## Contributors
|
|
634
690
|
|
|
635
691
|
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
|
636
692
|
|
package/dist/index.js
CHANGED
|
@@ -44,7 +44,6 @@ var defaultOptions = {
|
|
|
44
44
|
}
|
|
45
45
|
};
|
|
46
46
|
async function getIronSession(req, res, userSessionOptions) {
|
|
47
|
-
var _a, _b;
|
|
48
47
|
if (!req || !res || !userSessionOptions || !userSessionOptions.cookieName || !userSessionOptions.password) {
|
|
49
48
|
throw new Error(`iron-session: Bad usage. Minimum usage is const session = await getIronSession(req, res, { cookieName: "...", password: "...". Check the usage here: https://github.com/vvo/iron-session`);
|
|
50
49
|
}
|
|
@@ -54,10 +53,6 @@ async function getIronSession(req, res, userSessionOptions) {
|
|
|
54
53
|
throw new Error(`iron-session: Bad usage. Password must be at least 32 characters long.`);
|
|
55
54
|
}
|
|
56
55
|
});
|
|
57
|
-
const isHttps = req.socket.encrypted === true;
|
|
58
|
-
if (((_a = userSessionOptions.cookieOptions) == null ? void 0 : _a.secure) === true && isHttps === false) {
|
|
59
|
-
throw new Error(`iron-session: Can't use secure cookies when not in https. See usage at https://github.com/vvo/iron-session/`);
|
|
60
|
-
}
|
|
61
56
|
const options = {
|
|
62
57
|
...defaultOptions,
|
|
63
58
|
...userSessionOptions,
|
|
@@ -66,9 +61,6 @@ async function getIronSession(req, res, userSessionOptions) {
|
|
|
66
61
|
...userSessionOptions.cookieOptions || {}
|
|
67
62
|
}
|
|
68
63
|
};
|
|
69
|
-
if (((_b = userSessionOptions.cookieOptions) == null ? void 0 : _b.secure) === void 0) {
|
|
70
|
-
options.cookieOptions.secure = isHttps;
|
|
71
|
-
}
|
|
72
64
|
if (options.ttl === 0) {
|
|
73
65
|
options.ttl = 2147483647;
|
|
74
66
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["import Iron from \"@hapi/iron\";\nimport type { CookieSerializeOptions } from \"cookie\";\nimport { parse as parseCookie, serialize as serializeCookie } from \"cookie\";\nimport type { IncomingMessage, ServerResponse } from \"http\";\nimport type { TLSSocket } from \"tls\";\n\n// default time allowed to check for iron seal validity when ttl passed\n// see https://hapi.dev/family/iron/api/?v=6.0.0#options\nconst timestampSkewSec = 60;\n\ntype passwordsMap = { [id: string]: string };\ntype password = string | passwordsMap;\n\nconst fourteenDaysInSeconds = 15 * 24 * 3600;\n\n// We store a token major version to handle data format changes when any. So that when you upgrade the cookies\n// can be kept alive between upgrades, no need to disconnect everyone.\nconst currentMajorVersion = 2;\nconst versionDelimiter = \"~\";\n\nconst defaultOptions: {\n ttl: number;\n cookieOptions: CookieSerializeOptions;\n} = {\n ttl: fourteenDaysInSeconds,\n cookieOptions: {\n httpOnly: true,\n secure: true,\n sameSite: \"lax\",\n path: \"/\",\n },\n};\n\nexport interface IronSessionOptions {\n /**\n * This is the cookie name that will be used inside the browser. You should make sure it's unique given\n * your application. Example: vercel-session\n */\n cookieName: string;\n\n /**\n * This is the password(s) that will be used to encrypt the cookie. It can be either a string or an object\n * like {1: \"password\", 2: password}.\n *\n * When you provide multiple passwords then all of them will be used to decrypt the cookie and only the most\n * recent (= highest key, 2 in this example) password will be used to encrypt the cookie. This allow you\n * to use password rotation (security)\n */\n password: password;\n\n /**\n * This is the time in seconds that the session will be valid for. This also set the max-age attribute of\n * the cookie automatically (minus 60 seconds so that the cookie always expire before the session).\n */\n ttl?: number;\n\n /**\n * This is the options that will be passed to the cookie library.\n * You can see all of them here: https://github.com/jshttp/cookie#options-1.\n *\n * If you want to use \"session cookies\" (cookies that are deleted when the browser is closed) then you need\n * to pass cookieOptions: { maxAge: undefined }.\n */\n cookieOptions?: CookieSerializeOptions;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface IronSessionData {\n // If we allow for any keys, then there's no more type check on unknown properties which is not good\n // If we allow for any keys, the later delete will work but I prefer to disable the check at this stage and\n // provide good type checking instead\n // [key: string]: unknown;\n}\n\nexport type IronSession = IronSessionData & {\n /**\n * Destroys the session data and removes the cookie.\n */\n destroy: () => void;\n\n /**\n * Encrypts the session data and sets the cookie.\n */\n save: () => Promise<void>;\n};\n\ndeclare module \"http\" {\n interface IncomingMessage {\n session: IronSession;\n }\n}\n\nexport async function getIronSession(\n req: IncomingMessage,\n res: ServerResponse,\n userSessionOptions: IronSessionOptions,\n): Promise<IronSession> {\n if (\n !req ||\n !res ||\n !userSessionOptions ||\n !userSessionOptions.cookieName ||\n !userSessionOptions.password\n ) {\n throw new Error(\n `iron-session: Bad usage. Minimum usage is const session = await getIronSession(req, res, { cookieName: \"...\", password: \"...\". Check the usage here: https://github.com/vvo/iron-session`,\n );\n }\n\n const passwordsAsMap = normalizeStringPasswordToMap(\n userSessionOptions.password,\n );\n\n Object.values(\n normalizeStringPasswordToMap(userSessionOptions.password),\n ).forEach((password) => {\n if (password.length < 32) {\n throw new Error(\n `iron-session: Bad usage. Password must be at least 32 characters long.`,\n );\n }\n });\n\n const isHttps = (req.socket as TLSSocket).encrypted === true;\n\n if (userSessionOptions.cookieOptions?.secure === true && isHttps === false) {\n throw new Error(\n `iron-session: Can't use secure cookies when not in https. See usage at https://github.com/vvo/iron-session/`,\n );\n }\n\n const options: Required<IronSessionOptions> = {\n ...defaultOptions,\n ...userSessionOptions,\n cookieOptions: {\n ...defaultOptions.cookieOptions,\n ...(userSessionOptions.cookieOptions || {}),\n },\n };\n\n // if the user did not set the secure flag themselves, we automatically configure it\n if (userSessionOptions.cookieOptions?.secure === undefined) {\n options.cookieOptions.secure = isHttps;\n }\n\n if (options.ttl === 0) {\n // ttl = 0 means no expiration\n // but in reality cookies have to expire (can't have no max-age)\n // 2147483647 is the max value for max-age in cookies\n // see https://stackoverflow.com/a/11685301/147079\n options.ttl = 2147483647;\n }\n\n if (\n userSessionOptions.cookieOptions &&\n \"maxAge\" in userSessionOptions.cookieOptions\n ) {\n // session cookie, do not set maxAge, consider token as infinite\n if (userSessionOptions.cookieOptions.maxAge === undefined) {\n options.ttl = 0;\n } else {\n options.cookieOptions.maxAge = computeCookieMaxAge(\n userSessionOptions.cookieOptions.maxAge,\n );\n }\n } else {\n options.cookieOptions.maxAge = computeCookieMaxAge(options.ttl);\n }\n\n const sealFromCookies = parseCookie(req.headers.cookie || \"\")[\n options.cookieName\n ];\n\n const session =\n sealFromCookies === undefined\n ? {}\n : await unsealData<IronSessionData>(sealFromCookies, {\n password: passwordsAsMap,\n ttl: options.ttl,\n });\n\n Object.defineProperties(session, {\n save: {\n value: async function save() {\n if (res.headersSent === true) {\n throw new Error(\n `iron-session: Cannot set session cookie: session.save() was called after headers were sent. Make sure to call it before any res.send() or res.end()`,\n );\n }\n const seal = await sealData(session, {\n password: passwordsAsMap,\n ttl: options.ttl,\n });\n const cookieValue = serializeCookie(\n options.cookieName,\n seal,\n options.cookieOptions,\n );\n\n if (cookieValue.length > 4096) {\n throw new Error(\n `iron-session: Cookie length is too big ${cookieValue.length}, browsers will refuse it. Try to remove some data.`,\n );\n }\n\n addToCookies(cookieValue, res);\n },\n },\n destroy: {\n value: function destroy() {\n Object.keys(session).forEach((key) => {\n // @ts-ignore See comment on the IronSessionData interface\n delete session[key];\n });\n\n const cookieValue = serializeCookie(options.cookieName, \"\", {\n ...options.cookieOptions,\n maxAge: 0,\n });\n addToCookies(cookieValue, res);\n },\n },\n });\n\n return session as IronSession;\n}\n\nfunction addToCookies(cookieValue: string, res: ServerResponse) {\n let existingSetCookie =\n (res.getHeader(\"set-cookie\") as string[] | string) ?? [];\n if (typeof existingSetCookie === \"string\") {\n existingSetCookie = [existingSetCookie];\n }\n res.setHeader(\"set-cookie\", [...existingSetCookie, cookieValue]);\n}\n\nfunction computeCookieMaxAge(ttl: number) {\n // The next line makes sure browser will expire cookies before seals are considered expired by the server.\n // It also allows for clock difference of 60 seconds maximum between server and clients.\n // It also makes sure to expire the cookie immediately when value is 0\n return ttl - timestampSkewSec;\n}\n\nexport async function unsealData<T = Record<string, unknown>>(\n seal: string,\n {\n password,\n ttl = fourteenDaysInSeconds,\n }: { password: password; ttl?: number },\n): Promise<T> {\n const passwordsAsMap = normalizeStringPasswordToMap(password);\n const { sealWithoutVersion, tokenVersion } = parseSeal(seal);\n\n try {\n const data = await Iron.unseal(sealWithoutVersion, passwordsAsMap, {\n ...Iron.defaults,\n ttl: ttl * 1000,\n });\n\n if (tokenVersion === 2) {\n return data;\n }\n\n return {\n ...data.persistent,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (\n error.message === \"Expired seal\" ||\n error.message === \"Bad hmac value\" ||\n error.message === \"Cannot find password: \" ||\n error.message === \"Incorrect number of sealed components\"\n ) {\n // if seal expired or\n // if seal is not valid (encrypted using a different password, when passwords are badly rotated) or\n // if we can't find back the password in the seal\n // then we just start a new session over\n return {} as T;\n }\n }\n\n throw error;\n }\n}\n\nfunction parseSeal(seal: string): {\n sealWithoutVersion: string;\n tokenVersion: number | null;\n} {\n if (seal[seal.length - 2] === versionDelimiter) {\n const [sealWithoutVersion, tokenVersionAsString] =\n seal.split(versionDelimiter);\n return {\n sealWithoutVersion,\n tokenVersion: parseInt(tokenVersionAsString, 10),\n };\n }\n\n return { sealWithoutVersion: seal, tokenVersion: null };\n}\n\nexport async function sealData(\n data: unknown,\n {\n password,\n ttl = fourteenDaysInSeconds,\n }: { password: password; ttl?: number },\n) {\n const passwordsAsMap = normalizeStringPasswordToMap(password);\n\n const mostRecentPasswordId = Math.max(\n ...Object.keys(passwordsAsMap).map((id) => parseInt(id, 10)),\n );\n\n const passwordForSeal = {\n id: mostRecentPasswordId.toString(),\n secret: passwordsAsMap[mostRecentPasswordId],\n };\n\n const seal = await Iron.seal(data, passwordForSeal, {\n ...Iron.defaults,\n ttl: ttl * 1000,\n });\n\n return `${seal}${versionDelimiter}${currentMajorVersion}`;\n}\n\nfunction normalizeStringPasswordToMap(password: password) {\n return typeof password === \"string\" ? { 1: password } : password;\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAiB;AAEjB,oBAAmE;
|
|
4
|
+
"sourcesContent": ["import Iron from \"@hapi/iron\";\nimport type { CookieSerializeOptions } from \"cookie\";\nimport { parse as parseCookie, serialize as serializeCookie } from \"cookie\";\nimport type { IncomingMessage, ServerResponse } from \"http\";\n\n// default time allowed to check for iron seal validity when ttl passed\n// see https://hapi.dev/family/iron/api/?v=6.0.0#options\nconst timestampSkewSec = 60;\n\ntype passwordsMap = { [id: string]: string };\ntype password = string | passwordsMap;\n\nconst fourteenDaysInSeconds = 15 * 24 * 3600;\n\n// We store a token major version to handle data format changes when any. So that when you upgrade the cookies\n// can be kept alive between upgrades, no need to disconnect everyone.\nconst currentMajorVersion = 2;\nconst versionDelimiter = \"~\";\n\nconst defaultOptions: {\n ttl: number;\n cookieOptions: CookieSerializeOptions;\n} = {\n ttl: fourteenDaysInSeconds,\n cookieOptions: {\n httpOnly: true,\n secure: true,\n sameSite: \"lax\",\n path: \"/\",\n },\n};\n\nexport interface IronSessionOptions {\n /**\n * This is the cookie name that will be used inside the browser. You should make sure it's unique given\n * your application. Example: vercel-session\n */\n cookieName: string;\n\n /**\n * This is the password(s) that will be used to encrypt the cookie. It can be either a string or an object\n * like {1: \"password\", 2: password}.\n *\n * When you provide multiple passwords then all of them will be used to decrypt the cookie and only the most\n * recent (= highest key, 2 in this example) password will be used to encrypt the cookie. This allow you\n * to use password rotation (security)\n */\n password: password;\n\n /**\n * This is the time in seconds that the session will be valid for. This also set the max-age attribute of\n * the cookie automatically (minus 60 seconds so that the cookie always expire before the session).\n */\n ttl?: number;\n\n /**\n * This is the options that will be passed to the cookie library.\n * You can see all of them here: https://github.com/jshttp/cookie#options-1.\n *\n * If you want to use \"session cookies\" (cookies that are deleted when the browser is closed) then you need\n * to pass cookieOptions: { maxAge: undefined }.\n */\n cookieOptions?: CookieSerializeOptions;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface IronSessionData {\n // If we allow for any keys, then there's no more type check on unknown properties\n // which is not good\n // If we allow for any keys, the later delete will work but I prefer to disable the\n // check at this stage and\n // provide good type checking instead\n // [key: string]: unknown;\n}\n\nexport type IronSession = IronSessionData & {\n /**\n * Destroys the session data and removes the cookie.\n */\n destroy: () => void;\n\n /**\n * Encrypts the session data and sets the cookie.\n */\n save: () => Promise<void>;\n};\n\ndeclare module \"http\" {\n interface IncomingMessage {\n session: IronSession;\n }\n}\n\nexport async function getIronSession(\n req: IncomingMessage,\n res: ServerResponse,\n userSessionOptions: IronSessionOptions,\n): Promise<IronSession> {\n if (\n !req ||\n !res ||\n !userSessionOptions ||\n !userSessionOptions.cookieName ||\n !userSessionOptions.password\n ) {\n throw new Error(\n `iron-session: Bad usage. Minimum usage is const session = await getIronSession(req, res, { cookieName: \"...\", password: \"...\". Check the usage here: https://github.com/vvo/iron-session`,\n );\n }\n\n const passwordsAsMap = normalizeStringPasswordToMap(\n userSessionOptions.password,\n );\n\n Object.values(\n normalizeStringPasswordToMap(userSessionOptions.password),\n ).forEach((password) => {\n if (password.length < 32) {\n throw new Error(\n `iron-session: Bad usage. Password must be at least 32 characters long.`,\n );\n }\n });\n\n const options: Required<IronSessionOptions> = {\n ...defaultOptions,\n ...userSessionOptions,\n cookieOptions: {\n ...defaultOptions.cookieOptions,\n ...(userSessionOptions.cookieOptions || {}),\n },\n };\n\n if (options.ttl === 0) {\n // ttl = 0 means no expiration\n // but in reality cookies have to expire (can't have no max-age)\n // 2147483647 is the max value for max-age in cookies\n // see https://stackoverflow.com/a/11685301/147079\n options.ttl = 2147483647;\n }\n\n if (\n userSessionOptions.cookieOptions &&\n \"maxAge\" in userSessionOptions.cookieOptions\n ) {\n // session cookie, do not set maxAge, consider token as infinite\n if (userSessionOptions.cookieOptions.maxAge === undefined) {\n options.ttl = 0;\n } else {\n options.cookieOptions.maxAge = computeCookieMaxAge(\n userSessionOptions.cookieOptions.maxAge,\n );\n }\n } else {\n options.cookieOptions.maxAge = computeCookieMaxAge(options.ttl);\n }\n\n const sealFromCookies = parseCookie(req.headers.cookie || \"\")[\n options.cookieName\n ];\n\n const session =\n sealFromCookies === undefined\n ? {}\n : await unsealData<IronSessionData>(sealFromCookies, {\n password: passwordsAsMap,\n ttl: options.ttl,\n });\n\n Object.defineProperties(session, {\n save: {\n value: async function save() {\n if (res.headersSent === true) {\n throw new Error(\n `iron-session: Cannot set session cookie: session.save() was called after headers were sent. Make sure to call it before any res.send() or res.end()`,\n );\n }\n const seal = await sealData(session, {\n password: passwordsAsMap,\n ttl: options.ttl,\n });\n const cookieValue = serializeCookie(\n options.cookieName,\n seal,\n options.cookieOptions,\n );\n\n if (cookieValue.length > 4096) {\n throw new Error(\n `iron-session: Cookie length is too big ${cookieValue.length}, browsers will refuse it. Try to remove some data.`,\n );\n }\n\n addToCookies(cookieValue, res);\n },\n },\n destroy: {\n value: function destroy() {\n Object.keys(session).forEach((key) => {\n // @ts-ignore See comment on the IronSessionData interface\n delete session[key];\n });\n\n const cookieValue = serializeCookie(options.cookieName, \"\", {\n ...options.cookieOptions,\n maxAge: 0,\n });\n addToCookies(cookieValue, res);\n },\n },\n });\n\n return session as IronSession;\n}\n\nfunction addToCookies(cookieValue: string, res: ServerResponse) {\n let existingSetCookie =\n (res.getHeader(\"set-cookie\") as string[] | string) ?? [];\n if (typeof existingSetCookie === \"string\") {\n existingSetCookie = [existingSetCookie];\n }\n res.setHeader(\"set-cookie\", [...existingSetCookie, cookieValue]);\n}\n\nfunction computeCookieMaxAge(ttl: number) {\n // The next line makes sure browser will expire cookies before seals are considered expired by the server.\n // It also allows for clock difference of 60 seconds maximum between server and clients.\n // It also makes sure to expire the cookie immediately when value is 0\n return ttl - timestampSkewSec;\n}\n\nexport async function unsealData<T = Record<string, unknown>>(\n seal: string,\n {\n password,\n ttl = fourteenDaysInSeconds,\n }: { password: password; ttl?: number },\n): Promise<T> {\n const passwordsAsMap = normalizeStringPasswordToMap(password);\n const { sealWithoutVersion, tokenVersion } = parseSeal(seal);\n\n try {\n const data = await Iron.unseal(sealWithoutVersion, passwordsAsMap, {\n ...Iron.defaults,\n ttl: ttl * 1000,\n });\n\n if (tokenVersion === 2) {\n return data;\n }\n\n return {\n ...data.persistent,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (\n error.message === \"Expired seal\" ||\n error.message === \"Bad hmac value\" ||\n error.message === \"Cannot find password: \" ||\n error.message === \"Incorrect number of sealed components\"\n ) {\n // if seal expired or\n // if seal is not valid (encrypted using a different password, when passwords are badly rotated) or\n // if we can't find back the password in the seal\n // then we just start a new session over\n return {} as T;\n }\n }\n\n throw error;\n }\n}\n\nfunction parseSeal(seal: string): {\n sealWithoutVersion: string;\n tokenVersion: number | null;\n} {\n if (seal[seal.length - 2] === versionDelimiter) {\n const [sealWithoutVersion, tokenVersionAsString] =\n seal.split(versionDelimiter);\n return {\n sealWithoutVersion,\n tokenVersion: parseInt(tokenVersionAsString, 10),\n };\n }\n\n return { sealWithoutVersion: seal, tokenVersion: null };\n}\n\nexport async function sealData(\n data: unknown,\n {\n password,\n ttl = fourteenDaysInSeconds,\n }: { password: password; ttl?: number },\n) {\n const passwordsAsMap = normalizeStringPasswordToMap(password);\n\n const mostRecentPasswordId = Math.max(\n ...Object.keys(passwordsAsMap).map((id) => parseInt(id, 10)),\n );\n\n const passwordForSeal = {\n id: mostRecentPasswordId.toString(),\n secret: passwordsAsMap[mostRecentPasswordId],\n };\n\n const seal = await Iron.seal(data, passwordForSeal, {\n ...Iron.defaults,\n ttl: ttl * 1000,\n });\n\n return `${seal}${versionDelimiter}${currentMajorVersion}`;\n}\n\nfunction normalizeStringPasswordToMap(password: password) {\n return typeof password === \"string\" ? { 1: password } : password;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -15,7 +15,6 @@ var defaultOptions = {
|
|
|
15
15
|
}
|
|
16
16
|
};
|
|
17
17
|
async function getIronSession(req, res, userSessionOptions) {
|
|
18
|
-
var _a, _b;
|
|
19
18
|
if (!req || !res || !userSessionOptions || !userSessionOptions.cookieName || !userSessionOptions.password) {
|
|
20
19
|
throw new Error(`iron-session: Bad usage. Minimum usage is const session = await getIronSession(req, res, { cookieName: "...", password: "...". Check the usage here: https://github.com/vvo/iron-session`);
|
|
21
20
|
}
|
|
@@ -25,10 +24,6 @@ async function getIronSession(req, res, userSessionOptions) {
|
|
|
25
24
|
throw new Error(`iron-session: Bad usage. Password must be at least 32 characters long.`);
|
|
26
25
|
}
|
|
27
26
|
});
|
|
28
|
-
const isHttps = req.socket.encrypted === true;
|
|
29
|
-
if (((_a = userSessionOptions.cookieOptions) == null ? void 0 : _a.secure) === true && isHttps === false) {
|
|
30
|
-
throw new Error(`iron-session: Can't use secure cookies when not in https. See usage at https://github.com/vvo/iron-session/`);
|
|
31
|
-
}
|
|
32
27
|
const options = {
|
|
33
28
|
...defaultOptions,
|
|
34
29
|
...userSessionOptions,
|
|
@@ -37,9 +32,6 @@ async function getIronSession(req, res, userSessionOptions) {
|
|
|
37
32
|
...userSessionOptions.cookieOptions || {}
|
|
38
33
|
}
|
|
39
34
|
};
|
|
40
|
-
if (((_b = userSessionOptions.cookieOptions) == null ? void 0 : _b.secure) === void 0) {
|
|
41
|
-
options.cookieOptions.secure = isHttps;
|
|
42
|
-
}
|
|
43
35
|
if (options.ttl === 0) {
|
|
44
36
|
options.ttl = 2147483647;
|
|
45
37
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["import Iron from \"@hapi/iron\";\nimport type { CookieSerializeOptions } from \"cookie\";\nimport { parse as parseCookie, serialize as serializeCookie } from \"cookie\";\nimport type { IncomingMessage, ServerResponse } from \"http\";\nimport type { TLSSocket } from \"tls\";\n\n// default time allowed to check for iron seal validity when ttl passed\n// see https://hapi.dev/family/iron/api/?v=6.0.0#options\nconst timestampSkewSec = 60;\n\ntype passwordsMap = { [id: string]: string };\ntype password = string | passwordsMap;\n\nconst fourteenDaysInSeconds = 15 * 24 * 3600;\n\n// We store a token major version to handle data format changes when any. So that when you upgrade the cookies\n// can be kept alive between upgrades, no need to disconnect everyone.\nconst currentMajorVersion = 2;\nconst versionDelimiter = \"~\";\n\nconst defaultOptions: {\n ttl: number;\n cookieOptions: CookieSerializeOptions;\n} = {\n ttl: fourteenDaysInSeconds,\n cookieOptions: {\n httpOnly: true,\n secure: true,\n sameSite: \"lax\",\n path: \"/\",\n },\n};\n\nexport interface IronSessionOptions {\n /**\n * This is the cookie name that will be used inside the browser. You should make sure it's unique given\n * your application. Example: vercel-session\n */\n cookieName: string;\n\n /**\n * This is the password(s) that will be used to encrypt the cookie. It can be either a string or an object\n * like {1: \"password\", 2: password}.\n *\n * When you provide multiple passwords then all of them will be used to decrypt the cookie and only the most\n * recent (= highest key, 2 in this example) password will be used to encrypt the cookie. This allow you\n * to use password rotation (security)\n */\n password: password;\n\n /**\n * This is the time in seconds that the session will be valid for. This also set the max-age attribute of\n * the cookie automatically (minus 60 seconds so that the cookie always expire before the session).\n */\n ttl?: number;\n\n /**\n * This is the options that will be passed to the cookie library.\n * You can see all of them here: https://github.com/jshttp/cookie#options-1.\n *\n * If you want to use \"session cookies\" (cookies that are deleted when the browser is closed) then you need\n * to pass cookieOptions: { maxAge: undefined }.\n */\n cookieOptions?: CookieSerializeOptions;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface IronSessionData {\n // If we allow for any keys, then there's no more type check on unknown properties which is not good\n // If we allow for any keys, the later delete will work but I prefer to disable the check at this stage and\n // provide good type checking instead\n // [key: string]: unknown;\n}\n\nexport type IronSession = IronSessionData & {\n /**\n * Destroys the session data and removes the cookie.\n */\n destroy: () => void;\n\n /**\n * Encrypts the session data and sets the cookie.\n */\n save: () => Promise<void>;\n};\n\ndeclare module \"http\" {\n interface IncomingMessage {\n session: IronSession;\n }\n}\n\nexport async function getIronSession(\n req: IncomingMessage,\n res: ServerResponse,\n userSessionOptions: IronSessionOptions,\n): Promise<IronSession> {\n if (\n !req ||\n !res ||\n !userSessionOptions ||\n !userSessionOptions.cookieName ||\n !userSessionOptions.password\n ) {\n throw new Error(\n `iron-session: Bad usage. Minimum usage is const session = await getIronSession(req, res, { cookieName: \"...\", password: \"...\". Check the usage here: https://github.com/vvo/iron-session`,\n );\n }\n\n const passwordsAsMap = normalizeStringPasswordToMap(\n userSessionOptions.password,\n );\n\n Object.values(\n normalizeStringPasswordToMap(userSessionOptions.password),\n ).forEach((password) => {\n if (password.length < 32) {\n throw new Error(\n `iron-session: Bad usage. Password must be at least 32 characters long.`,\n );\n }\n });\n\n const isHttps = (req.socket as TLSSocket).encrypted === true;\n\n if (userSessionOptions.cookieOptions?.secure === true && isHttps === false) {\n throw new Error(\n `iron-session: Can't use secure cookies when not in https. See usage at https://github.com/vvo/iron-session/`,\n );\n }\n\n const options: Required<IronSessionOptions> = {\n ...defaultOptions,\n ...userSessionOptions,\n cookieOptions: {\n ...defaultOptions.cookieOptions,\n ...(userSessionOptions.cookieOptions || {}),\n },\n };\n\n // if the user did not set the secure flag themselves, we automatically configure it\n if (userSessionOptions.cookieOptions?.secure === undefined) {\n options.cookieOptions.secure = isHttps;\n }\n\n if (options.ttl === 0) {\n // ttl = 0 means no expiration\n // but in reality cookies have to expire (can't have no max-age)\n // 2147483647 is the max value for max-age in cookies\n // see https://stackoverflow.com/a/11685301/147079\n options.ttl = 2147483647;\n }\n\n if (\n userSessionOptions.cookieOptions &&\n \"maxAge\" in userSessionOptions.cookieOptions\n ) {\n // session cookie, do not set maxAge, consider token as infinite\n if (userSessionOptions.cookieOptions.maxAge === undefined) {\n options.ttl = 0;\n } else {\n options.cookieOptions.maxAge = computeCookieMaxAge(\n userSessionOptions.cookieOptions.maxAge,\n );\n }\n } else {\n options.cookieOptions.maxAge = computeCookieMaxAge(options.ttl);\n }\n\n const sealFromCookies = parseCookie(req.headers.cookie || \"\")[\n options.cookieName\n ];\n\n const session =\n sealFromCookies === undefined\n ? {}\n : await unsealData<IronSessionData>(sealFromCookies, {\n password: passwordsAsMap,\n ttl: options.ttl,\n });\n\n Object.defineProperties(session, {\n save: {\n value: async function save() {\n if (res.headersSent === true) {\n throw new Error(\n `iron-session: Cannot set session cookie: session.save() was called after headers were sent. Make sure to call it before any res.send() or res.end()`,\n );\n }\n const seal = await sealData(session, {\n password: passwordsAsMap,\n ttl: options.ttl,\n });\n const cookieValue = serializeCookie(\n options.cookieName,\n seal,\n options.cookieOptions,\n );\n\n if (cookieValue.length > 4096) {\n throw new Error(\n `iron-session: Cookie length is too big ${cookieValue.length}, browsers will refuse it. Try to remove some data.`,\n );\n }\n\n addToCookies(cookieValue, res);\n },\n },\n destroy: {\n value: function destroy() {\n Object.keys(session).forEach((key) => {\n // @ts-ignore See comment on the IronSessionData interface\n delete session[key];\n });\n\n const cookieValue = serializeCookie(options.cookieName, \"\", {\n ...options.cookieOptions,\n maxAge: 0,\n });\n addToCookies(cookieValue, res);\n },\n },\n });\n\n return session as IronSession;\n}\n\nfunction addToCookies(cookieValue: string, res: ServerResponse) {\n let existingSetCookie =\n (res.getHeader(\"set-cookie\") as string[] | string) ?? [];\n if (typeof existingSetCookie === \"string\") {\n existingSetCookie = [existingSetCookie];\n }\n res.setHeader(\"set-cookie\", [...existingSetCookie, cookieValue]);\n}\n\nfunction computeCookieMaxAge(ttl: number) {\n // The next line makes sure browser will expire cookies before seals are considered expired by the server.\n // It also allows for clock difference of 60 seconds maximum between server and clients.\n // It also makes sure to expire the cookie immediately when value is 0\n return ttl - timestampSkewSec;\n}\n\nexport async function unsealData<T = Record<string, unknown>>(\n seal: string,\n {\n password,\n ttl = fourteenDaysInSeconds,\n }: { password: password; ttl?: number },\n): Promise<T> {\n const passwordsAsMap = normalizeStringPasswordToMap(password);\n const { sealWithoutVersion, tokenVersion } = parseSeal(seal);\n\n try {\n const data = await Iron.unseal(sealWithoutVersion, passwordsAsMap, {\n ...Iron.defaults,\n ttl: ttl * 1000,\n });\n\n if (tokenVersion === 2) {\n return data;\n }\n\n return {\n ...data.persistent,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (\n error.message === \"Expired seal\" ||\n error.message === \"Bad hmac value\" ||\n error.message === \"Cannot find password: \" ||\n error.message === \"Incorrect number of sealed components\"\n ) {\n // if seal expired or\n // if seal is not valid (encrypted using a different password, when passwords are badly rotated) or\n // if we can't find back the password in the seal\n // then we just start a new session over\n return {} as T;\n }\n }\n\n throw error;\n }\n}\n\nfunction parseSeal(seal: string): {\n sealWithoutVersion: string;\n tokenVersion: number | null;\n} {\n if (seal[seal.length - 2] === versionDelimiter) {\n const [sealWithoutVersion, tokenVersionAsString] =\n seal.split(versionDelimiter);\n return {\n sealWithoutVersion,\n tokenVersion: parseInt(tokenVersionAsString, 10),\n };\n }\n\n return { sealWithoutVersion: seal, tokenVersion: null };\n}\n\nexport async function sealData(\n data: unknown,\n {\n password,\n ttl = fourteenDaysInSeconds,\n }: { password: password; ttl?: number },\n) {\n const passwordsAsMap = normalizeStringPasswordToMap(password);\n\n const mostRecentPasswordId = Math.max(\n ...Object.keys(passwordsAsMap).map((id) => parseInt(id, 10)),\n );\n\n const passwordForSeal = {\n id: mostRecentPasswordId.toString(),\n secret: passwordsAsMap[mostRecentPasswordId],\n };\n\n const seal = await Iron.seal(data, passwordForSeal, {\n ...Iron.defaults,\n ttl: ttl * 1000,\n });\n\n return `${seal}${versionDelimiter}${currentMajorVersion}`;\n}\n\nfunction normalizeStringPasswordToMap(password: password) {\n return typeof password === \"string\" ? { 1: password } : password;\n}\n"],
|
|
5
|
-
"mappings": ";AAAA;AAEA;
|
|
4
|
+
"sourcesContent": ["import Iron from \"@hapi/iron\";\nimport type { CookieSerializeOptions } from \"cookie\";\nimport { parse as parseCookie, serialize as serializeCookie } from \"cookie\";\nimport type { IncomingMessage, ServerResponse } from \"http\";\n\n// default time allowed to check for iron seal validity when ttl passed\n// see https://hapi.dev/family/iron/api/?v=6.0.0#options\nconst timestampSkewSec = 60;\n\ntype passwordsMap = { [id: string]: string };\ntype password = string | passwordsMap;\n\nconst fourteenDaysInSeconds = 15 * 24 * 3600;\n\n// We store a token major version to handle data format changes when any. So that when you upgrade the cookies\n// can be kept alive between upgrades, no need to disconnect everyone.\nconst currentMajorVersion = 2;\nconst versionDelimiter = \"~\";\n\nconst defaultOptions: {\n ttl: number;\n cookieOptions: CookieSerializeOptions;\n} = {\n ttl: fourteenDaysInSeconds,\n cookieOptions: {\n httpOnly: true,\n secure: true,\n sameSite: \"lax\",\n path: \"/\",\n },\n};\n\nexport interface IronSessionOptions {\n /**\n * This is the cookie name that will be used inside the browser. You should make sure it's unique given\n * your application. Example: vercel-session\n */\n cookieName: string;\n\n /**\n * This is the password(s) that will be used to encrypt the cookie. It can be either a string or an object\n * like {1: \"password\", 2: password}.\n *\n * When you provide multiple passwords then all of them will be used to decrypt the cookie and only the most\n * recent (= highest key, 2 in this example) password will be used to encrypt the cookie. This allow you\n * to use password rotation (security)\n */\n password: password;\n\n /**\n * This is the time in seconds that the session will be valid for. This also set the max-age attribute of\n * the cookie automatically (minus 60 seconds so that the cookie always expire before the session).\n */\n ttl?: number;\n\n /**\n * This is the options that will be passed to the cookie library.\n * You can see all of them here: https://github.com/jshttp/cookie#options-1.\n *\n * If you want to use \"session cookies\" (cookies that are deleted when the browser is closed) then you need\n * to pass cookieOptions: { maxAge: undefined }.\n */\n cookieOptions?: CookieSerializeOptions;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface IronSessionData {\n // If we allow for any keys, then there's no more type check on unknown properties\n // which is not good\n // If we allow for any keys, the later delete will work but I prefer to disable the\n // check at this stage and\n // provide good type checking instead\n // [key: string]: unknown;\n}\n\nexport type IronSession = IronSessionData & {\n /**\n * Destroys the session data and removes the cookie.\n */\n destroy: () => void;\n\n /**\n * Encrypts the session data and sets the cookie.\n */\n save: () => Promise<void>;\n};\n\ndeclare module \"http\" {\n interface IncomingMessage {\n session: IronSession;\n }\n}\n\nexport async function getIronSession(\n req: IncomingMessage,\n res: ServerResponse,\n userSessionOptions: IronSessionOptions,\n): Promise<IronSession> {\n if (\n !req ||\n !res ||\n !userSessionOptions ||\n !userSessionOptions.cookieName ||\n !userSessionOptions.password\n ) {\n throw new Error(\n `iron-session: Bad usage. Minimum usage is const session = await getIronSession(req, res, { cookieName: \"...\", password: \"...\". Check the usage here: https://github.com/vvo/iron-session`,\n );\n }\n\n const passwordsAsMap = normalizeStringPasswordToMap(\n userSessionOptions.password,\n );\n\n Object.values(\n normalizeStringPasswordToMap(userSessionOptions.password),\n ).forEach((password) => {\n if (password.length < 32) {\n throw new Error(\n `iron-session: Bad usage. Password must be at least 32 characters long.`,\n );\n }\n });\n\n const options: Required<IronSessionOptions> = {\n ...defaultOptions,\n ...userSessionOptions,\n cookieOptions: {\n ...defaultOptions.cookieOptions,\n ...(userSessionOptions.cookieOptions || {}),\n },\n };\n\n if (options.ttl === 0) {\n // ttl = 0 means no expiration\n // but in reality cookies have to expire (can't have no max-age)\n // 2147483647 is the max value for max-age in cookies\n // see https://stackoverflow.com/a/11685301/147079\n options.ttl = 2147483647;\n }\n\n if (\n userSessionOptions.cookieOptions &&\n \"maxAge\" in userSessionOptions.cookieOptions\n ) {\n // session cookie, do not set maxAge, consider token as infinite\n if (userSessionOptions.cookieOptions.maxAge === undefined) {\n options.ttl = 0;\n } else {\n options.cookieOptions.maxAge = computeCookieMaxAge(\n userSessionOptions.cookieOptions.maxAge,\n );\n }\n } else {\n options.cookieOptions.maxAge = computeCookieMaxAge(options.ttl);\n }\n\n const sealFromCookies = parseCookie(req.headers.cookie || \"\")[\n options.cookieName\n ];\n\n const session =\n sealFromCookies === undefined\n ? {}\n : await unsealData<IronSessionData>(sealFromCookies, {\n password: passwordsAsMap,\n ttl: options.ttl,\n });\n\n Object.defineProperties(session, {\n save: {\n value: async function save() {\n if (res.headersSent === true) {\n throw new Error(\n `iron-session: Cannot set session cookie: session.save() was called after headers were sent. Make sure to call it before any res.send() or res.end()`,\n );\n }\n const seal = await sealData(session, {\n password: passwordsAsMap,\n ttl: options.ttl,\n });\n const cookieValue = serializeCookie(\n options.cookieName,\n seal,\n options.cookieOptions,\n );\n\n if (cookieValue.length > 4096) {\n throw new Error(\n `iron-session: Cookie length is too big ${cookieValue.length}, browsers will refuse it. Try to remove some data.`,\n );\n }\n\n addToCookies(cookieValue, res);\n },\n },\n destroy: {\n value: function destroy() {\n Object.keys(session).forEach((key) => {\n // @ts-ignore See comment on the IronSessionData interface\n delete session[key];\n });\n\n const cookieValue = serializeCookie(options.cookieName, \"\", {\n ...options.cookieOptions,\n maxAge: 0,\n });\n addToCookies(cookieValue, res);\n },\n },\n });\n\n return session as IronSession;\n}\n\nfunction addToCookies(cookieValue: string, res: ServerResponse) {\n let existingSetCookie =\n (res.getHeader(\"set-cookie\") as string[] | string) ?? [];\n if (typeof existingSetCookie === \"string\") {\n existingSetCookie = [existingSetCookie];\n }\n res.setHeader(\"set-cookie\", [...existingSetCookie, cookieValue]);\n}\n\nfunction computeCookieMaxAge(ttl: number) {\n // The next line makes sure browser will expire cookies before seals are considered expired by the server.\n // It also allows for clock difference of 60 seconds maximum between server and clients.\n // It also makes sure to expire the cookie immediately when value is 0\n return ttl - timestampSkewSec;\n}\n\nexport async function unsealData<T = Record<string, unknown>>(\n seal: string,\n {\n password,\n ttl = fourteenDaysInSeconds,\n }: { password: password; ttl?: number },\n): Promise<T> {\n const passwordsAsMap = normalizeStringPasswordToMap(password);\n const { sealWithoutVersion, tokenVersion } = parseSeal(seal);\n\n try {\n const data = await Iron.unseal(sealWithoutVersion, passwordsAsMap, {\n ...Iron.defaults,\n ttl: ttl * 1000,\n });\n\n if (tokenVersion === 2) {\n return data;\n }\n\n return {\n ...data.persistent,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (\n error.message === \"Expired seal\" ||\n error.message === \"Bad hmac value\" ||\n error.message === \"Cannot find password: \" ||\n error.message === \"Incorrect number of sealed components\"\n ) {\n // if seal expired or\n // if seal is not valid (encrypted using a different password, when passwords are badly rotated) or\n // if we can't find back the password in the seal\n // then we just start a new session over\n return {} as T;\n }\n }\n\n throw error;\n }\n}\n\nfunction parseSeal(seal: string): {\n sealWithoutVersion: string;\n tokenVersion: number | null;\n} {\n if (seal[seal.length - 2] === versionDelimiter) {\n const [sealWithoutVersion, tokenVersionAsString] =\n seal.split(versionDelimiter);\n return {\n sealWithoutVersion,\n tokenVersion: parseInt(tokenVersionAsString, 10),\n };\n }\n\n return { sealWithoutVersion: seal, tokenVersion: null };\n}\n\nexport async function sealData(\n data: unknown,\n {\n password,\n ttl = fourteenDaysInSeconds,\n }: { password: password; ttl?: number },\n) {\n const passwordsAsMap = normalizeStringPasswordToMap(password);\n\n const mostRecentPasswordId = Math.max(\n ...Object.keys(passwordsAsMap).map((id) => parseInt(id, 10)),\n );\n\n const passwordForSeal = {\n id: mostRecentPasswordId.toString(),\n secret: passwordsAsMap[mostRecentPasswordId],\n };\n\n const seal = await Iron.seal(data, passwordForSeal, {\n ...Iron.defaults,\n ttl: ttl * 1000,\n });\n\n return `${seal}${versionDelimiter}${currentMajorVersion}`;\n}\n\nfunction normalizeStringPasswordToMap(password: password) {\n return typeof password === \"string\" ? { 1: password } : password;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iron-session",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.3",
|
|
4
4
|
"description": "Node.js stateless session utility using signed and encrypted cookies to store data. Works with Next.js, Express, NestJs, Fastify, and any Node.js HTTP framework.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Vincent Voyer <vincent@codeagain.com>",
|
|
@@ -35,10 +35,11 @@
|
|
|
35
35
|
"build:express": "tsup express/index.ts -d express/dist",
|
|
36
36
|
"build:next": "tsup next/index.ts -d next/dist",
|
|
37
37
|
"build:src": "tsup src/index.ts",
|
|
38
|
+
"installExamples": "(cd examples/express && npm ci) && (cd examples/next.js && npm ci) && (cd examples/next.js-typescript && npm ci)",
|
|
38
39
|
"lint": "eslint --ext ts,js,jsx,tsx src/ next/ express/ examples/",
|
|
39
40
|
"prepublishOnly": "npm run build && cp next/dist/* next/ && cp express/dist/* express/",
|
|
40
41
|
"postpublish": "rm next/*.d.ts next/*.js next/*.map next/*.mjs && rm express/*.d.ts express/*.js express/*.map express/*.mjs",
|
|
41
|
-
"test": "jest --coverage && npm run lint && tsc --noEmit && npm run build && npm run build:clean",
|
|
42
|
+
"test": "jest --coverage --ci && npm run installExamples && npm run lint && tsc --noEmit && npm run build && npm run build:clean",
|
|
42
43
|
"watch": "npm run build:clean && concurrently \"npm:watch:*\"",
|
|
43
44
|
"watch:express": "tsup express/index.ts -d express/dist --watch express/index.ts",
|
|
44
45
|
"watch:next": "tsup next/index.ts -d next/dist --watch next/index.ts",
|
|
@@ -110,24 +111,23 @@
|
|
|
110
111
|
"@hapi/iron": "^6.0.0",
|
|
111
112
|
"@types/cookie": "^0.4.0",
|
|
112
113
|
"@types/express": "^4.17.13",
|
|
113
|
-
"@types/node": "^16.11.
|
|
114
|
+
"@types/node": "^16.11.7",
|
|
114
115
|
"cookie": "^0.4.1"
|
|
115
116
|
},
|
|
116
117
|
"devDependencies": {
|
|
117
|
-
"@swc/core": "^1.2.
|
|
118
|
-
"@swc/core-darwin-x64": "^1.2.104",
|
|
118
|
+
"@swc/core": "^1.2.107",
|
|
119
119
|
"@swc/jest": "0.2.5",
|
|
120
120
|
"@tsconfig/node12": "1.0.9",
|
|
121
|
-
"@types/jest": "
|
|
122
|
-
"@typescript-eslint/eslint-plugin": "5.
|
|
123
|
-
"@typescript-eslint/parser": "5.
|
|
121
|
+
"@types/jest": "27.0.2",
|
|
122
|
+
"@typescript-eslint/eslint-plugin": "5.3.0",
|
|
123
|
+
"@typescript-eslint/parser": "^5.3.1",
|
|
124
124
|
"concurrently": "6.3.0",
|
|
125
|
-
"eslint": "^8.
|
|
125
|
+
"eslint": "^8.2.0",
|
|
126
126
|
"jest": "27.3.1",
|
|
127
127
|
"prettier": "2.4.1",
|
|
128
128
|
"prettier-plugin-packagejson": "2.2.13",
|
|
129
129
|
"rimraf": "3.0.2",
|
|
130
|
-
"tsup": "5.
|
|
130
|
+
"tsup": "5.6.0",
|
|
131
131
|
"typescript": "4.4.4"
|
|
132
132
|
},
|
|
133
133
|
"peerDependencies": {
|