iron-session 2.0.0-alpha.9 → 6.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,103 +1,656 @@
1
- # TSDX User Guide
1
+ # iron-session [![GitHub license](https://img.shields.io/github/license/vvo/iron-session?style=flat)](https://github.com/vvo/iron-session/blob/master/LICENSE) [![Tests](https://github.com/vvo/iron-session/workflows/Tests/badge.svg)](https://github.com/vvo/iron-session/actions) ![npm](https://img.shields.io/npm/v/iron-session) [![Downloads](https://img.shields.io/npm/dm/next-iron-session.svg)](http://npm-stat.com/charts.html?package=iron-session)
2
2
 
3
- Congrats! You just saved yourself hours of work by bootstrapping this project with TSDX. Let’s get you oriented with what’s here and how to use it.
3
+ <p align="center"><b>⭐️ Featured in the <a href="https://nextjs.org/docs/authentication">Next.js documentation</a></b></p>
4
4
 
5
- > This TSDX setup is meant for developing libraries (not apps!) that can be published to NPM. If you’re looking to build a Node app, you could use `ts-node-dev`, plain `ts-node`, or simple `tsc`.
5
+ **⚠️ Nov 2021 update**: The library was renamed to `iron-session` and fully rewritten in TypeScript, it includes lots of new features and fixes. Follow the migration guide here: https://github.com/vvo/iron-session/releases/tag/v6.0.0.
6
6
 
