@opensourceframework/next-iron-session 8.0.4
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/LICENSE.md +20 -0
- package/README.md +227 -0
- package/dist/index.cjs +295 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +115 -0
- package/dist/index.d.ts +115 -0
- package/dist/index.js +271 -0
- package/dist/index.js.map +1 -0
- package/package.json +89 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Vincent Voyer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# iron-session  [](https://github.com/vvo/iron-session/blob/master/LICENSE) [](https://www.npmjs.com/package/iron-session)  
|
|
2
|
+
|
|
3
|
+
**`iron-session` is a secure, stateless, and cookie-based session library for JavaScript.**
|
|
4
|
+
|
|
5
|
+
The session data is stored in signed and encrypted cookies which are decoded by your server code in a stateless fashion (= no network involved). This is the same technique used by frameworks like
|
|
6
|
+
[Ruby On Rails](https://guides.rubyonrails.org/security.html#session-storage).
|
|
7
|
+
|
|
8
|
+
<p align="center"><i>Online demo and examples: <a href="https://get-iron-session.vercel.app/">https://get-iron-session.vercel.app</a></i> 👀 <br/>
|
|
9
|
+
<i>Featured in the <a href="https://nextjs.org/docs/authentication">Next.js documentation</a></i> ⭐️</p>
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [Table of Contents](#table-of-contents)
|
|
14
|
+
- [Installation](#installation)
|
|
15
|
+
- [Usage](#usage)
|
|
16
|
+
- [Examples](#examples)
|
|
17
|
+
- [Project status](#project-status)
|
|
18
|
+
- [Session options](#session-options)
|
|
19
|
+
- [API](#api)
|
|
20
|
+
- [`getIronSession<T>(req, res, sessionOptions): Promise<IronSession<T>>`](#getironsessiontreq-res-sessionoptions-promiseironsessiont)
|
|
21
|
+
- [`getIronSession<T>(cookieStore, sessionOptions): Promise<IronSession<T>>`](#getironsessiontcookiestore-sessionoptions-promiseironsessiont)
|
|
22
|
+
- [`session.save(): Promise<void>`](#sessionsave-promisevoid)
|
|
23
|
+
- [`session.destroy(): void`](#sessiondestroy-void)
|
|
24
|
+
- [`session.updateConfig(sessionOptions: SessionOptions): void`](#sessionupdateconfigsessionoptions-sessionoptions-void)
|
|
25
|
+
- [`sealData(data: unknown, { password, ttl }): Promise<string>`](#sealdatadata-unknown--password-ttl--promisestring)
|
|
26
|
+
- [`unsealData<T>(seal: string, { password, ttl }): Promise<T>`](#unsealdatatseal-string--password-ttl--promiset)
|
|
27
|
+
- [FAQ](#faq)
|
|
28
|
+
- [Why use pure cookies for sessions?](#why-use-pure-cookies-for-sessions)
|
|
29
|
+
- [How to invalidate sessions?](#how-to-invalidate-sessions)
|
|
30
|
+
- [Can I use something else than cookies?](#can-i-use-something-else-than-cookies)
|
|
31
|
+
- [How is this different from JWT?](#how-is-this-different-from-jwt)
|
|
32
|
+
- [Credits](#credits)
|
|
33
|
+
- [Good Reads](#good-reads)
|
|
34
|
+
|
|
35
|
+
### Attribution
|
|
36
|
+
|
|
37
|
+
- **Original Author**: Unknown
|
|
38
|
+
- **Original Repository**: https://github.com/vvo/next-iron-session
|
|
39
|
+
- **Original License**: MIT
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
pnpm add iron-session
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
*We have extensive examples here too: https://get-iron-session.vercel.app/.*
|
|
50
|
+
|
|
51
|
+
To get a session, there's a single method to know: `getIronSession`.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
// Next.js API Routes and Node.js/Express/Connect.
|
|
55
|
+
import { getIronSession } from 'iron-session';
|
|
56
|
+
|
|
57
|
+
export async function get(req, res) {
|
|
58
|
+
const session = await getIronSession(req, res, { password: "...", cookieName: "..." });
|
|
59
|
+
return session;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function post(req, res) {
|
|
63
|
+
const session = await getIronSession(req, res, { password: "...", cookieName: "..." });
|
|
64
|
+
session.username = "Alison";
|
|
65
|
+
await session.save();
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
// Next.js Route Handlers (App Router)
|
|
71
|
+
import { cookies } from 'next/headers';
|
|
72
|
+
import { getIronSession } from 'iron-session';
|
|
73
|
+
|
|
74
|
+
export async function GET() {
|
|
75
|
+
const session = await getIronSession(cookies(), { password: "...", cookieName: "..." });
|
|
76
|
+
return session;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function POST() {
|
|
80
|
+
const session = await getIronSession(cookies(), { password: "...", cookieName: "..." });
|
|
81
|
+
session.username = "Alison";
|
|
82
|
+
await session.save();
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
// Next.js Server Components and Server Actions (App Router)
|
|
88
|
+
import { cookies } from 'next/headers';
|
|
89
|
+
import { getIronSession } from 'iron-session';
|
|
90
|
+
|
|
91
|
+
async function getIronSessionData() {
|
|
92
|
+
const session = await getIronSession(cookies(), { password: "...", cookieName: "..." });
|
|
93
|
+
return session
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function Profile() {
|
|
97
|
+
const session = await getIronSessionData();
|
|
98
|
+
|
|
99
|
+
return <div>{session.username}</div>;
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Examples
|
|
104
|
+
|
|
105
|
+
We have many different patterns and examples on the online demo, have a look: https://get-iron-session.vercel.app/.
|
|
106
|
+
|
|
107
|
+
## Project status
|
|
108
|
+
|
|
109
|
+
✅ Production ready and maintained.
|
|
110
|
+
|
|
111
|
+
## Session options
|
|
112
|
+
|
|
113
|
+
Two options are required: `password` and `cookieName`. Everything else is automatically computed and usually doesn't need to be changed.****
|
|
114
|
+
|
|
115
|
+
- `password`, **required**: Private key used to encrypt the cookie. It has to be at least 32 characters long. Use <https://1password.com/password-generator/> to generate strong passwords. `password` can be either a `string` or an `object` with incrementing keys like this: `{2: "...", 1: "..."}` to allow for password rotation. iron-session will use the highest numbered key for new cookies.
|
|
116
|
+
- `cookieName`, **required**: Name of the cookie to be stored
|
|
117
|
+
- `ttl`, _optional_: In seconds. Default to the equivalent of 14 days. You can set this to `0` and iron-session will compute the maximum allowed value by cookies.
|
|
118
|
+
- `cookieOptions`, _optional_: Any option available from [jshttp/cookie#serialize](https://github.com/jshttp/cookie#cookieserializename-value-options) except for `encode` which is not a Set-Cookie Attribute. See [Mozilla Set-Cookie Attributes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes) and [Chrome Cookie Fields](https://developer.chrome.com/docs/devtools/application/cookies/#fields). Default to:
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
{
|
|
122
|
+
httpOnly: true,
|
|
123
|
+
secure: true, // set this to false in local (non-HTTPS) development
|
|
124
|
+
sameSite: "lax",// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#lax
|
|
125
|
+
maxAge: (ttl === 0 ? 2147483647 : ttl) - 60, // Expire cookie before the session expires.
|
|
126
|
+
path: "/",
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## API
|
|
131
|
+
|
|
132
|
+
### `getIronSession<T>(req, res, sessionOptions): Promise<IronSession<T>>`
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
type SessionData = {
|
|
136
|
+
// Your data
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const session = await getIronSession<SessionData>(req, res, sessionOptions);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### `getIronSession<T>(cookieStore, sessionOptions): Promise<IronSession<T>>`
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
type SessionData = {
|
|
146
|
+
// Your data
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const session = await getIronSession<SessionData>(cookies(), sessionOptions);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### `session.save(): Promise<void>`
|
|
153
|
+
|
|
154
|
+
Saves the session. This is an asynchronous operation. It must be done and awaited before headers are sent to the client.
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
await session.save()
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### `session.destroy(): void`
|
|
161
|
+
|
|
162
|
+
Destroys the session. This is a synchronous operation as it only removes the cookie. It must be done before headers are sent to the client.
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
session.destroy()
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### `session.updateConfig(sessionOptions: SessionOptions): void`
|
|
169
|
+
|
|
170
|
+
Updates the configuration of the session with new session options. You still need to call save() if you want them to be applied.
|
|
171
|
+
|
|
172
|
+
### `sealData(data: unknown, { password, ttl }): Promise<string>`
|
|
173
|
+
|
|
174
|
+
This is the underlying method and seal mechanism that powers `iron-session`. You can use it to seal any `data` you want and pass it around. One usecase are magic links: you generate a seal that contains a user id to login and send it to a route on your website (like `/magic-login`). Once received, you can safely decode the seal with `unsealData` and log the user in.
|
|
175
|
+
|
|
176
|
+
### `unsealData<T>(seal: string, { password, ttl }): Promise<T>`
|
|
177
|
+
|
|
178
|
+
This is the opposite of `sealData` and allow you to decode a seal to get the original data back.
|
|
179
|
+
|
|
180
|
+
## FAQ
|
|
181
|
+
|
|
182
|
+
### Why use pure cookies for sessions?
|
|
183
|
+
|
|
184
|
+
This makes your sessions stateless: since the data is passed around in cookies, you do not need any server or service to store session data.
|
|
185
|
+
|
|
186
|
+
More information can also be found on the [Ruby On Rails website](https://guides.rubyonrails.org/security.html#session-storage) which uses the same technique.
|
|
187
|
+
|
|
188
|
+
### How to invalidate sessions?
|
|
189
|
+
|
|
190
|
+
Sessions cannot be instantly invalidated (or "disconnect this customer") as there is typically no state stored about sessions on the server by default. However, in most applications, the first step upon receiving an authenticated request is to validate the user and their permissions in the database. So, to easily disconnect customers (or invalidate sessions), you can add an `isBlocked`` state in the database and create a UI to block customers.
|
|
191
|
+
|
|
192
|
+
Then, every time a request is received that involves reading or altering sensitive data, make sure to check this flag.
|
|
193
|
+
|
|
194
|
+
### Can I use something else than cookies?
|
|
195
|
+
|
|
196
|
+
Yes, we expose `sealData` and `unsealData` which are not tied to cookies. This way you can seal and unseal any object in your application and move seals around to login users.
|
|
197
|
+
|
|
198
|
+
### How is this different from [JWT](https://jwt.io/)?
|
|
199
|
+
|
|
200
|
+
Not so much:
|
|
201
|
+
|
|
202
|
+
- JWT is a standard, it stores metadata in the JWT token themselves to ensure communication between different systems is flawless.
|
|
203
|
+
- JWT tokens are not encrypted, the payload is visible by customers if they manage to inspect the seal. You would have to use [JWE](https://tools.ietf.org/html/rfc7516) to achieve the same.
|
|
204
|
+
- @hapi/iron mechanism is not a standard, it's a way to sign and encrypt data into seals
|
|
205
|
+
|
|
206
|
+
Depending on your own needs and preferences, `iron-session` may or may not fit you.
|
|
207
|
+
|
|
208
|
+
## Credits
|
|
209
|
+
|
|
210
|
+
- [Eran Hammer and hapi.js contributors](https://github.com/hapijs/iron/graphs/contributors)
|
|
211
|
+
for creating the underlying cryptography library
|
|
212
|
+
[`@hapi/iron`](https://hapi.dev/module/iron/).
|
|
213
|
+
- [Divyansh Singh](https://github.com/brc-dd) for reimplementing `@hapi/iron` as
|
|
214
|
+
[`iron-webcrypto`](https://github.com/brc-dd/iron-webcrypto) using standard
|
|
215
|
+
web APIs.
|
|
216
|
+
- [Hoang Vo](https://github.com/hoangvvo) for advice and guidance while building
|
|
217
|
+
this module. Hoang built
|
|
218
|
+
[`next-connect`](https://github.com/hoangvvo/next-connect) and
|
|
219
|
+
[`next-session`](https://github.com/hoangvvo/next-session).
|
|
220
|
+
- All the
|
|
221
|
+
[contributors](https://github.com/vvo/iron-session/graphs/contributors) for
|
|
222
|
+
making this project better.
|
|
223
|
+
|
|
224
|
+
## Good Reads
|
|
225
|
+
|
|
226
|
+
- <https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html>
|
|
227
|
+
- <https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html>
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var cookie = require('cookie');
|
|
4
|
+
var ironWebcrypto = require('iron-webcrypto');
|
|
5
|
+
var crypto = require('uncrypto');
|
|
6
|
+
|
|
7
|
+
function _interopNamespace(e) {
|
|
8
|
+
if (e && e.__esModule) return e;
|
|
9
|
+
var n = Object.create(null);
|
|
10
|
+
if (e) {
|
|
11
|
+
Object.keys(e).forEach(function (k) {
|
|
12
|
+
if (k !== 'default') {
|
|
13
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
14
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: function () { return e[k]; }
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
n.default = e;
|
|
22
|
+
return Object.freeze(n);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
|
|
26
|
+
|
|
27
|
+
// src/core.ts
|
|
28
|
+
var timestampSkewSec = 60;
|
|
29
|
+
var fourteenDaysInSeconds = 14 * 24 * 3600;
|
|
30
|
+
var currentMajorVersion = 2;
|
|
31
|
+
var versionDelimiter = "~";
|
|
32
|
+
var defaultOptions = {
|
|
33
|
+
ttl: fourteenDaysInSeconds,
|
|
34
|
+
cookieOptions: { httpOnly: true, secure: true, sameSite: "lax", path: "/" }
|
|
35
|
+
};
|
|
36
|
+
function normalizeStringPasswordToMap(password) {
|
|
37
|
+
return typeof password === "string" ? { 1: password } : password;
|
|
38
|
+
}
|
|
39
|
+
function parseSeal(seal) {
|
|
40
|
+
const [sealWithoutVersion, tokenVersionAsString] = seal.split(versionDelimiter);
|
|
41
|
+
const tokenVersion = tokenVersionAsString == null ? null : parseInt(tokenVersionAsString, 10);
|
|
42
|
+
return { sealWithoutVersion, tokenVersion };
|
|
43
|
+
}
|
|
44
|
+
function computeCookieMaxAge(ttl) {
|
|
45
|
+
if (ttl === 0) {
|
|
46
|
+
return 2147483647;
|
|
47
|
+
}
|
|
48
|
+
return ttl - timestampSkewSec;
|
|
49
|
+
}
|
|
50
|
+
function getCookie(req, cookieName) {
|
|
51
|
+
return cookie.parse(
|
|
52
|
+
("headers" in req && typeof req.headers.get === "function" ? req.headers.get("cookie") : req.headers.cookie) ?? ""
|
|
53
|
+
)[cookieName] ?? "";
|
|
54
|
+
}
|
|
55
|
+
function getServerActionCookie(cookieName, cookieHandler) {
|
|
56
|
+
const cookieObject = cookieHandler.get(cookieName);
|
|
57
|
+
const cookie = cookieObject?.value;
|
|
58
|
+
if (typeof cookie === "string") {
|
|
59
|
+
return cookie;
|
|
60
|
+
}
|
|
61
|
+
return "";
|
|
62
|
+
}
|
|
63
|
+
function setCookie(res, cookieValue) {
|
|
64
|
+
if ("headers" in res && typeof res.headers.append === "function") {
|
|
65
|
+
res.headers.append("set-cookie", cookieValue);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
let existingSetCookie = res.getHeader("set-cookie") ?? [];
|
|
69
|
+
if (!Array.isArray(existingSetCookie)) {
|
|
70
|
+
existingSetCookie = [existingSetCookie.toString()];
|
|
71
|
+
}
|
|
72
|
+
res.setHeader("set-cookie", [
|
|
73
|
+
...existingSetCookie,
|
|
74
|
+
cookieValue
|
|
75
|
+
]);
|
|
76
|
+
}
|
|
77
|
+
function createSealData(_crypto) {
|
|
78
|
+
return async function sealData2(data, {
|
|
79
|
+
password,
|
|
80
|
+
ttl = fourteenDaysInSeconds
|
|
81
|
+
}) {
|
|
82
|
+
const passwordsMap = normalizeStringPasswordToMap(password);
|
|
83
|
+
const mostRecentPasswordId = Math.max(
|
|
84
|
+
...Object.keys(passwordsMap).map(Number)
|
|
85
|
+
);
|
|
86
|
+
const passwordForSeal = {
|
|
87
|
+
id: mostRecentPasswordId.toString(),
|
|
88
|
+
secret: passwordsMap[mostRecentPasswordId]
|
|
89
|
+
};
|
|
90
|
+
const seal = await ironWebcrypto.seal(_crypto, data, passwordForSeal, {
|
|
91
|
+
...ironWebcrypto.defaults,
|
|
92
|
+
ttl: ttl * 1e3
|
|
93
|
+
});
|
|
94
|
+
return `${seal}${versionDelimiter}${currentMajorVersion}`;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function createUnsealData(_crypto) {
|
|
98
|
+
return async function unsealData2(seal, {
|
|
99
|
+
password,
|
|
100
|
+
ttl = fourteenDaysInSeconds
|
|
101
|
+
}) {
|
|
102
|
+
const passwordsMap = normalizeStringPasswordToMap(password);
|
|
103
|
+
const { sealWithoutVersion, tokenVersion } = parseSeal(seal);
|
|
104
|
+
try {
|
|
105
|
+
const data = await ironWebcrypto.unseal(_crypto, sealWithoutVersion, passwordsMap, {
|
|
106
|
+
...ironWebcrypto.defaults,
|
|
107
|
+
ttl: ttl * 1e3
|
|
108
|
+
}) ?? {};
|
|
109
|
+
if (tokenVersion === 2) {
|
|
110
|
+
return data;
|
|
111
|
+
}
|
|
112
|
+
return { ...data.persistent };
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (error instanceof Error && /^(Expired seal|Bad hmac value|Cannot find password|Incorrect number of sealed components)/.test(
|
|
115
|
+
error.message
|
|
116
|
+
)) {
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function getSessionConfig(sessionOptions) {
|
|
124
|
+
const options = {
|
|
125
|
+
...defaultOptions,
|
|
126
|
+
...sessionOptions,
|
|
127
|
+
cookieOptions: {
|
|
128
|
+
...defaultOptions.cookieOptions,
|
|
129
|
+
...sessionOptions.cookieOptions || {}
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
if (sessionOptions.cookieOptions && "maxAge" in sessionOptions.cookieOptions) {
|
|
133
|
+
if (sessionOptions.cookieOptions.maxAge === void 0) {
|
|
134
|
+
options.ttl = 0;
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
options.cookieOptions.maxAge = computeCookieMaxAge(options.ttl);
|
|
138
|
+
}
|
|
139
|
+
return options;
|
|
140
|
+
}
|
|
141
|
+
var badUsageMessage = "iron-session: Bad usage: use getIronSession(req, res, options) or getIronSession(cookieStore, options).";
|
|
142
|
+
function createGetIronSession(sealData2, unsealData2) {
|
|
143
|
+
return getIronSession2;
|
|
144
|
+
async function getIronSession2(reqOrCookieStore, resOrsessionOptions, sessionOptions) {
|
|
145
|
+
if (!reqOrCookieStore) {
|
|
146
|
+
throw new Error(badUsageMessage);
|
|
147
|
+
}
|
|
148
|
+
if (!resOrsessionOptions) {
|
|
149
|
+
throw new Error(badUsageMessage);
|
|
150
|
+
}
|
|
151
|
+
if (!sessionOptions) {
|
|
152
|
+
return getIronSessionFromCookieStore(
|
|
153
|
+
reqOrCookieStore,
|
|
154
|
+
resOrsessionOptions,
|
|
155
|
+
sealData2,
|
|
156
|
+
unsealData2
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
const req = reqOrCookieStore;
|
|
160
|
+
const res = resOrsessionOptions;
|
|
161
|
+
if (!sessionOptions) {
|
|
162
|
+
throw new Error(badUsageMessage);
|
|
163
|
+
}
|
|
164
|
+
if (!sessionOptions.cookieName) {
|
|
165
|
+
throw new Error("iron-session: Bad usage. Missing cookie name.");
|
|
166
|
+
}
|
|
167
|
+
if (!sessionOptions.password) {
|
|
168
|
+
throw new Error("iron-session: Bad usage. Missing password.");
|
|
169
|
+
}
|
|
170
|
+
const passwordsMap = normalizeStringPasswordToMap(sessionOptions.password);
|
|
171
|
+
if (Object.values(passwordsMap).some((password) => password.length < 32)) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
"iron-session: Bad usage. Password must be at least 32 characters long."
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
let sessionConfig = getSessionConfig(sessionOptions);
|
|
177
|
+
const sealFromCookies = getCookie(req, sessionConfig.cookieName);
|
|
178
|
+
const session = sealFromCookies ? await unsealData2(sealFromCookies, {
|
|
179
|
+
password: passwordsMap,
|
|
180
|
+
ttl: sessionConfig.ttl
|
|
181
|
+
}) : {};
|
|
182
|
+
Object.defineProperties(session, {
|
|
183
|
+
updateConfig: {
|
|
184
|
+
value: function updateConfig(newSessionOptions) {
|
|
185
|
+
sessionConfig = getSessionConfig(newSessionOptions);
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
save: {
|
|
189
|
+
value: async function save() {
|
|
190
|
+
if ("headersSent" in res && res.headersSent) {
|
|
191
|
+
throw new Error(
|
|
192
|
+
"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()"
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
const seal = await sealData2(session, {
|
|
196
|
+
password: passwordsMap,
|
|
197
|
+
ttl: sessionConfig.ttl
|
|
198
|
+
});
|
|
199
|
+
const cookieValue = cookie.serialize(
|
|
200
|
+
sessionConfig.cookieName,
|
|
201
|
+
seal,
|
|
202
|
+
sessionConfig.cookieOptions
|
|
203
|
+
);
|
|
204
|
+
if (cookieValue.length > 4096) {
|
|
205
|
+
throw new Error(
|
|
206
|
+
`iron-session: Cookie length is too big (${cookieValue.length} bytes), browsers will refuse it. Try to remove some data.`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
setCookie(res, cookieValue);
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
destroy: {
|
|
213
|
+
value: function destroy() {
|
|
214
|
+
Object.keys(session).forEach((key) => {
|
|
215
|
+
delete session[key];
|
|
216
|
+
});
|
|
217
|
+
const cookieValue = cookie.serialize(sessionConfig.cookieName, "", {
|
|
218
|
+
...sessionConfig.cookieOptions,
|
|
219
|
+
maxAge: 0
|
|
220
|
+
});
|
|
221
|
+
setCookie(res, cookieValue);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
return session;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async function getIronSessionFromCookieStore(cookieStore, sessionOptions, sealData2, unsealData2) {
|
|
229
|
+
if (!sessionOptions.cookieName) {
|
|
230
|
+
throw new Error("iron-session: Bad usage. Missing cookie name.");
|
|
231
|
+
}
|
|
232
|
+
if (!sessionOptions.password) {
|
|
233
|
+
throw new Error("iron-session: Bad usage. Missing password.");
|
|
234
|
+
}
|
|
235
|
+
const passwordsMap = normalizeStringPasswordToMap(sessionOptions.password);
|
|
236
|
+
if (Object.values(passwordsMap).some((password) => password.length < 32)) {
|
|
237
|
+
throw new Error(
|
|
238
|
+
"iron-session: Bad usage. Password must be at least 32 characters long."
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
let sessionConfig = getSessionConfig(sessionOptions);
|
|
242
|
+
const sealFromCookies = getServerActionCookie(
|
|
243
|
+
sessionConfig.cookieName,
|
|
244
|
+
cookieStore
|
|
245
|
+
);
|
|
246
|
+
const session = sealFromCookies ? await unsealData2(sealFromCookies, {
|
|
247
|
+
password: passwordsMap,
|
|
248
|
+
ttl: sessionConfig.ttl
|
|
249
|
+
}) : {};
|
|
250
|
+
Object.defineProperties(session, {
|
|
251
|
+
updateConfig: {
|
|
252
|
+
value: function updateConfig(newSessionOptions) {
|
|
253
|
+
sessionConfig = getSessionConfig(newSessionOptions);
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
save: {
|
|
257
|
+
value: async function save() {
|
|
258
|
+
const seal = await sealData2(session, {
|
|
259
|
+
password: passwordsMap,
|
|
260
|
+
ttl: sessionConfig.ttl
|
|
261
|
+
});
|
|
262
|
+
const cookieLength = sessionConfig.cookieName.length + seal.length + JSON.stringify(sessionConfig.cookieOptions).length;
|
|
263
|
+
if (cookieLength > 4096) {
|
|
264
|
+
throw new Error(
|
|
265
|
+
`iron-session: Cookie length is too big (${cookieLength} bytes), browsers will refuse it. Try to remove some data.`
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
cookieStore.set(
|
|
269
|
+
sessionConfig.cookieName,
|
|
270
|
+
seal,
|
|
271
|
+
sessionConfig.cookieOptions
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
destroy: {
|
|
276
|
+
value: function destroy() {
|
|
277
|
+
Object.keys(session).forEach((key) => {
|
|
278
|
+
delete session[key];
|
|
279
|
+
});
|
|
280
|
+
const cookieOptions = { ...sessionConfig.cookieOptions, maxAge: 0 };
|
|
281
|
+
cookieStore.set(sessionConfig.cookieName, "", cookieOptions);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
return session;
|
|
286
|
+
}
|
|
287
|
+
var sealData = createSealData(crypto__namespace);
|
|
288
|
+
var unsealData = createUnsealData(crypto__namespace);
|
|
289
|
+
var getIronSession = createGetIronSession(sealData, unsealData);
|
|
290
|
+
|
|
291
|
+
exports.getIronSession = getIronSession;
|
|
292
|
+
exports.sealData = sealData;
|
|
293
|
+
exports.unsealData = unsealData;
|
|
294
|
+
//# sourceMappingURL=index.cjs.map
|
|
295
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core.ts","../src/index.ts"],"names":["parse","sealData","ironSeal","ironDefaults","unsealData","ironUnseal","getIronSession","serialize","crypto"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { IncomingMessage, ServerResponse } from \"http\";\nimport { parse, serialize, type CookieSerializeOptions } from \"cookie\";\nimport {\n defaults as ironDefaults,\n seal as ironSeal,\n unseal as ironUnseal,\n} from \"iron-webcrypto\";\n\ntype PasswordsMap = Record<string, string>;\ntype Password = PasswordsMap | string;\ntype RequestType = IncomingMessage | Request;\ntype ResponseType = Response | ServerResponse;\n\n/**\n * {@link https://wicg.github.io/cookie-store/#dictdef-cookielistitem CookieListItem}\n * as specified by W3C.\n */\ninterface CookieListItem\n extends Pick<\n CookieSerializeOptions,\n \"domain\" | \"path\" | \"sameSite\" | \"secure\"\n > {\n /** A string with the name of a cookie. */\n name: string;\n /** A string containing the value of the cookie. */\n value: string;\n /** A number of milliseconds or Date interface containing the expires of the cookie. */\n expires?: CookieSerializeOptions[\"expires\"] | number;\n}\n\n/**\n * Superset of {@link CookieListItem} extending it with\n * the `httpOnly`, `maxAge` and `priority` properties.\n */\ntype ResponseCookie = CookieListItem &\n Pick<CookieSerializeOptions, \"httpOnly\" | \"maxAge\" | \"priority\">;\n\n/**\n * The high-level type definition of the .get() and .set() methods\n * of { cookies() } from \"next/headers\"\n */\nexport interface CookieStore {\n get: (name: string) => { name: string; value: string } | undefined;\n set: {\n (name: string, value: string, cookie?: Partial<ResponseCookie>): void;\n (options: ResponseCookie): void;\n };\n}\n\n/**\n * Set-Cookie Attributes do not include `encode`. We omit this from our `cookieOptions` type.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie\n * @see https://developer.chrome.com/docs/devtools/application/cookies/\n */\ntype CookieOptions = Omit<CookieSerializeOptions, \"encode\">;\n\nexport interface SessionOptions {\n /**\n * The cookie name that will be used inside the browser. Make sure it's unique\n * given your application.\n *\n * @example 'vercel-session'\n */\n cookieName: string;\n\n /**\n * The password(s) that will be used to encrypt the cookie. Can either be a string\n * or an object.\n *\n * When you provide multiple passwords then all of them will be used to decrypt\n * the cookie. But only the most recent (`= highest key`, `2` in the example)\n * password will be used to encrypt the cookie. This allows password rotation.\n *\n * @example { 1: 'password-1', 2: 'password-2' }\n */\n password: Password;\n\n /**\n * The time (in seconds) that the session will be valid for. Also sets the\n * `max-age` attribute of the cookie automatically (`= ttl - 60s`, so that the\n * cookie always expire before the session).\n *\n * `ttl = 0` means no expiration.\n *\n * @default 1209600\n */\n ttl?: number;\n\n /**\n * The options that will be passed to the cookie library.\n *\n * If you want to use \"session cookies\" (cookies that are deleted when the browser\n * is closed) then you need to pass `cookieOptions: { maxAge: undefined }`\n *\n * @see https://github.com/jshttp/cookie#options-1\n */\n cookieOptions?: CookieOptions;\n}\n\nexport type IronSession<T> = T & {\n /**\n * Encrypts the session data and sets the cookie.\n */\n readonly save: () => Promise<void>;\n\n /**\n * Destroys the session data and removes the cookie.\n */\n readonly destroy: () => void;\n\n /**\n * Update the session configuration. You still need to call save() to send the new cookie.\n */\n readonly updateConfig: (newSessionOptions: SessionOptions) => void;\n};\n\n// default time allowed to check for iron seal validity when ttl passed\n// see https://hapi.dev/module/iron/api/?v=7.0.1#options\nconst timestampSkewSec = 60;\nconst fourteenDaysInSeconds = 14 * 24 * 3600;\n\n// We store a token major version to handle data format changes so that the cookies\n// can be kept alive between upgrades, no need to disconnect everyone.\nconst currentMajorVersion = 2;\nconst versionDelimiter = \"~\";\n\nconst defaultOptions: Required<Pick<SessionOptions, \"ttl\" | \"cookieOptions\">> =\n {\n ttl: fourteenDaysInSeconds,\n cookieOptions: { httpOnly: true, secure: true, sameSite: \"lax\", path: \"/\" },\n };\n\nfunction normalizeStringPasswordToMap(password: Password): PasswordsMap {\n return typeof password === \"string\" ? { 1: password } : password;\n}\n\nfunction parseSeal(seal: string): {\n sealWithoutVersion: string;\n tokenVersion: number | null;\n} {\n const [sealWithoutVersion, tokenVersionAsString] =\n seal.split(versionDelimiter);\n const tokenVersion =\n tokenVersionAsString == null ? null : parseInt(tokenVersionAsString, 10);\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return { sealWithoutVersion: sealWithoutVersion!, tokenVersion };\n}\n\nfunction computeCookieMaxAge(ttl: number): number {\n if (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 return 2147483647;\n }\n\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 between server and clients.\n return ttl - timestampSkewSec;\n}\n\nfunction getCookie(req: RequestType, cookieName: string): string {\n return (\n parse(\n (\"headers\" in req && typeof req.headers.get === \"function\"\n ? req.headers.get(\"cookie\")\n : (req as IncomingMessage).headers.cookie) ?? \"\",\n )[cookieName] ?? \"\"\n );\n}\n\nfunction getServerActionCookie(\n cookieName: string,\n cookieHandler: CookieStore,\n): string {\n const cookieObject = cookieHandler.get(cookieName);\n const cookie = cookieObject?.value;\n if (typeof cookie === \"string\") {\n return cookie;\n }\n return \"\";\n}\n\nfunction setCookie(res: ResponseType, cookieValue: string): void {\n if (\"headers\" in res && typeof res.headers.append === \"function\") {\n res.headers.append(\"set-cookie\", cookieValue);\n return;\n }\n let existingSetCookie = (res as ServerResponse).getHeader(\"set-cookie\") ?? [];\n if (!Array.isArray(existingSetCookie)) {\n existingSetCookie = [existingSetCookie.toString()];\n }\n (res as ServerResponse).setHeader(\"set-cookie\", [\n ...existingSetCookie,\n cookieValue,\n ]);\n}\n\nexport function createSealData(_crypto: Crypto) {\n return async function sealData(\n data: unknown,\n {\n password,\n ttl = fourteenDaysInSeconds,\n }: { password: Password; ttl?: number },\n ): Promise<string> {\n const passwordsMap = normalizeStringPasswordToMap(password);\n\n const mostRecentPasswordId = Math.max(\n ...Object.keys(passwordsMap).map(Number),\n );\n const passwordForSeal = {\n id: mostRecentPasswordId.toString(),\n secret: passwordsMap[mostRecentPasswordId]!,\n };\n\n const seal = await ironSeal(_crypto, data, passwordForSeal, {\n ...ironDefaults,\n ttl: ttl * 1000,\n });\n\n return `${seal}${versionDelimiter}${currentMajorVersion}`;\n };\n}\n\nexport function createUnsealData(_crypto: Crypto) {\n return async function unsealData<T>(\n seal: string,\n {\n password,\n ttl = fourteenDaysInSeconds,\n }: { password: Password; ttl?: number },\n ): Promise<T> {\n const passwordsMap = normalizeStringPasswordToMap(password);\n const { sealWithoutVersion, tokenVersion } = parseSeal(seal);\n\n try {\n const data =\n (await ironUnseal(_crypto, sealWithoutVersion, passwordsMap, {\n ...ironDefaults,\n ttl: ttl * 1000,\n })) ?? {};\n\n if (tokenVersion === 2) {\n return data as T;\n }\n\n // @ts-expect-error `persistent` does not exist on newer tokens\n return { ...data.persistent } as T;\n } catch (error) {\n if (\n error instanceof Error &&\n /^(Expired seal|Bad hmac value|Cannot find password|Incorrect number of sealed components)/.test(\n error.message,\n )\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 throw error;\n }\n };\n}\n\nfunction getSessionConfig(\n sessionOptions: SessionOptions,\n): Required<SessionOptions> {\n const options = {\n ...defaultOptions,\n ...sessionOptions,\n cookieOptions: {\n ...defaultOptions.cookieOptions,\n ...(sessionOptions.cookieOptions || {}),\n },\n };\n\n if (\n sessionOptions.cookieOptions &&\n \"maxAge\" in sessionOptions.cookieOptions\n ) {\n if (sessionOptions.cookieOptions.maxAge === undefined) {\n // session cookies, do not set maxAge, consider token as infinite\n options.ttl = 0;\n }\n } else {\n options.cookieOptions.maxAge = computeCookieMaxAge(options.ttl);\n }\n\n return options;\n}\n\nconst badUsageMessage =\n \"iron-session: Bad usage: use getIronSession(req, res, options) or getIronSession(cookieStore, options).\";\n\nexport function createGetIronSession(\n sealData: ReturnType<typeof createSealData>,\n unsealData: ReturnType<typeof createUnsealData>,\n) {\n return getIronSession;\n\n async function getIronSession<T extends object>(\n cookies: CookieStore,\n sessionOptions: SessionOptions,\n ): Promise<IronSession<T>>;\n async function getIronSession<T extends object>(\n req: RequestType,\n res: ResponseType,\n sessionOptions: SessionOptions,\n ): Promise<IronSession<T>>;\n async function getIronSession<T extends object>(\n reqOrCookieStore: RequestType | CookieStore,\n resOrsessionOptions: ResponseType | SessionOptions,\n sessionOptions?: SessionOptions,\n ): Promise<IronSession<T>> {\n if (!reqOrCookieStore) {\n throw new Error(badUsageMessage);\n }\n\n if (!resOrsessionOptions) {\n throw new Error(badUsageMessage);\n }\n\n if (!sessionOptions) {\n return getIronSessionFromCookieStore<T>(\n reqOrCookieStore as CookieStore,\n resOrsessionOptions as SessionOptions,\n sealData,\n unsealData,\n );\n }\n\n const req = reqOrCookieStore as RequestType;\n const res = resOrsessionOptions as ResponseType;\n\n if (!sessionOptions) {\n throw new Error(badUsageMessage);\n }\n\n if (!sessionOptions.cookieName) {\n throw new Error(\"iron-session: Bad usage. Missing cookie name.\");\n }\n\n if (!sessionOptions.password) {\n throw new Error(\"iron-session: Bad usage. Missing password.\");\n }\n\n const passwordsMap = normalizeStringPasswordToMap(sessionOptions.password);\n\n if (Object.values(passwordsMap).some((password) => password.length < 32)) {\n throw new Error(\n \"iron-session: Bad usage. Password must be at least 32 characters long.\",\n );\n }\n\n let sessionConfig = getSessionConfig(sessionOptions);\n\n const sealFromCookies = getCookie(req, sessionConfig.cookieName);\n const session = sealFromCookies\n ? await unsealData<T>(sealFromCookies, {\n password: passwordsMap,\n ttl: sessionConfig.ttl,\n })\n : ({} as T);\n\n Object.defineProperties(session, {\n updateConfig: {\n value: function updateConfig(newSessionOptions: SessionOptions) {\n sessionConfig = getSessionConfig(newSessionOptions);\n },\n },\n save: {\n value: async function save() {\n if (\"headersSent\" in res && res.headersSent) {\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\n const seal = await sealData(session, {\n password: passwordsMap,\n ttl: sessionConfig.ttl,\n });\n const cookieValue = serialize(\n sessionConfig.cookieName,\n seal,\n sessionConfig.cookieOptions,\n );\n\n if (cookieValue.length > 4096) {\n throw new Error(\n `iron-session: Cookie length is too big (${cookieValue.length} bytes), browsers will refuse it. Try to remove some data.`,\n );\n }\n\n setCookie(res, cookieValue);\n },\n },\n\n destroy: {\n value: function destroy() {\n Object.keys(session).forEach((key) => {\n delete (session as Record<string, unknown>)[key];\n });\n const cookieValue = serialize(sessionConfig.cookieName, \"\", {\n ...sessionConfig.cookieOptions,\n maxAge: 0,\n });\n\n setCookie(res, cookieValue);\n },\n },\n });\n\n return session as IronSession<T>;\n }\n}\n\nasync function getIronSessionFromCookieStore<T extends object>(\n cookieStore: CookieStore,\n sessionOptions: SessionOptions,\n sealData: ReturnType<typeof createSealData>,\n unsealData: ReturnType<typeof createUnsealData>,\n): Promise<IronSession<T>> {\n if (!sessionOptions.cookieName) {\n throw new Error(\"iron-session: Bad usage. Missing cookie name.\");\n }\n\n if (!sessionOptions.password) {\n throw new Error(\"iron-session: Bad usage. Missing password.\");\n }\n\n const passwordsMap = normalizeStringPasswordToMap(sessionOptions.password);\n\n if (Object.values(passwordsMap).some((password) => password.length < 32)) {\n throw new Error(\n \"iron-session: Bad usage. Password must be at least 32 characters long.\",\n );\n }\n\n let sessionConfig = getSessionConfig(sessionOptions);\n const sealFromCookies = getServerActionCookie(\n sessionConfig.cookieName,\n cookieStore,\n );\n const session = sealFromCookies\n ? await unsealData<T>(sealFromCookies, {\n password: passwordsMap,\n ttl: sessionConfig.ttl,\n })\n : ({} as T);\n\n Object.defineProperties(session, {\n updateConfig: {\n value: function updateConfig(newSessionOptions: SessionOptions) {\n sessionConfig = getSessionConfig(newSessionOptions);\n },\n },\n save: {\n value: async function save() {\n const seal = await sealData(session, {\n password: passwordsMap,\n ttl: sessionConfig.ttl,\n });\n\n const cookieLength =\n sessionConfig.cookieName.length +\n seal.length +\n JSON.stringify(sessionConfig.cookieOptions).length;\n\n if (cookieLength > 4096) {\n throw new Error(\n `iron-session: Cookie length is too big (${cookieLength} bytes), browsers will refuse it. Try to remove some data.`,\n );\n }\n\n cookieStore.set(\n sessionConfig.cookieName,\n seal,\n sessionConfig.cookieOptions,\n );\n },\n },\n\n destroy: {\n value: function destroy() {\n Object.keys(session).forEach((key) => {\n delete (session as Record<string, unknown>)[key];\n });\n\n const cookieOptions = { ...sessionConfig.cookieOptions, maxAge: 0 };\n cookieStore.set(sessionConfig.cookieName, \"\", cookieOptions);\n },\n },\n });\n\n return session as IronSession<T>;\n}\n","import {\n createGetIronSession,\n createSealData,\n createUnsealData,\n} from \"./core.js\";\n\nimport * as crypto from \"uncrypto\";\n\nexport type { IronSession, SessionOptions } from \"./core.js\";\nexport const sealData = createSealData(crypto);\nexport const unsealData = createUnsealData(crypto);\nexport const getIronSession = createGetIronSession(sealData, unsealData);\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as http from 'http';
|
|
2
|
+
import { CookieSerializeOptions } from 'cookie';
|
|
3
|
+
|
|
4
|
+
type PasswordsMap = Record<string, string>;
|
|
5
|
+
type Password = PasswordsMap | string;
|
|
6
|
+
/**
|
|
7
|
+
* {@link https://wicg.github.io/cookie-store/#dictdef-cookielistitem CookieListItem}
|
|
8
|
+
* as specified by W3C.
|
|
9
|
+
*/
|
|
10
|
+
interface CookieListItem extends Pick<CookieSerializeOptions, "domain" | "path" | "sameSite" | "secure"> {
|
|
11
|
+
/** A string with the name of a cookie. */
|
|
12
|
+
name: string;
|
|
13
|
+
/** A string containing the value of the cookie. */
|
|
14
|
+
value: string;
|
|
15
|
+
/** A number of milliseconds or Date interface containing the expires of the cookie. */
|
|
16
|
+
expires?: CookieSerializeOptions["expires"] | number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Superset of {@link CookieListItem} extending it with
|
|
20
|
+
* the `httpOnly`, `maxAge` and `priority` properties.
|
|
21
|
+
*/
|
|
22
|
+
type ResponseCookie = CookieListItem & Pick<CookieSerializeOptions, "httpOnly" | "maxAge" | "priority">;
|
|
23
|
+
/**
|
|
24
|
+
* The high-level type definition of the .get() and .set() methods
|
|
25
|
+
* of { cookies() } from "next/headers"
|
|
26
|
+
*/
|
|
27
|
+
interface CookieStore {
|
|
28
|
+
get: (name: string) => {
|
|
29
|
+
name: string;
|
|
30
|
+
value: string;
|
|
31
|
+
} | undefined;
|
|
32
|
+
set: {
|
|
33
|
+
(name: string, value: string, cookie?: Partial<ResponseCookie>): void;
|
|
34
|
+
(options: ResponseCookie): void;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Set-Cookie Attributes do not include `encode`. We omit this from our `cookieOptions` type.
|
|
39
|
+
*
|
|
40
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
|
|
41
|
+
* @see https://developer.chrome.com/docs/devtools/application/cookies/
|
|
42
|
+
*/
|
|
43
|
+
type CookieOptions = Omit<CookieSerializeOptions, "encode">;
|
|
44
|
+
interface SessionOptions {
|
|
45
|
+
/**
|
|
46
|
+
* The cookie name that will be used inside the browser. Make sure it's unique
|
|
47
|
+
* given your application.
|
|
48
|
+
*
|
|
49
|
+
* @example 'vercel-session'
|
|
50
|
+
*/
|
|
51
|
+
cookieName: string;
|
|
52
|
+
/**
|
|
53
|
+
* The password(s) that will be used to encrypt the cookie. Can either be a string
|
|
54
|
+
* or an object.
|
|
55
|
+
*
|
|
56
|
+
* When you provide multiple passwords then all of them will be used to decrypt
|
|
57
|
+
* the cookie. But only the most recent (`= highest key`, `2` in the example)
|
|
58
|
+
* password will be used to encrypt the cookie. This allows password rotation.
|
|
59
|
+
*
|
|
60
|
+
* @example { 1: 'password-1', 2: 'password-2' }
|
|
61
|
+
*/
|
|
62
|
+
password: Password;
|
|
63
|
+
/**
|
|
64
|
+
* The time (in seconds) that the session will be valid for. Also sets the
|
|
65
|
+
* `max-age` attribute of the cookie automatically (`= ttl - 60s`, so that the
|
|
66
|
+
* cookie always expire before the session).
|
|
67
|
+
*
|
|
68
|
+
* `ttl = 0` means no expiration.
|
|
69
|
+
*
|
|
70
|
+
* @default 1209600
|
|
71
|
+
*/
|
|
72
|
+
ttl?: number;
|
|
73
|
+
/**
|
|
74
|
+
* The options that will be passed to the cookie library.
|
|
75
|
+
*
|
|
76
|
+
* If you want to use "session cookies" (cookies that are deleted when the browser
|
|
77
|
+
* is closed) then you need to pass `cookieOptions: { maxAge: undefined }`
|
|
78
|
+
*
|
|
79
|
+
* @see https://github.com/jshttp/cookie#options-1
|
|
80
|
+
*/
|
|
81
|
+
cookieOptions?: CookieOptions;
|
|
82
|
+
}
|
|
83
|
+
type IronSession<T> = T & {
|
|
84
|
+
/**
|
|
85
|
+
* Encrypts the session data and sets the cookie.
|
|
86
|
+
*/
|
|
87
|
+
readonly save: () => Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Destroys the session data and removes the cookie.
|
|
90
|
+
*/
|
|
91
|
+
readonly destroy: () => void;
|
|
92
|
+
/**
|
|
93
|
+
* Update the session configuration. You still need to call save() to send the new cookie.
|
|
94
|
+
*/
|
|
95
|
+
readonly updateConfig: (newSessionOptions: SessionOptions) => void;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
declare const sealData: (data: unknown, { password, ttl, }: {
|
|
99
|
+
password: string | {
|
|
100
|
+
[x: string]: string;
|
|
101
|
+
};
|
|
102
|
+
ttl?: number;
|
|
103
|
+
}) => Promise<string>;
|
|
104
|
+
declare const unsealData: <T>(seal: string, { password, ttl, }: {
|
|
105
|
+
password: string | {
|
|
106
|
+
[x: string]: string;
|
|
107
|
+
};
|
|
108
|
+
ttl?: number;
|
|
109
|
+
}) => Promise<T>;
|
|
110
|
+
declare const getIronSession: {
|
|
111
|
+
<T extends object>(cookies: CookieStore, sessionOptions: SessionOptions): Promise<IronSession<T>>;
|
|
112
|
+
<T extends object>(req: http.IncomingMessage | Request, res: Response | http.ServerResponse<http.IncomingMessage>, sessionOptions: SessionOptions): Promise<IronSession<T>>;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export { type IronSession, type SessionOptions, getIronSession, sealData, unsealData };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as http from 'http';
|
|
2
|
+
import { CookieSerializeOptions } from 'cookie';
|
|
3
|
+
|
|
4
|
+
type PasswordsMap = Record<string, string>;
|
|
5
|
+
type Password = PasswordsMap | string;
|
|
6
|
+
/**
|
|
7
|
+
* {@link https://wicg.github.io/cookie-store/#dictdef-cookielistitem CookieListItem}
|
|
8
|
+
* as specified by W3C.
|
|
9
|
+
*/
|
|
10
|
+
interface CookieListItem extends Pick<CookieSerializeOptions, "domain" | "path" | "sameSite" | "secure"> {
|
|
11
|
+
/** A string with the name of a cookie. */
|
|
12
|
+
name: string;
|
|
13
|
+
/** A string containing the value of the cookie. */
|
|
14
|
+
value: string;
|
|
15
|
+
/** A number of milliseconds or Date interface containing the expires of the cookie. */
|
|
16
|
+
expires?: CookieSerializeOptions["expires"] | number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Superset of {@link CookieListItem} extending it with
|
|
20
|
+
* the `httpOnly`, `maxAge` and `priority` properties.
|
|
21
|
+
*/
|
|
22
|
+
type ResponseCookie = CookieListItem & Pick<CookieSerializeOptions, "httpOnly" | "maxAge" | "priority">;
|
|
23
|
+
/**
|
|
24
|
+
* The high-level type definition of the .get() and .set() methods
|
|
25
|
+
* of { cookies() } from "next/headers"
|
|
26
|
+
*/
|
|
27
|
+
interface CookieStore {
|
|
28
|
+
get: (name: string) => {
|
|
29
|
+
name: string;
|
|
30
|
+
value: string;
|
|
31
|
+
} | undefined;
|
|
32
|
+
set: {
|
|
33
|
+
(name: string, value: string, cookie?: Partial<ResponseCookie>): void;
|
|
34
|
+
(options: ResponseCookie): void;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Set-Cookie Attributes do not include `encode`. We omit this from our `cookieOptions` type.
|
|
39
|
+
*
|
|
40
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
|
|
41
|
+
* @see https://developer.chrome.com/docs/devtools/application/cookies/
|
|
42
|
+
*/
|
|
43
|
+
type CookieOptions = Omit<CookieSerializeOptions, "encode">;
|
|
44
|
+
interface SessionOptions {
|
|
45
|
+
/**
|
|
46
|
+
* The cookie name that will be used inside the browser. Make sure it's unique
|
|
47
|
+
* given your application.
|
|
48
|
+
*
|
|
49
|
+
* @example 'vercel-session'
|
|
50
|
+
*/
|
|
51
|
+
cookieName: string;
|
|
52
|
+
/**
|
|
53
|
+
* The password(s) that will be used to encrypt the cookie. Can either be a string
|
|
54
|
+
* or an object.
|
|
55
|
+
*
|
|
56
|
+
* When you provide multiple passwords then all of them will be used to decrypt
|
|
57
|
+
* the cookie. But only the most recent (`= highest key`, `2` in the example)
|
|
58
|
+
* password will be used to encrypt the cookie. This allows password rotation.
|
|
59
|
+
*
|
|
60
|
+
* @example { 1: 'password-1', 2: 'password-2' }
|
|
61
|
+
*/
|
|
62
|
+
password: Password;
|
|
63
|
+
/**
|
|
64
|
+
* The time (in seconds) that the session will be valid for. Also sets the
|
|
65
|
+
* `max-age` attribute of the cookie automatically (`= ttl - 60s`, so that the
|
|
66
|
+
* cookie always expire before the session).
|
|
67
|
+
*
|
|
68
|
+
* `ttl = 0` means no expiration.
|
|
69
|
+
*
|
|
70
|
+
* @default 1209600
|
|
71
|
+
*/
|
|
72
|
+
ttl?: number;
|
|
73
|
+
/**
|
|
74
|
+
* The options that will be passed to the cookie library.
|
|
75
|
+
*
|
|
76
|
+
* If you want to use "session cookies" (cookies that are deleted when the browser
|
|
77
|
+
* is closed) then you need to pass `cookieOptions: { maxAge: undefined }`
|
|
78
|
+
*
|
|
79
|
+
* @see https://github.com/jshttp/cookie#options-1
|
|
80
|
+
*/
|
|
81
|
+
cookieOptions?: CookieOptions;
|
|
82
|
+
}
|
|
83
|
+
type IronSession<T> = T & {
|
|
84
|
+
/**
|
|
85
|
+
* Encrypts the session data and sets the cookie.
|
|
86
|
+
*/
|
|
87
|
+
readonly save: () => Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Destroys the session data and removes the cookie.
|
|
90
|
+
*/
|
|
91
|
+
readonly destroy: () => void;
|
|
92
|
+
/**
|
|
93
|
+
* Update the session configuration. You still need to call save() to send the new cookie.
|
|
94
|
+
*/
|
|
95
|
+
readonly updateConfig: (newSessionOptions: SessionOptions) => void;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
declare const sealData: (data: unknown, { password, ttl, }: {
|
|
99
|
+
password: string | {
|
|
100
|
+
[x: string]: string;
|
|
101
|
+
};
|
|
102
|
+
ttl?: number;
|
|
103
|
+
}) => Promise<string>;
|
|
104
|
+
declare const unsealData: <T>(seal: string, { password, ttl, }: {
|
|
105
|
+
password: string | {
|
|
106
|
+
[x: string]: string;
|
|
107
|
+
};
|
|
108
|
+
ttl?: number;
|
|
109
|
+
}) => Promise<T>;
|
|
110
|
+
declare const getIronSession: {
|
|
111
|
+
<T extends object>(cookies: CookieStore, sessionOptions: SessionOptions): Promise<IronSession<T>>;
|
|
112
|
+
<T extends object>(req: http.IncomingMessage | Request, res: Response | http.ServerResponse<http.IncomingMessage>, sessionOptions: SessionOptions): Promise<IronSession<T>>;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export { type IronSession, type SessionOptions, getIronSession, sealData, unsealData };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { serialize, parse } from 'cookie';
|
|
2
|
+
import { seal, defaults, unseal } from 'iron-webcrypto';
|
|
3
|
+
import * as crypto from 'uncrypto';
|
|
4
|
+
|
|
5
|
+
// src/core.ts
|
|
6
|
+
var timestampSkewSec = 60;
|
|
7
|
+
var fourteenDaysInSeconds = 14 * 24 * 3600;
|
|
8
|
+
var currentMajorVersion = 2;
|
|
9
|
+
var versionDelimiter = "~";
|
|
10
|
+
var defaultOptions = {
|
|
11
|
+
ttl: fourteenDaysInSeconds,
|
|
12
|
+
cookieOptions: { httpOnly: true, secure: true, sameSite: "lax", path: "/" }
|
|
13
|
+
};
|
|
14
|
+
function normalizeStringPasswordToMap(password) {
|
|
15
|
+
return typeof password === "string" ? { 1: password } : password;
|
|
16
|
+
}
|
|
17
|
+
function parseSeal(seal) {
|
|
18
|
+
const [sealWithoutVersion, tokenVersionAsString] = seal.split(versionDelimiter);
|
|
19
|
+
const tokenVersion = tokenVersionAsString == null ? null : parseInt(tokenVersionAsString, 10);
|
|
20
|
+
return { sealWithoutVersion, tokenVersion };
|
|
21
|
+
}
|
|
22
|
+
function computeCookieMaxAge(ttl) {
|
|
23
|
+
if (ttl === 0) {
|
|
24
|
+
return 2147483647;
|
|
25
|
+
}
|
|
26
|
+
return ttl - timestampSkewSec;
|
|
27
|
+
}
|
|
28
|
+
function getCookie(req, cookieName) {
|
|
29
|
+
return parse(
|
|
30
|
+
("headers" in req && typeof req.headers.get === "function" ? req.headers.get("cookie") : req.headers.cookie) ?? ""
|
|
31
|
+
)[cookieName] ?? "";
|
|
32
|
+
}
|
|
33
|
+
function getServerActionCookie(cookieName, cookieHandler) {
|
|
34
|
+
const cookieObject = cookieHandler.get(cookieName);
|
|
35
|
+
const cookie = cookieObject?.value;
|
|
36
|
+
if (typeof cookie === "string") {
|
|
37
|
+
return cookie;
|
|
38
|
+
}
|
|
39
|
+
return "";
|
|
40
|
+
}
|
|
41
|
+
function setCookie(res, cookieValue) {
|
|
42
|
+
if ("headers" in res && typeof res.headers.append === "function") {
|
|
43
|
+
res.headers.append("set-cookie", cookieValue);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
let existingSetCookie = res.getHeader("set-cookie") ?? [];
|
|
47
|
+
if (!Array.isArray(existingSetCookie)) {
|
|
48
|
+
existingSetCookie = [existingSetCookie.toString()];
|
|
49
|
+
}
|
|
50
|
+
res.setHeader("set-cookie", [
|
|
51
|
+
...existingSetCookie,
|
|
52
|
+
cookieValue
|
|
53
|
+
]);
|
|
54
|
+
}
|
|
55
|
+
function createSealData(_crypto) {
|
|
56
|
+
return async function sealData2(data, {
|
|
57
|
+
password,
|
|
58
|
+
ttl = fourteenDaysInSeconds
|
|
59
|
+
}) {
|
|
60
|
+
const passwordsMap = normalizeStringPasswordToMap(password);
|
|
61
|
+
const mostRecentPasswordId = Math.max(
|
|
62
|
+
...Object.keys(passwordsMap).map(Number)
|
|
63
|
+
);
|
|
64
|
+
const passwordForSeal = {
|
|
65
|
+
id: mostRecentPasswordId.toString(),
|
|
66
|
+
secret: passwordsMap[mostRecentPasswordId]
|
|
67
|
+
};
|
|
68
|
+
const seal$1 = await seal(_crypto, data, passwordForSeal, {
|
|
69
|
+
...defaults,
|
|
70
|
+
ttl: ttl * 1e3
|
|
71
|
+
});
|
|
72
|
+
return `${seal$1}${versionDelimiter}${currentMajorVersion}`;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function createUnsealData(_crypto) {
|
|
76
|
+
return async function unsealData2(seal, {
|
|
77
|
+
password,
|
|
78
|
+
ttl = fourteenDaysInSeconds
|
|
79
|
+
}) {
|
|
80
|
+
const passwordsMap = normalizeStringPasswordToMap(password);
|
|
81
|
+
const { sealWithoutVersion, tokenVersion } = parseSeal(seal);
|
|
82
|
+
try {
|
|
83
|
+
const data = await unseal(_crypto, sealWithoutVersion, passwordsMap, {
|
|
84
|
+
...defaults,
|
|
85
|
+
ttl: ttl * 1e3
|
|
86
|
+
}) ?? {};
|
|
87
|
+
if (tokenVersion === 2) {
|
|
88
|
+
return data;
|
|
89
|
+
}
|
|
90
|
+
return { ...data.persistent };
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (error instanceof Error && /^(Expired seal|Bad hmac value|Cannot find password|Incorrect number of sealed components)/.test(
|
|
93
|
+
error.message
|
|
94
|
+
)) {
|
|
95
|
+
return {};
|
|
96
|
+
}
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function getSessionConfig(sessionOptions) {
|
|
102
|
+
const options = {
|
|
103
|
+
...defaultOptions,
|
|
104
|
+
...sessionOptions,
|
|
105
|
+
cookieOptions: {
|
|
106
|
+
...defaultOptions.cookieOptions,
|
|
107
|
+
...sessionOptions.cookieOptions || {}
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
if (sessionOptions.cookieOptions && "maxAge" in sessionOptions.cookieOptions) {
|
|
111
|
+
if (sessionOptions.cookieOptions.maxAge === void 0) {
|
|
112
|
+
options.ttl = 0;
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
options.cookieOptions.maxAge = computeCookieMaxAge(options.ttl);
|
|
116
|
+
}
|
|
117
|
+
return options;
|
|
118
|
+
}
|
|
119
|
+
var badUsageMessage = "iron-session: Bad usage: use getIronSession(req, res, options) or getIronSession(cookieStore, options).";
|
|
120
|
+
function createGetIronSession(sealData2, unsealData2) {
|
|
121
|
+
return getIronSession2;
|
|
122
|
+
async function getIronSession2(reqOrCookieStore, resOrsessionOptions, sessionOptions) {
|
|
123
|
+
if (!reqOrCookieStore) {
|
|
124
|
+
throw new Error(badUsageMessage);
|
|
125
|
+
}
|
|
126
|
+
if (!resOrsessionOptions) {
|
|
127
|
+
throw new Error(badUsageMessage);
|
|
128
|
+
}
|
|
129
|
+
if (!sessionOptions) {
|
|
130
|
+
return getIronSessionFromCookieStore(
|
|
131
|
+
reqOrCookieStore,
|
|
132
|
+
resOrsessionOptions,
|
|
133
|
+
sealData2,
|
|
134
|
+
unsealData2
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
const req = reqOrCookieStore;
|
|
138
|
+
const res = resOrsessionOptions;
|
|
139
|
+
if (!sessionOptions) {
|
|
140
|
+
throw new Error(badUsageMessage);
|
|
141
|
+
}
|
|
142
|
+
if (!sessionOptions.cookieName) {
|
|
143
|
+
throw new Error("iron-session: Bad usage. Missing cookie name.");
|
|
144
|
+
}
|
|
145
|
+
if (!sessionOptions.password) {
|
|
146
|
+
throw new Error("iron-session: Bad usage. Missing password.");
|
|
147
|
+
}
|
|
148
|
+
const passwordsMap = normalizeStringPasswordToMap(sessionOptions.password);
|
|
149
|
+
if (Object.values(passwordsMap).some((password) => password.length < 32)) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
"iron-session: Bad usage. Password must be at least 32 characters long."
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
let sessionConfig = getSessionConfig(sessionOptions);
|
|
155
|
+
const sealFromCookies = getCookie(req, sessionConfig.cookieName);
|
|
156
|
+
const session = sealFromCookies ? await unsealData2(sealFromCookies, {
|
|
157
|
+
password: passwordsMap,
|
|
158
|
+
ttl: sessionConfig.ttl
|
|
159
|
+
}) : {};
|
|
160
|
+
Object.defineProperties(session, {
|
|
161
|
+
updateConfig: {
|
|
162
|
+
value: function updateConfig(newSessionOptions) {
|
|
163
|
+
sessionConfig = getSessionConfig(newSessionOptions);
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
save: {
|
|
167
|
+
value: async function save() {
|
|
168
|
+
if ("headersSent" in res && res.headersSent) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
"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()"
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
const seal = await sealData2(session, {
|
|
174
|
+
password: passwordsMap,
|
|
175
|
+
ttl: sessionConfig.ttl
|
|
176
|
+
});
|
|
177
|
+
const cookieValue = serialize(
|
|
178
|
+
sessionConfig.cookieName,
|
|
179
|
+
seal,
|
|
180
|
+
sessionConfig.cookieOptions
|
|
181
|
+
);
|
|
182
|
+
if (cookieValue.length > 4096) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`iron-session: Cookie length is too big (${cookieValue.length} bytes), browsers will refuse it. Try to remove some data.`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
setCookie(res, cookieValue);
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
destroy: {
|
|
191
|
+
value: function destroy() {
|
|
192
|
+
Object.keys(session).forEach((key) => {
|
|
193
|
+
delete session[key];
|
|
194
|
+
});
|
|
195
|
+
const cookieValue = serialize(sessionConfig.cookieName, "", {
|
|
196
|
+
...sessionConfig.cookieOptions,
|
|
197
|
+
maxAge: 0
|
|
198
|
+
});
|
|
199
|
+
setCookie(res, cookieValue);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
return session;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async function getIronSessionFromCookieStore(cookieStore, sessionOptions, sealData2, unsealData2) {
|
|
207
|
+
if (!sessionOptions.cookieName) {
|
|
208
|
+
throw new Error("iron-session: Bad usage. Missing cookie name.");
|
|
209
|
+
}
|
|
210
|
+
if (!sessionOptions.password) {
|
|
211
|
+
throw new Error("iron-session: Bad usage. Missing password.");
|
|
212
|
+
}
|
|
213
|
+
const passwordsMap = normalizeStringPasswordToMap(sessionOptions.password);
|
|
214
|
+
if (Object.values(passwordsMap).some((password) => password.length < 32)) {
|
|
215
|
+
throw new Error(
|
|
216
|
+
"iron-session: Bad usage. Password must be at least 32 characters long."
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
let sessionConfig = getSessionConfig(sessionOptions);
|
|
220
|
+
const sealFromCookies = getServerActionCookie(
|
|
221
|
+
sessionConfig.cookieName,
|
|
222
|
+
cookieStore
|
|
223
|
+
);
|
|
224
|
+
const session = sealFromCookies ? await unsealData2(sealFromCookies, {
|
|
225
|
+
password: passwordsMap,
|
|
226
|
+
ttl: sessionConfig.ttl
|
|
227
|
+
}) : {};
|
|
228
|
+
Object.defineProperties(session, {
|
|
229
|
+
updateConfig: {
|
|
230
|
+
value: function updateConfig(newSessionOptions) {
|
|
231
|
+
sessionConfig = getSessionConfig(newSessionOptions);
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
save: {
|
|
235
|
+
value: async function save() {
|
|
236
|
+
const seal = await sealData2(session, {
|
|
237
|
+
password: passwordsMap,
|
|
238
|
+
ttl: sessionConfig.ttl
|
|
239
|
+
});
|
|
240
|
+
const cookieLength = sessionConfig.cookieName.length + seal.length + JSON.stringify(sessionConfig.cookieOptions).length;
|
|
241
|
+
if (cookieLength > 4096) {
|
|
242
|
+
throw new Error(
|
|
243
|
+
`iron-session: Cookie length is too big (${cookieLength} bytes), browsers will refuse it. Try to remove some data.`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
cookieStore.set(
|
|
247
|
+
sessionConfig.cookieName,
|
|
248
|
+
seal,
|
|
249
|
+
sessionConfig.cookieOptions
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
destroy: {
|
|
254
|
+
value: function destroy() {
|
|
255
|
+
Object.keys(session).forEach((key) => {
|
|
256
|
+
delete session[key];
|
|
257
|
+
});
|
|
258
|
+
const cookieOptions = { ...sessionConfig.cookieOptions, maxAge: 0 };
|
|
259
|
+
cookieStore.set(sessionConfig.cookieName, "", cookieOptions);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
return session;
|
|
264
|
+
}
|
|
265
|
+
var sealData = createSealData(crypto);
|
|
266
|
+
var unsealData = createUnsealData(crypto);
|
|
267
|
+
var getIronSession = createGetIronSession(sealData, unsealData);
|
|
268
|
+
|
|
269
|
+
export { getIronSession, sealData, unsealData };
|
|
270
|
+
//# sourceMappingURL=index.js.map
|
|
271
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core.ts","../src/index.ts"],"names":["sealData","seal","ironSeal","ironDefaults","unsealData","ironUnseal","getIronSession"],"mappings":"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","file":"index.js","sourcesContent":["import type { IncomingMessage, ServerResponse } from \"http\";\nimport { parse, serialize, type CookieSerializeOptions } from \"cookie\";\nimport {\n defaults as ironDefaults,\n seal as ironSeal,\n unseal as ironUnseal,\n} from \"iron-webcrypto\";\n\ntype PasswordsMap = Record<string, string>;\ntype Password = PasswordsMap | string;\ntype RequestType = IncomingMessage | Request;\ntype ResponseType = Response | ServerResponse;\n\n/**\n * {@link https://wicg.github.io/cookie-store/#dictdef-cookielistitem CookieListItem}\n * as specified by W3C.\n */\ninterface CookieListItem\n extends Pick<\n CookieSerializeOptions,\n \"domain\" | \"path\" | \"sameSite\" | \"secure\"\n > {\n /** A string with the name of a cookie. */\n name: string;\n /** A string containing the value of the cookie. */\n value: string;\n /** A number of milliseconds or Date interface containing the expires of the cookie. */\n expires?: CookieSerializeOptions[\"expires\"] | number;\n}\n\n/**\n * Superset of {@link CookieListItem} extending it with\n * the `httpOnly`, `maxAge` and `priority` properties.\n */\ntype ResponseCookie = CookieListItem &\n Pick<CookieSerializeOptions, \"httpOnly\" | \"maxAge\" | \"priority\">;\n\n/**\n * The high-level type definition of the .get() and .set() methods\n * of { cookies() } from \"next/headers\"\n */\nexport interface CookieStore {\n get: (name: string) => { name: string; value: string } | undefined;\n set: {\n (name: string, value: string, cookie?: Partial<ResponseCookie>): void;\n (options: ResponseCookie): void;\n };\n}\n\n/**\n * Set-Cookie Attributes do not include `encode`. We omit this from our `cookieOptions` type.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie\n * @see https://developer.chrome.com/docs/devtools/application/cookies/\n */\ntype CookieOptions = Omit<CookieSerializeOptions, \"encode\">;\n\nexport interface SessionOptions {\n /**\n * The cookie name that will be used inside the browser. Make sure it's unique\n * given your application.\n *\n * @example 'vercel-session'\n */\n cookieName: string;\n\n /**\n * The password(s) that will be used to encrypt the cookie. Can either be a string\n * or an object.\n *\n * When you provide multiple passwords then all of them will be used to decrypt\n * the cookie. But only the most recent (`= highest key`, `2` in the example)\n * password will be used to encrypt the cookie. This allows password rotation.\n *\n * @example { 1: 'password-1', 2: 'password-2' }\n */\n password: Password;\n\n /**\n * The time (in seconds) that the session will be valid for. Also sets the\n * `max-age` attribute of the cookie automatically (`= ttl - 60s`, so that the\n * cookie always expire before the session).\n *\n * `ttl = 0` means no expiration.\n *\n * @default 1209600\n */\n ttl?: number;\n\n /**\n * The options that will be passed to the cookie library.\n *\n * If you want to use \"session cookies\" (cookies that are deleted when the browser\n * is closed) then you need to pass `cookieOptions: { maxAge: undefined }`\n *\n * @see https://github.com/jshttp/cookie#options-1\n */\n cookieOptions?: CookieOptions;\n}\n\nexport type IronSession<T> = T & {\n /**\n * Encrypts the session data and sets the cookie.\n */\n readonly save: () => Promise<void>;\n\n /**\n * Destroys the session data and removes the cookie.\n */\n readonly destroy: () => void;\n\n /**\n * Update the session configuration. You still need to call save() to send the new cookie.\n */\n readonly updateConfig: (newSessionOptions: SessionOptions) => void;\n};\n\n// default time allowed to check for iron seal validity when ttl passed\n// see https://hapi.dev/module/iron/api/?v=7.0.1#options\nconst timestampSkewSec = 60;\nconst fourteenDaysInSeconds = 14 * 24 * 3600;\n\n// We store a token major version to handle data format changes so that the cookies\n// can be kept alive between upgrades, no need to disconnect everyone.\nconst currentMajorVersion = 2;\nconst versionDelimiter = \"~\";\n\nconst defaultOptions: Required<Pick<SessionOptions, \"ttl\" | \"cookieOptions\">> =\n {\n ttl: fourteenDaysInSeconds,\n cookieOptions: { httpOnly: true, secure: true, sameSite: \"lax\", path: \"/\" },\n };\n\nfunction normalizeStringPasswordToMap(password: Password): PasswordsMap {\n return typeof password === \"string\" ? { 1: password } : password;\n}\n\nfunction parseSeal(seal: string): {\n sealWithoutVersion: string;\n tokenVersion: number | null;\n} {\n const [sealWithoutVersion, tokenVersionAsString] =\n seal.split(versionDelimiter);\n const tokenVersion =\n tokenVersionAsString == null ? null : parseInt(tokenVersionAsString, 10);\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return { sealWithoutVersion: sealWithoutVersion!, tokenVersion };\n}\n\nfunction computeCookieMaxAge(ttl: number): number {\n if (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 return 2147483647;\n }\n\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 between server and clients.\n return ttl - timestampSkewSec;\n}\n\nfunction getCookie(req: RequestType, cookieName: string): string {\n return (\n parse(\n (\"headers\" in req && typeof req.headers.get === \"function\"\n ? req.headers.get(\"cookie\")\n : (req as IncomingMessage).headers.cookie) ?? \"\",\n )[cookieName] ?? \"\"\n );\n}\n\nfunction getServerActionCookie(\n cookieName: string,\n cookieHandler: CookieStore,\n): string {\n const cookieObject = cookieHandler.get(cookieName);\n const cookie = cookieObject?.value;\n if (typeof cookie === \"string\") {\n return cookie;\n }\n return \"\";\n}\n\nfunction setCookie(res: ResponseType, cookieValue: string): void {\n if (\"headers\" in res && typeof res.headers.append === \"function\") {\n res.headers.append(\"set-cookie\", cookieValue);\n return;\n }\n let existingSetCookie = (res as ServerResponse).getHeader(\"set-cookie\") ?? [];\n if (!Array.isArray(existingSetCookie)) {\n existingSetCookie = [existingSetCookie.toString()];\n }\n (res as ServerResponse).setHeader(\"set-cookie\", [\n ...existingSetCookie,\n cookieValue,\n ]);\n}\n\nexport function createSealData(_crypto: Crypto) {\n return async function sealData(\n data: unknown,\n {\n password,\n ttl = fourteenDaysInSeconds,\n }: { password: Password; ttl?: number },\n ): Promise<string> {\n const passwordsMap = normalizeStringPasswordToMap(password);\n\n const mostRecentPasswordId = Math.max(\n ...Object.keys(passwordsMap).map(Number),\n );\n const passwordForSeal = {\n id: mostRecentPasswordId.toString(),\n secret: passwordsMap[mostRecentPasswordId]!,\n };\n\n const seal = await ironSeal(_crypto, data, passwordForSeal, {\n ...ironDefaults,\n ttl: ttl * 1000,\n });\n\n return `${seal}${versionDelimiter}${currentMajorVersion}`;\n };\n}\n\nexport function createUnsealData(_crypto: Crypto) {\n return async function unsealData<T>(\n seal: string,\n {\n password,\n ttl = fourteenDaysInSeconds,\n }: { password: Password; ttl?: number },\n ): Promise<T> {\n const passwordsMap = normalizeStringPasswordToMap(password);\n const { sealWithoutVersion, tokenVersion } = parseSeal(seal);\n\n try {\n const data =\n (await ironUnseal(_crypto, sealWithoutVersion, passwordsMap, {\n ...ironDefaults,\n ttl: ttl * 1000,\n })) ?? {};\n\n if (tokenVersion === 2) {\n return data as T;\n }\n\n // @ts-expect-error `persistent` does not exist on newer tokens\n return { ...data.persistent } as T;\n } catch (error) {\n if (\n error instanceof Error &&\n /^(Expired seal|Bad hmac value|Cannot find password|Incorrect number of sealed components)/.test(\n error.message,\n )\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 throw error;\n }\n };\n}\n\nfunction getSessionConfig(\n sessionOptions: SessionOptions,\n): Required<SessionOptions> {\n const options = {\n ...defaultOptions,\n ...sessionOptions,\n cookieOptions: {\n ...defaultOptions.cookieOptions,\n ...(sessionOptions.cookieOptions || {}),\n },\n };\n\n if (\n sessionOptions.cookieOptions &&\n \"maxAge\" in sessionOptions.cookieOptions\n ) {\n if (sessionOptions.cookieOptions.maxAge === undefined) {\n // session cookies, do not set maxAge, consider token as infinite\n options.ttl = 0;\n }\n } else {\n options.cookieOptions.maxAge = computeCookieMaxAge(options.ttl);\n }\n\n return options;\n}\n\nconst badUsageMessage =\n \"iron-session: Bad usage: use getIronSession(req, res, options) or getIronSession(cookieStore, options).\";\n\nexport function createGetIronSession(\n sealData: ReturnType<typeof createSealData>,\n unsealData: ReturnType<typeof createUnsealData>,\n) {\n return getIronSession;\n\n async function getIronSession<T extends object>(\n cookies: CookieStore,\n sessionOptions: SessionOptions,\n ): Promise<IronSession<T>>;\n async function getIronSession<T extends object>(\n req: RequestType,\n res: ResponseType,\n sessionOptions: SessionOptions,\n ): Promise<IronSession<T>>;\n async function getIronSession<T extends object>(\n reqOrCookieStore: RequestType | CookieStore,\n resOrsessionOptions: ResponseType | SessionOptions,\n sessionOptions?: SessionOptions,\n ): Promise<IronSession<T>> {\n if (!reqOrCookieStore) {\n throw new Error(badUsageMessage);\n }\n\n if (!resOrsessionOptions) {\n throw new Error(badUsageMessage);\n }\n\n if (!sessionOptions) {\n return getIronSessionFromCookieStore<T>(\n reqOrCookieStore as CookieStore,\n resOrsessionOptions as SessionOptions,\n sealData,\n unsealData,\n );\n }\n\n const req = reqOrCookieStore as RequestType;\n const res = resOrsessionOptions as ResponseType;\n\n if (!sessionOptions) {\n throw new Error(badUsageMessage);\n }\n\n if (!sessionOptions.cookieName) {\n throw new Error(\"iron-session: Bad usage. Missing cookie name.\");\n }\n\n if (!sessionOptions.password) {\n throw new Error(\"iron-session: Bad usage. Missing password.\");\n }\n\n const passwordsMap = normalizeStringPasswordToMap(sessionOptions.password);\n\n if (Object.values(passwordsMap).some((password) => password.length < 32)) {\n throw new Error(\n \"iron-session: Bad usage. Password must be at least 32 characters long.\",\n );\n }\n\n let sessionConfig = getSessionConfig(sessionOptions);\n\n const sealFromCookies = getCookie(req, sessionConfig.cookieName);\n const session = sealFromCookies\n ? await unsealData<T>(sealFromCookies, {\n password: passwordsMap,\n ttl: sessionConfig.ttl,\n })\n : ({} as T);\n\n Object.defineProperties(session, {\n updateConfig: {\n value: function updateConfig(newSessionOptions: SessionOptions) {\n sessionConfig = getSessionConfig(newSessionOptions);\n },\n },\n save: {\n value: async function save() {\n if (\"headersSent\" in res && res.headersSent) {\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\n const seal = await sealData(session, {\n password: passwordsMap,\n ttl: sessionConfig.ttl,\n });\n const cookieValue = serialize(\n sessionConfig.cookieName,\n seal,\n sessionConfig.cookieOptions,\n );\n\n if (cookieValue.length > 4096) {\n throw new Error(\n `iron-session: Cookie length is too big (${cookieValue.length} bytes), browsers will refuse it. Try to remove some data.`,\n );\n }\n\n setCookie(res, cookieValue);\n },\n },\n\n destroy: {\n value: function destroy() {\n Object.keys(session).forEach((key) => {\n delete (session as Record<string, unknown>)[key];\n });\n const cookieValue = serialize(sessionConfig.cookieName, \"\", {\n ...sessionConfig.cookieOptions,\n maxAge: 0,\n });\n\n setCookie(res, cookieValue);\n },\n },\n });\n\n return session as IronSession<T>;\n }\n}\n\nasync function getIronSessionFromCookieStore<T extends object>(\n cookieStore: CookieStore,\n sessionOptions: SessionOptions,\n sealData: ReturnType<typeof createSealData>,\n unsealData: ReturnType<typeof createUnsealData>,\n): Promise<IronSession<T>> {\n if (!sessionOptions.cookieName) {\n throw new Error(\"iron-session: Bad usage. Missing cookie name.\");\n }\n\n if (!sessionOptions.password) {\n throw new Error(\"iron-session: Bad usage. Missing password.\");\n }\n\n const passwordsMap = normalizeStringPasswordToMap(sessionOptions.password);\n\n if (Object.values(passwordsMap).some((password) => password.length < 32)) {\n throw new Error(\n \"iron-session: Bad usage. Password must be at least 32 characters long.\",\n );\n }\n\n let sessionConfig = getSessionConfig(sessionOptions);\n const sealFromCookies = getServerActionCookie(\n sessionConfig.cookieName,\n cookieStore,\n );\n const session = sealFromCookies\n ? await unsealData<T>(sealFromCookies, {\n password: passwordsMap,\n ttl: sessionConfig.ttl,\n })\n : ({} as T);\n\n Object.defineProperties(session, {\n updateConfig: {\n value: function updateConfig(newSessionOptions: SessionOptions) {\n sessionConfig = getSessionConfig(newSessionOptions);\n },\n },\n save: {\n value: async function save() {\n const seal = await sealData(session, {\n password: passwordsMap,\n ttl: sessionConfig.ttl,\n });\n\n const cookieLength =\n sessionConfig.cookieName.length +\n seal.length +\n JSON.stringify(sessionConfig.cookieOptions).length;\n\n if (cookieLength > 4096) {\n throw new Error(\n `iron-session: Cookie length is too big (${cookieLength} bytes), browsers will refuse it. Try to remove some data.`,\n );\n }\n\n cookieStore.set(\n sessionConfig.cookieName,\n seal,\n sessionConfig.cookieOptions,\n );\n },\n },\n\n destroy: {\n value: function destroy() {\n Object.keys(session).forEach((key) => {\n delete (session as Record<string, unknown>)[key];\n });\n\n const cookieOptions = { ...sessionConfig.cookieOptions, maxAge: 0 };\n cookieStore.set(sessionConfig.cookieName, \"\", cookieOptions);\n },\n },\n });\n\n return session as IronSession<T>;\n}\n","import {\n createGetIronSession,\n createSealData,\n createUnsealData,\n} from \"./core.js\";\n\nimport * as crypto from \"uncrypto\";\n\nexport type { IronSession, SessionOptions } from \"./core.js\";\nexport const sealData = createSealData(crypto);\nexport const unsealData = createUnsealData(crypto);\nexport const getIronSession = createGetIronSession(sealData, unsealData);\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opensourceframework/next-iron-session",
|
|
3
|
+
"version": "8.0.4",
|
|
4
|
+
"description": "Secure, stateless, and cookie-based session library for JavaScript",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"session",
|
|
7
|
+
"secure",
|
|
8
|
+
"stateless",
|
|
9
|
+
"cookie",
|
|
10
|
+
"encryption",
|
|
11
|
+
"security",
|
|
12
|
+
"next.js",
|
|
13
|
+
"node.js"
|
|
14
|
+
],
|
|
15
|
+
"bugs": "https://github.com/vvo/iron-session/issues",
|
|
16
|
+
"repository": "github:vvo/iron-session",
|
|
17
|
+
"funding": [
|
|
18
|
+
"https://github.com/sponsors/vvo",
|
|
19
|
+
"https://github.com/sponsors/brc-dd"
|
|
20
|
+
],
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"author": "OpenSource Framework Contributors (fork), Original: Unknown",
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"type": "module",
|
|
25
|
+
"exports": {
|
|
26
|
+
"import": "./dist/index.js",
|
|
27
|
+
"require": "./dist/index.cjs"
|
|
28
|
+
},
|
|
29
|
+
"main": "./dist/index.cjs",
|
|
30
|
+
"files": [
|
|
31
|
+
"dist/*"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsup",
|
|
35
|
+
"dev": "pnpm build && concurrently \"pnpm build --watch\" \"pnpm --filter=next-example dev\" ",
|
|
36
|
+
"lint": "tsc --noEmit && tsc --noEmit -p examples/next/tsconfig.json && pnpm eslint . && publint",
|
|
37
|
+
"prepare": "pnpm build",
|
|
38
|
+
"start": "turbo start --filter=next-example",
|
|
39
|
+
"test": "vitest run && pnpm build",
|
|
40
|
+
"test:watch": "vitest"
|
|
41
|
+
},
|
|
42
|
+
"prettier": {
|
|
43
|
+
"plugins": [
|
|
44
|
+
"prettier-plugin-packagejson"
|
|
45
|
+
],
|
|
46
|
+
"trailingComma": "all"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"cookie": "^0.7.2",
|
|
50
|
+
"iron-webcrypto": "^1.2.1",
|
|
51
|
+
"uncrypto": "^0.1.3"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/cookie": "0.6.0",
|
|
55
|
+
"@types/node": "20.17.24",
|
|
56
|
+
"@typescript-eslint/eslint-plugin": "7.18.0",
|
|
57
|
+
"@typescript-eslint/parser": "7.18.0",
|
|
58
|
+
"c8": "10.1.3",
|
|
59
|
+
"concurrently": "8.2.2",
|
|
60
|
+
"eslint": "8.57.1",
|
|
61
|
+
"eslint-config-prettier": "9.1.0",
|
|
62
|
+
"eslint-import-resolver-node": "0.3.9",
|
|
63
|
+
"eslint-import-resolver-typescript": "3.9.1",
|
|
64
|
+
"eslint-plugin-import": "2.31.0",
|
|
65
|
+
"eslint-plugin-prettier": "5.2.5",
|
|
66
|
+
"prettier": "3.5.3",
|
|
67
|
+
"prettier-plugin-packagejson": "2.5.10",
|
|
68
|
+
"publint": "0.3.9",
|
|
69
|
+
"tsup": "8.4.0",
|
|
70
|
+
"tsx": "4.19.3",
|
|
71
|
+
"turbo": "^2.0.5",
|
|
72
|
+
"typescript": "5.8.2",
|
|
73
|
+
"vitest": "^4.0.18"
|
|
74
|
+
},
|
|
75
|
+
"packageManager": "pnpm@9.6.0",
|
|
76
|
+
"publishConfig": {
|
|
77
|
+
"access": "public"
|
|
78
|
+
},
|
|
79
|
+
"contributors": [
|
|
80
|
+
{
|
|
81
|
+
"name": "Unknown",
|
|
82
|
+
"url": "https://github.com/next-iron-session"
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
"homepage": "https://github.com/riceharvest/opensourceframework/tree/main/packages/next-iron-session#readme",
|
|
86
|
+
"engines": {
|
|
87
|
+
"node": ">=18.0.0"
|
|
88
|
+
}
|
|
89
|
+
}
|