@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 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 ![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/vvo/iron-session/ci.yaml) [![GitHub license](https://img.shields.io/github/license/vvo/iron-session?style=flat)](https://github.com/vvo/iron-session/blob/master/LICENSE) [![npm](https://img.shields.io/npm/v/iron-session)](https://www.npmjs.com/package/iron-session) ![npm](https://img.shields.io/npm/dm/iron-session) ![npm package minimized gzipped size (select exports)](https://img.shields.io/bundlejs/size/iron-session?exports=getIronSession)
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"]}
@@ -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 };
@@ -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
+ }