Skip to content

Commit 997c307

Browse files
feat: add server side session storage
1 parent 404acc6 commit 997c307

File tree

6 files changed

+300
-21
lines changed

6 files changed

+300
-21
lines changed

README.md

Lines changed: 146 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,22 @@ export default defineNitroPlugin(() => {
514514
})
515515
```
516516

517+
### Extending Cookie Lifetime
518+
519+
When using cookie mode, only the session data itself can be enriched through the session hooks. If you want to extend the lifetime of the cookie itself, you could use a middleware that refreshes the cookie by re-setting the session data. (At this point you can also add any other custom data to your cookies)
520+
521+
Example middleware:
522+
523+
```ts
524+
// server/middleware/cookie-lifetime-extend.ts
525+
export default defineEventHandler((event) => {
526+
const session = await getUserSession(event)
527+
if (session && Object.keys(session).length > 0) {
528+
await setUserSession(event, session)
529+
}
530+
}
531+
```
532+
517533
## Server-Side Rendering
518534
519535
You can make authenticated requests both from the client and the server. However, you must use `useRequestFetch()` to make authenticated requests during SSR if you are not using `useFetch()`
@@ -579,42 +595,158 @@ If you are caching your routes with `routeRules`, please make sure to use [Nitro
579595
580596
## Configuration
581597
582-
We leverage `runtimeConfig.session` to give the defaults option to [h3 `useSession`](https://h3.unjs.io/examples/handle-session).
598+
### Session Storage
583599
584-
You can overwrite the options in your `nuxt.config.ts`:
600+
Nuxt Auth Utils supports different session storage modes that can be configured in your `nuxt.config.ts`:
585601
586602
```ts
587603
export default defineNuxtConfig({
588604
modules: ['nuxt-auth-utils'],
605+
auth: {
606+
storageType: 'cookie', // 'memory', 'cache', 'nuxt-session'
607+
}
608+
})
609+
```
610+
611+
#### Storage Types
612+
613+
- **`cookie`** (default): Stores session data in encrypted cookies. This is the most secure option and works well for most use cases.
614+
615+
```ts
616+
auth: {
617+
storageType: 'cookie'
618+
}
619+
```
620+
621+
- **`cache`**: Uses Nitro's cache storage. Useful when you need to store larger session data that might exceed cookie size limits.
622+
623+
```ts
624+
auth: {
625+
storageType: 'cache'
626+
}
627+
```
628+
629+
- **`memory`**: Stores sessions in memory. Only suitable for development or testing.
630+
631+
```ts
632+
auth: {
633+
storageType: 'memory'
634+
}
635+
```
636+
637+
> [!WARNING]
638+
> Memory storage is cleared when the server restarts and doesn't work with multiple server instances. Not recommended for production use.
639+
640+
- **`nuxt-session`**: Uses a custom storage mount named 'nuxt-session'. Useful when you want to use a different storage driver.
641+
642+
```ts
643+
// nuxt.config.ts
644+
export default defineNuxtConfig({
645+
auth: {
646+
storageType: 'nuxt-session'
647+
},
648+
nitro: {
649+
storage: {
650+
'nuxt-session': {
651+
driver: 'fsLite',
652+
base: './.data/sessions'
653+
}
654+
}
655+
}
656+
})
657+
```
658+
659+
> [!NOTE]
660+
> This will store sessions in the `.data/sessions` directory. Make sure to add `.data` to your `.gitignore`.
661+
662+
#### Session Configuration
663+
664+
You can configure session behavior through the `auth` or `runtimeConfig` options:
665+
666+
```ts
667+
export default defineNuxtConfig({
668+
auth: {
669+
storageType: 'cookie'
670+
},
589671
runtimeConfig: {
590672
session: {
591-
maxAge: 60 * 60 * 24 * 7 // 1 week
673+
name: 'nuxt-session', // Cookie name
674+
maxAge: 60 * 60 * 24 * 7, // 1 week
675+
password: process.env.NUXT_SESSION_PASSWORD,
676+
cookie: {
677+
sameSite: 'lax',
678+
// Additional cookie options
679+
// secure: true,
680+
// domain: 'example.com',
681+
// path: '/'
682+
}
592683
}
593684
}
594685
})
595686
```
596687
597-
Our defaults are:
688+
We leverage `runtimeConfig.session` to give the defaults option to [h3 `useSession`](https://h3.unjs.io/examples/handle-session).
689+
Checkout the [`SessionConfig`](https://github.com/unjs/h3/blob/c04c458810e34eb15c1647e1369e7d7ef19f567d/src/utils/session.ts#L20) for all options.
690+
691+
> [!NOTE]
692+
> When using non-cookie storage types, the cookie only contains a session ID while the actual session data is stored in the selected storage.
693+
694+
When using a non-cookie mode
598695
599696
```ts
600-
{
601-
name: 'nuxt-session',
602-
password: process.env.NUXT_SESSION_PASSWORD || '',
603-
cookie: {
604-
sameSite: 'lax'
697+
export default defineNuxtConfig({
698+
auth: {
699+
storageType: 'cache',
700+
sessionInactivityMaxAge: 60 * 60 * 24 * 30, // Session timeout after inactivity (30 days)
701+
autoExtendSession: true // Extend session on each request
702+
},
703+
runtimeConfig: {
704+
session: {
705+
password: process.env.NUXT_SESSION_PASSWORD,
706+
}
605707
}
606-
}
708+
})
709+
```
710+
711+
> [!IMPORTANT]
712+
> The `sessionInactivityMaxAge` option is specifically designed for non-cookie storage types to manage and cleanup inactive sessions. When using this configuration, cookies still respect the `maxAge` setting from the session configuration, if one is specified. Whether you need both `maxAge` and `sessionInactivityMaxAge` will depend on your specific application requirements and session management strategy.
713+
714+
## Session Cleanup
715+
716+
When using non-cookie storage types, you may want to clean up expired sessions periodically. This can be done using Nitro's scheduled tasks feature.
717+
718+
1. Create a task file:
719+
720+
```ts:server/tasks/clear-sessions.ts
721+
export default defineTask({
722+
meta: {
723+
name: 'clear-sessions',
724+
description: 'Clear expired sessions',
725+
},
726+
run({ payload, context }) {
727+
console.log('Running clear-sessions task...')
728+
cleanupOrphanedUserSessions()
729+
return { result: 'Success' }
730+
},
731+
})
607732
```
608733
609-
You can also overwrite the session config by passing it as 3rd argument of the `setUserSession` and `replaceUserSession` functions:
734+
2. Configure the task schedule in your `nuxt.config.ts`:
610735
611736
```ts
612-
await setUserSession(event, { ... } , {
613-
maxAge: 60 * 60 * 24 * 7 // 1 week
737+
export default defineNuxtConfig({
738+
nitro: {
739+
experimental: {
740+
tasks: true
741+
},
742+
scheduledTasks: {
743+
'*/5 * * * *': ['clear-sessions'] // Run every 5 minutes
744+
}
745+
}
614746
})
615747
```
616748
617-
Checkout the [`SessionConfig`](https://github.com/unjs/h3/blob/c04c458810e34eb15c1647e1369e7d7ef19f567d/src/utils/session.ts#L20) for all options.
749+
This will automatically clean up any expired sessions based on your `sessionInactivityMaxAge` configuration.
618750
619751
## More
620752

playground/nuxt.config.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,21 @@ export default defineNuxtConfig({
2020
nitro: {
2121
experimental: {
2222
database: true,
23+
// tasks: true,
2324
},
25+
// scheduledTasks: {
26+
// '*/1 * * * *': ['clear-sessions'], // every minute clear overdue sessions
27+
// },
2428
},
2529
auth: {
2630
webAuthn: true,
31+
// storageType: 'cache',
32+
// sessionInactivityMaxAge: 60 * 2, // 2 minutes
33+
// autoExtendSession: true,
2734
},
35+
// runtimeConfig: {
36+
// session: {
37+
// maxAge: 60 * 60 * 24 * 7, // 7 days
38+
// },
39+
// },
2840
})
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export default defineTask({
2+
meta: {
3+
name: 'clear-sessions',
4+
description: 'Clear expired sessions',
5+
},
6+
run({ payload, context }) {
7+
console.log('Running clear-sessions task...')
8+
cleanupOrphanedUserSessions()
9+
return { result: 'Success' }
10+
},
11+
})

src/module.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,25 @@ export interface ModuleOptions {
2222
* @default false
2323
*/
2424
webAuthn?: boolean
25+
/**
26+
* Use session storage
27+
* Use 'cache' for standard cache storage,
28+
* 'cookie' for cookies,
29+
* 'memory' for in-memory storage,
30+
* 'nuxt-session' for a custom storage mount named 'nuxt-session'
31+
* @default 'cache'
32+
*/
33+
storageType?: 'cache' | 'cookie' | 'memory' | 'nuxt-session'
34+
/**
35+
* Session inactivity max age in milliseconds
36+
* @default 2592000000 (30 days)
37+
*/
38+
sessionInactivityMaxAge?: number
39+
/**
40+
* Auto extend session
41+
* @default true
42+
*/
43+
autoExtendSession?: boolean
2544
/**
2645
* Hash options used for password hashing
2746
*/
@@ -49,6 +68,9 @@ export default defineNuxtModule<ModuleOptions>({
4968
// Default configuration options of the Nuxt module
5069
defaults: {
5170
webAuthn: false,
71+
storageType: 'cookie',
72+
sessionInactivityMaxAge: 2592000, // 30 days
73+
autoExtendSession: true,
5274
hash: {
5375
scrypt: {},
5476
},
@@ -143,6 +165,17 @@ export default defineNuxtModule<ModuleOptions>({
143165
authenticate: {},
144166
})
145167

168+
runtimeConfig.useSessionStorageType = runtimeConfig.useSessionStorageType || options.storageType
169+
runtimeConfig.sessionInactivityMaxAge = runtimeConfig.sessionInactivityMaxAge || options.sessionInactivityMaxAge
170+
runtimeConfig.autoExtendSession = runtimeConfig.autoExtendSession || options.autoExtendSession
171+
logger.withTag('nuxt-auth-utils').info(`Using session storage type: ${runtimeConfig.useSessionStorageType}`)
172+
if (runtimeConfig.useSessionStorageType === 'memory') {
173+
logger.warn('Using in-memory session storage, this is not recommended for production')
174+
if (!nuxt.options.dev) {
175+
logger.error('You are not running in dev mode, please make sure this is intentional')
176+
}
177+
}
178+
146179
// OAuth settings
147180
runtimeConfig.oauth = defu(runtimeConfig.oauth, {})
148181
// GitHub OAuth

0 commit comments

Comments
 (0)