7
- > If you’re new to TypeScript, checkout [this handy cheatsheet](https://devhints.io/typescript)
7
+ _🛠 Node.js stateless session utility using signed and encrypted cookies to store data. Works with Next.js, Express, NestJs, Fastify, and any Node.js HTTP framework._
8
8
 
9
- ## Commands
9
+ The session data is stored in encrypted cookies ("seals"). And only your server can decode the session data. There are no session ids, making iron sessions "stateless" from the server point of view.
10
10
 
11
- TSDX scaffolds your new library inside `/src`.
11
+ This strategy of storing session data is the same technique used by **frameworks like [Ruby On Rails](https://guides.rubyonrails.org/security.html#session-storage)** (their default strategy).
12
12
 
13
- To run TSDX, use:
13
+ The underlying cryptography library is [iron](https://hapi.dev/module/iron) which was [created by the lead developer of OAuth 2.0](https://hueniversedotcom.wordpress.com/2015/09/19/auth-to-see-the-wizard-or-i-wrote-an-oauth-replacement/).
14
+
15
+ <p align="center"><b>Online demo at <a href="https://iron-session-example.vercel.app/">https://iron-session-example.vercel.app</a> 👀</b></p>
16
+
17
+ ---
18
+
19
+ _Table of contents:_
20
+
21
+ - [Installation](#installation)
22
+ - [Usage (Next.js)](#usage-nextjs)
23
+ - [Advanced usage](#advanced-usage)
24
+ - [Coding best practices](#coding-best-practices)
25
+ - [Session wrappers](#session-wrappers)
26
+ - [Typing session data with TypeScript](#typing-session-data-with-typescript)
27
+ - [Express](#express)
28
+ - [Handle password rotation/update the password](#handle-password-rotationupdate-the-password)
29
+ - [Magic links](#magic-links)
30
+ - [Impersonation, login as someone else](#impersonation-login-as-someone-else)
31
+ - [Session cookies](#session-cookies)
32
+ - [Firebase usage](#firebase-usage)
33
+ - [API](#api)
34
+ - [ironOptions](#ironoptions)
35
+ - [Next.js: withIronSessionApiRoute(handler, ironOptions)](#nextjs-withironsessionapiroutehandler-ironoptions)
36
+ - [Next.js: withIronSessionSsr(handler, ironOptions)](#nextjs-withironsessionssrhandler-ironoptions)
37
+ - [Express: ironSession(ironOptions)](#express-ironsessionironoptions)
38
+ - [session.save()](#sessionsave)
39
+ - [session.destroy()](#sessiondestroy)
40
+ - [FAQ](#faq)
41
+ - [Why use pure cookies for sessions?](#why-use-pure-cookies-for-sessions)
42
+ - [What are the drawbacks?](#what-are-the-drawbacks)
43
+ - [How is this different from JWT?](#how-is-this-different-from-jwt)
44
+ - [Project status](#project-status)
45
+ - [Credits](#credits)
46
+ - [References](#references)
47
+ - [Contributors](#contributors)
48
+
49
+ ## Installation
14
50
 
15
51
  ```bash
16
- npm start # or yarn start
52
+ npm add iron-session
53
+ ```
54
+
55
+ ## Usage (Next.js)
56
+
57
+ You can find full featured examples (Next.js, Express) in the [examples folder](examples).
58
+
59
+ The password is a private key you must pass at runtime and builtime (for getServerSideProps), it has to be at least 32 characters long. You can use https://1password.com/password-generator/ to generate strong passwords.
60
+
61
+ Session duration is 14 days by default, check the API docs for more info.
62
+
63
+ ⚠️ Always store passwords in encrypted environment variables on your platform. Vercel does this automatically.
64
+
65
+ **Login API Route:**
66
+
67
+ ```ts
68
+ // pages/api/login.ts
69
+
70
+ import { withIronSessionApiRoute } from "iron-session/next";
71
+
72
+ export default withIronSessionApiRoute(
73
+ async function loginRoute(req, res) {
74
+ // get user from database then:
75
+ req.session.user = {
76
+ id: 230,
77
+ admin: true,
78
+ };
79
+ await req.session.save();
80
+ res.send({ ok: true });
81
+ },
82
+ {
83
+ cookieName: "myapp_cookiename",
84
+ password: "complex_password_at_least_32_characters_long",
85
+ },
86
+ );
87
+ ```
88
+
89
+ **User API Route:**
90
+
91
+ ```ts
92
+ // pages/api/user.ts
93
+
94
+ import { withIronSessionApiRoute } from "iron-session/next";
95
+
96
+ export default withIronSessionApiRoute(
97
+ function userRoute(req, res) {
98
+ res.send({ user: req.session.user });
99
+ },
100
+ {
101
+ cookieName: "myapp_cookiename",
102
+ password: "complex_password_at_least_32_characters_long",
103
+ },
104
+ );
105
+ ```
106
+
107
+ **Logout Route:**
108
+
109
+ ```ts
110
+ // pages/api/logout.ts
111
+
112
+ import { withIronSessionApiRoute } from "iron-session/next";
113
+
114
+ export default withIronSessionApiRoute(
115
+ function logoutRoute(req, res, session) {
116
+ req.session.destroy();
117
+ res.send({ ok: true });
118
+ },
119
+ {
120
+ cookieName: "myapp_cookiename",
121
+ password: "complex_password_at_least_32_characters_long",
122
+ },
123
+ );
124
+ ```
125
+
126
+ **getServerSideProps:**
127
+
128
+ ```ts
129
+ // pages/admin.tsx
130
+
131
+ import { withIronSessionSsr } from "iron-session/next";
132
+
133
+ export const getServerSideProps = withIronSessionSsr(
134
+ async function getServerSideProps({ req }) {
135
+ const user = req.session.user;
136
+
137
+ if (user.admin !== true) {
138
+ return {
139
+ notFound: true,
140
+ };
141
+ }
142
+
143
+ return {
144
+ props: {
145
+ user: req.session.user,
146
+ },
147
+ };
148
+ },
149
+ {
150
+ cookieName: "myapp_cookiename",
151
+ password: "complex_password_at_least_32_characters_long",
152
+ },
153
+ );
154
+ ```
155
+
156
+ Note: We encourage you to create a `withSession` utility so you do not have to repeat the password and cookie name in every route. You can see how to do that [in the example](./examples/next.js-typescript/lib/session.ts).
157
+
158
+ ## Advanced usage
159
+
160
+ ### Coding best practices
161
+
162
+ Here's an example Login API route that is easier to read because of less nesting:
163
+
164
+ ```ts
165
+ // pages/api/login.ts
166
+
167
+ import { withIronSessionApiRoute } from "iron-session/next";
168
+ import { ironOptions } from "lib/config";
169
+
170
+ export default withIronSessionApiRoute(loginRoute, ironOptions);
171
+
172
+ async function loginRoute(req, res) {
173
+ // get user from database then:
174
+ req.session.user = {
175
+ id: 230,
176
+ admin: true,
177
+ };
178
+ await req.session.save();
179
+ res.send({ ok: true });
180
+ }
181
+ );
182
+ ```
183
+
184
+ ```ts
185
+ // lib/config.ts
186
+
187
+ export const ironOptions = {
188
+ cookieName: "myapp_cookiename",
189
+ password: "complex_password_at_least_32_characters_long",
190
+ };
191
+ ```
192
+
193
+ ### Session wrappers
194
+
195
+ If you do not want to pass down the password and cookie name in every API route file or page then you can create wrappers like this:
196
+
197
+ **JavaScript:**
198
+
199
+ ```js
200
+ // lib/withSession.js
201
+
202
+ import { withIronSessionApiRoute, withIronSessionSsr } from "iron-session/next";
203
+
204
+ const sessionOptions = {
205
+ password: "complex_password_at_least_32_characters_long",
206
+ cookieName: "myapp_cookiename",
207
+ };
208
+
209
+ export function withSessionRoute(handler) {
210
+ return withIronSessionApiRoute(handler, sessionOptions);
211
+ }
212
+
213
+ export function withSessionSsr(handler) {
214
+ return withIronSessionSsr(handler, sessionOptions);
215
+ }
17
216
  ```
18
217
 
19
- This builds to `/dist` and runs the project in watch mode so any edits you save inside `src` causes a rebuild to `/dist`.
218
+ **TypeScript:**
20
219
 
21
- To do a one-off build, use `npm run build` or `yarn build`.
220
+ ```ts
221
+ // lib/withSession.ts
22
222
 
23
- To run tests, use `npm test` or `yarn test`.
223
+ import { withIronSessionApiRoute, withIronSessionSsr } from "iron-session/next";
224
+ import {
225
+ GetServerSidePropsContext,
226
+ GetServerSidePropsResult,
227
+ NextApiHandler,
228
+ } from "next";
24
229
 
25
- ## Configuration
230
+ const sessionOptions = {
231
+ password: "complex_password_at_least_32_characters_long",
232
+ cookieName: "myapp_cookiename",
233
+ };
26
234
 
27
- Code quality is set up for you with `prettier`, `husky`, and `lint-staged`. Adjust the respective fields in `package.json` accordingly.
235
+ export function withSessionRoute(handler: NextApiHandler) {
236
+ return withIronSessionApiRoute(handler, sessionOptions);
237
+ }
28
238
 
29
- ### Jest
239
+ // Theses types are compatible with InferGetStaticPropsType https://nextjs.org/docs/basic-features/data-fetching#typescript-use-getstaticprops
240
+ export function withSessionSsr<
241
+ P extends { [key: string]: unknown } = { [key: string]: unknown },
242
+ >(
243
+ handler: (
244
+ context: GetServerSidePropsContext,
245
+ ) => GetServerSidePropsResult<P> | Promise<GetServerSidePropsResult<P>>,
246
+ ) {
247
+ return withIronSessionSsr(handler, sessionOptions);
248
+ }
249
+ ```
250
+
251
+ **Usage in API Routes:**
252
+
253
+ ```ts
254
+ // pages/api/login.ts
255
+
256
+ import { withSessionApiRoute } from "lib/withSession";
30
257
 
31
- Jest tests are set up to run with `npm test` or `yarn test`.
258
+ export default withSessionApiRoute(loginRoute);
32
259
 
33
- ### Bundle Analysis
260
+ async function loginRoute(req, res) {
261
+ // get user from database then:
262
+ req.session.user = {
263
+ id: 230,
264
+ admin: true,
265
+ };
266
+ await req.session.save();
267
+ res.send("Logged in");
268
+ }
269
+ ```
270
+
271
+ **Usage in getServerSideProps:**
34
272
 
35
- [`size-limit`](https://github.com/ai/size-limit) is set up to calculate the real cost of your library with `npm run size` and visualize the bundle with `npm run analyze`.
273
+ ```ts
274
+ // pages/admin.tsx
36
275
 
37
- #### Setup Files
276
+ import { withSessionSsr } from "lib/withSession";
38
277
 
39
- This is the folder structure we set up for you:
278
+ export const getServerSideProps = withIronSessionSsr(
279
+ async function getServerSideProps({ req }) {
280
+ const user = req.session.user;
40
281
 
41
- ```txt
42
- /src
43
- index.tsx # EDIT THIS
44
- /test
45
- blah.test.tsx # EDIT THIS
46
- .gitignore
47
- package.json
48
- README.md # EDIT THIS
49
- tsconfig.json
282
+ if (user.admin !== true) {
283
+ return {
284
+ notFound: true,
285
+ };
286
+ }
287
+
288
+ return {
289
+ props: {
290
+ user: req.session.user,
291
+ },
292
+ };
293
+ },
294
+ );
50
295
  ```
51
296
 
52
- ### Rollup
297
+ ### Typing session data with TypeScript
53
298
 
54
- TSDX uses [Rollup](https://rollupjs.org) as a bundler and generates multiple rollup configs for various module formats and build settings. See [Optimizations](#optimizations) for details.
299
+ `req.session` is automatically populated with the right types so .save() and .destroy() can be called on it.
55
300
 
56
- ### TypeScript
301
+ But you might want to go further and type your session data also. To do so, use [module augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation):
57
302
 
58
- `tsconfig.json` is set up to interpret `dom` and `esnext` types, as well as `react` for `jsx`. Adjust according to your needs.
303
+ ```ts
304
+ declare module "iron-session" {
305
+ interface IronSessionData {
306
+ user?: {
307
+ id: number;
308
+ admin?: boolean;
309
+ };
310
+ }
311
+ }
312
+ ```
313
+
314
+ You can put this code anywhere in your project, as long as it is in a file that will be required at some point. For example it could be inside your `lib/withSession.ts` wrapper or inside an [`additional.d.ts`](https://nextjs.org/docs/basic-features/typescript) if you're using Next.js.
315
+
316
+ We've taken this technique from [express-session types](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/express-session). If you have any comment on
317
+
318
+ ### Express
319
+
320
+ See [examples/express](examples/express) for an example of how to use this with Express.
59
321
 
60
- ## Continuous Integration
322
+ ### Handle password rotation/update the password
61
323
 
62
- ### GitHub Actions
324
+ When you want to:
63
325
 
64
- Two actions are added by default:
326
+ - rotate passwords for better security every two (or more, or less) weeks
327
+ - change the password you previously used because it leaked somewhere (😱)
65
328
 
66
- - `main` which installs deps w/ cache, lints, tests, and builds on all pushes against a Node and OS matrix
67
- - `size` which comments cost comparison of your library on every pull request using [`size-limit`](https://github.com/ai/size-limit)
329
+ Then you can use multiple passwords:
68
330
 
69
- ## Optimizations
331
+ **Week 1**:
70
332
 
71
- Please see the main `tsdx` [optimizations docs](https://github.com/palmerhq/tsdx#optimizations). In particular, know that you can take advantage of development-only optimizations:
333
+ ```js
334
+ withIronSessionApiRoute(handler, {
335
+ password: {
336
+ 1: "complex_password_at_least_32_characters_long",
337
+ },
338
+ });
339
+ ```
340
+
341
+ **Week 2**:
72
342
 
73
343
  ```js
74
- // ./types/index.d.ts
75
- declare var __DEV__: boolean;
344
+ withIronSessionApiRoute(handler, {
345
+ password: {
346
+ 2: "another_password_at_least_32_characters_long",
347
+ 1: "complex_password_at_least_32_characters_long",
348
+ },
349
+ });
350
+ ```
351
+
352
+ **Notes:**
353
+
354
+ - The password used to encrypt session data (to `seal`) is always the highest number found in the map (2 in the example).
355
+ - The passwords used to decrypt session data are all passwords in the map (this is how rotation works).
356
+ - Even if you do not provide a list at first, you can always move to multiple passwords afterwards. The first password you've used has a default id of 1.
357
+
358
+ ### Magic links
359
+
360
+ Because of the stateless nature of `iron-session`, it's very easy to implement patterns like magic links. For example, you might want to send an email to the user with a link to a page where they will be automatically logged in. Or you might want to send a Slack message to someone with a link to your application where they will be automatically logged in.
361
+
362
+ Here's how to implement that:
363
+
364
+ **Send an email with a magic link to the user**:
365
+
366
+ ```ts
367
+ // pages/api/sendEmail.ts
368
+
369
+ import { sealData } from "iron-session";
370
+
371
+ export default async function sendEmailRoute(req, res) {
372
+ const user = getUserFromDatabase(req.query.userId);
373
+
374
+ const seal = await sealData(
375
+ {
376
+ userId: user.id,
377
+ },
378
+ {
379
+ password: "complex_password_at_least_32_characters_long",
380
+ },
381
+ );
382
+
383
+ await sendEmail(
384
+ user.email,
385
+ "Magic link",
386
+ `Hey there ${user.name}, <a href="https://myapp.com/api/magicLogin?seal=${seal}">click here to login</a>.`,
387
+ );
76
388
 
77
- // inside your code...
78
- if (__DEV__) {
79
- console.log('foo');
389
+ res.send({ ok: true });
80
390
  }
81
391
  ```
82
392
 
83
- You can also choose to install and use [invariant](https://github.com/palmerhq/tsdx#invariant) and [warning](https://github.com/palmerhq/tsdx#warning) functions.
393
+ **Login the user automatically and redirect:**
394
+
395
+ ```ts
396
+ // pages/api/magicLogin.ts
397
+
398
+ import { unsealData } from "iron-session";
399
+ import { withIronSessionApiRoute } from "iron-session/next";
400
+
401
+ const ironOptions = {
402
+ password: "complex_password_at_least_32_characters_long",
403
+ };
404
+
405
+ export default withIronSessionApiRoute(magicLoginRoute, ironOptions);
406
+
407
+ async function magicLoginRoute(req, res) {
408
+ const { userId } = await unsealData(req.query.seal, ironOptions);
409
+
410
+ const user = getUserFromDatabase(userId);
411
+
412
+ req.session.user = {
413
+ id: user.id,
414
+ };
415
+
416
+ await req.session.save();
417
+
418
+ res.redirect(`/dashboard`);
419
+ }
420
+ ```
421
+
422
+ You might want to include error handling in the API routes. For example checking if `req.session.user` is already defined in login or handling bad seals.
423
+
424
+ ### Impersonation, login as someone else
425
+
426
+ You may want to impersonate your own users, to check how they see your application. This can be extremely useful. For example you could have a page that list all your users and with links you can click to impersonate them.
427
+
428
+ **Login as someone else:**
429
+
430
+ ```ts
431
+ // pages/api/impersonate.ts
432
+
433
+ import { withIronSessionApiRoute } from "iron-session/next";
434
+
435
+ export default withIronSessionApiRoute(impersonateRoute, {
436
+ password: "complex_password_at_least_32_characters_long",
437
+ });
438
+
439
+ async function impersonateRoute(req, res) {
440
+ if (!req.session.isAdmin) {
441
+ // let's pretend this route does not exists if user is not an admin
442
+ return res.status(404).end();
443
+ }
444
+
445
+ req.session.originalUser = req.session.originalUser || req.session.user;
446
+ req.session.user = {
447
+ id: req.query.userId,
448
+ };
449
+ await req.session.save();
450
+ res.redirect("/dashboard");
451
+ }
452
+ ```
453
+
454
+ **Stop impersonation:**
455
+
456
+ ```ts
457
+ // pages/api/stopImpersonate.ts
458
+
459
+ import { withIronSessionApiRoute } from "iron-session/next";
460
+
461
+ export default withIronSessionApiRoute(stopImpersonateRoute, {
462
+ password: "complex_password_at_least_32_characters_long",
463
+ });
464
+
465
+ async function stopImpersonateRoute(req, res) {
466
+ if (!req.session.isAdmin) {
467
+ // let's pretend this route does not exists if user is not an admin
468
+ return res.status(404).end();
469
+ }
470
+
471
+ req.session.user = req.session.originalUser;
472
+ delete req.session.originalUser;
473
+ await req.session.save();
474
+ res.redirect("/dashboard");
475
+ }
476
+ ```
477
+
478
+ ### Session cookies
479
+
480
+ If you want cookies to expire when the user closes the browser, pass `maxAge: undefined` in cookie options, this way:
481
+
482
+ ```ts
483
+ // pages/api/user.ts
484
+
485
+ import { withIronSessionApiRoute } from "iron-session/next";
486
+
487
+ export default withIronSessionApiRoute(
488
+ function userRoute(req, res) {
489
+ res.send({ user: req.session.user });
490
+ },
491
+ {
492
+ cookieName: "myapp_cookiename",
493
+ password: "complex_password_at_least_32_characters_long",
494
+ cookieOptions: { maxAge: undefined },
495
+ },
496
+ );
497
+ ```
498
+
499
+ Beware, modern browsers might not delete cookies at all using this technique because of [session restoring](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#define_the_lifetime_of_a_cookie).
500
+
501
+ ### Firebase usage
502
+
503
+ This library can be used with Firebase, as long as you set the cookie name to `__session` which seems to be the only valid cookie name there.
504
+
505
+ ## API
506
+
507
+ ### ironOptions
508
+
509
+ Only two options are required: `password` and `cookieName`. Everything else is automatically computed and usually doesn't need to be changed.
510
+
511
+ - `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 `array` of objects like this: `[{id: 2, password: "..."}, {id: 1, password: "..."}]` to allow for password rotation.
512
+ - `cookieName`, **required**: Name of the cookie to be stored
513
+ - `ttl`, _optional_: In seconds, default to 14 days
514
+ - [`cookieOptions`](https://github.com/jshttp/cookie#cookieserializename-value-options), _optional_: Any option available from [jshttp/cookie#serialize](https://github.com/jshttp/cookie#cookieserializename-value-options). Default to:
515
+
516
+ ```js
517
+ {
518
+ httpOnly: true,
519
+ secure: true, // true when using https, false otherwise
520
+ sameSite: "lax", // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#lax
521
+ // The next line makes sure browser will expire cookies before seals are considered expired by the server. It also allows for clock difference of 60 seconds maximum between servers and clients.
522
+ maxAge: (ttl === 0 ? 2147483647 : ttl) - 60,
523
+ path: "/",
524
+ // other options:
525
+ // domain, if you want the cookie to be valid for the whole domain and subdomains, use domain: example.com
526
+ // encode, there should be no need to use this option, encoding is done by iron-session already
527
+ // expires, there should be no need to use this option, maxAge takes precedence
528
+ }
529
+ ```
530
+
531
+ ### Next.js: withIronSessionApiRoute(handler, ironOptions)
532
+
533
+ Wraps a [Next.js API Route](https://nextjs.org/docs/api-routes/dynamic-api-routes) and adds a `session` object to the request.
534
+
535
+ ```ts
536
+ import { withIronSessionApiRoute } from "iron-session/next";
537
+
538
+ export default withIronSessionApiRoute(
539
+ function userRoute(req, res) {
540
+ res.send({ user: req.session.user });
541
+ },
542
+ {
543
+ cookieName: "myapp_cookiename",
544
+ password: "complex_password_at_least_32_characters_long",
545
+ },
546
+ );
547
+ ```
548
+
549
+ ### Next.js: withIronSessionSsr(handler, ironOptions)
550
+
551
+ Wraps a [Next.js getServerSideProps](https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering) and adds a `session` object to the request of the context.
552
+
553
+ ```ts
554
+ import { withIronSessionSsr } from "iron-session/next";
555
+
556
+ export const getServerSideProps = withIronSessionSsr(
557
+ async function getServerSideProps({ req }) {
558
+ return {
559
+ props: {
560
+ user: req.session.user,
561
+ },
562
+ };
563
+ },
564
+ {
565
+ cookieName: "myapp_cookiename",
566
+ password: "complex_password_at_least_32_characters_long",
567
+ },
568
+ );
569
+ ```
570
+
571
+ ### Express: ironSession(ironOptions)
572
+
573
+ Creates an express middleware that adds a `session` object to the request.
574
+
575
+ ```js
576
+ import { ironSession } from "iron-session";
577
+
578
+ app.use(ironSession(ironOptions));
579
+ ```
580
+
581
+ ### session.save()
582
+
583
+ Saves the session and sets the cookie header to be sent once the response is sent.
584
+
585
+ ```ts
586
+ await req.session.save();
587
+ ```
588
+
589
+ ### session.destroy()
590
+
591
+ Empties the session object and sets the cookie header to be sent once the response is sent. The browser will then remove the cookie automatically.
592
+
593
+ You don't have to call `req.session.save()` after calling `req.session.destroy()`. The session is saved automatically.
594
+
595
+ ## FAQ
596
+
597
+ ### Why use pure cookies for sessions?
598
+
599
+ This makes your sessions stateless: you do not have to store session data on your server. You do not need another server or service to store session data. This is particularly useful in serverless architectures where you're trying to reduce your backend dependencies.
600
+
601
+ ### What are the drawbacks?
602
+
603
+ There are some drawbacks to this approach:
604
+
605
+ - you cannot invalidate a seal when needed because there's no state stored on the server-side about them. We consider that the way the cookie is stored reduces the possibility for this eventuality to happen. Also, in most applications the first thing you do when receiving an authenticated request is to validate the user and their rights in your database, which defeats the case where someone would try to use a token while their account was deactivated/deleted. Now if someone steals a user token you should have a process in place to mitigate that: deactivate the user and force a re-login with a flag in your database for example.
606
+ - application not supporting cookies won't work, but you can use [iron-store](https://github.com/vvo/iron-store/) to implement something similar. In the future, we could allow `iron-session` to accept [basic auth](https://tools.ietf.org/html/rfc7617) or bearer token methods too. Open an issue if you're interested.
607
+ - on most browsers, you're limited to 4,096 bytes per cookie. To give you an idea, an `iron-session` cookie containing `{user: {id: 100}}` is 265 bytes signed and encrypted: still plenty of available cookie space in here.
608
+ - performance: crypto on the server-side could be slow, if that's the case let me know. Also, cookies are sent to every request to your website, even images, so this could be an issue
609
+
610
+ Now that you know the drawbacks, you can decide if they are an issue for your application or not.
611
+ 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.
612
+
613
+ ### How is this different from [JWT](https://jwt.io/)?
614
+
615
+ Not so much:
616
+
617
+ - JWT is a standard, it stores metadata in the JWT token themselves to ensure communication between different systems is flawless.
618
+ - 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.
619
+ - @hapi/iron mechanism is not a standard, it's a way to sign and encrypt data into seals
620
+
621
+ Depending on your own needs and preferences, `iron-session` may or may not fit you.
622
+
623
+ ## Project status
624
+
625
+ ✅ Production ready and maintained.
626
+
627
+ ## Credits
84
628
 
85
- ## Module Formats
629
+ Thanks to [Hoang Vo](https://github.com/hoangvvo) for advice and guidance while building this module. Hoang built [next-connect](https://github.com/hoangvvo/next-connect) and [next-session](https://github.com/hoangvvo/next-session).
86
630
 
87
- CJS, ESModules, and UMD module formats are supported.
631
+ Thanks to [hapi](https://hapi.dev/) team for creating [iron](https://github.com/hapijs/iron).
88
632
 
89
- The appropriate paths are configured in `package.json` and `dist/index.js` accordingly. Please report if any issues are found.
633
+ ## References
90
634
 
91
- ## Named Exports
635
+ - https://owasp.org/www-project-cheat-sheets/cheatsheets/Session_Management_Cheat_Sheet.html#cookies
636
+ - https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#samesite-cookie-attribute
92
637
 
93
- Per Palmer Group guidelines, [always use named exports.](https://github.com/palmerhq/typescript#exports) Code split inside your React app instead of your React library.
638
+ ## Contributors
94
639
 
95
- ## Including Styles
640
+ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
96
641
 
97
- There are many ways to ship styles, including with CSS-in-JS. TSDX has no opinion on this, configure how you like.
642
+ <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
643
+ <!-- prettier-ignore-start -->
644
+ <!-- markdownlint-disable -->
645
+ <table>
646
+ <tr>
647
+ <td align="center"><a href="http://www.afterecon.com/"><img src="https://avatars.githubusercontent.com/u/5559355?v=4?s=100" width="100px;" alt=""/><br /><sub><b>John Vandivier</b></sub></a><br /><a href="https://github.com/vvo/iron-session/commits?author=Vandivier" title="Code">💻</a> <a href="#ideas-Vandivier" title="Ideas, Planning, & Feedback">🤔</a> <a href="#example-Vandivier" title="Examples">💡</a></td>
648
+ </tr>
649
+ </table>
98
650
 
99
- For vanilla CSS, you can include it at the root directory and add it to the `files` section in your `package.json`, so that it can be imported separately by your users and run through their bundler's loader.
651
+ <!-- markdownlint-restore -->
652
+ <!-- prettier-ignore-end -->
100
653
 
101
- ## Publishing to NPM
654
+ <!-- ALL-CONTRIBUTORS-LIST:END -->
102
655
 
103
- We recommend using [np](https://github.com/sindresorhus/np).
656
+ This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!