diff --git a/README.md b/README.md index f7242df..e5f65c6 100644 --- a/README.md +++ b/README.md @@ -218,7 +218,10 @@ Here's what the full _default_ module configuration looks like: // The session cookie same site policy is `lax` cookieSameSite: 'lax', // In-memory storage is used (these are `unjs/unstorage` options) - storageOptions: {}, + storageOptions: { + driver: 'memory', + options: {} + }, // The request-domain is strictly used for the cookie, no sub-domains allowed domain: null, // Sessions aren't pinned to the user's IP address @@ -235,6 +238,28 @@ Here's what the full _default_ module configuration looks like: } ``` +``` +#### Using a different storage driver + +You can use any stroage driver supported by unstorage. For example, this will use the redis driver instead of the default memory driver. +```ts +//nuxt.config.ts +{ + ..., + session: { + session:{ + storageOptions:{ + driver: 'redis', + options: { + url: 'redis://localhost:6379' + } + } + } + } +} + +``` + ### Security This section mostly contains a list of possible security problems and how to mitigate (some) of them. Note that the below flaws exist with many libraries and frameworks we use in our day-to-day when building and working with APIs. E.g., your vanilla-nuxt-app is not safe of some of them like the client sending malicious data. Missing in the below list are estimates of how likely it is that one of the list-items may occur and what impact it will have on your app. This is because that heavily depends on: diff --git a/playground/nuxt.config.ts b/playground/nuxt.config.ts index 4f3daa1..bacc838 100644 --- a/playground/nuxt.config.ts +++ b/playground/nuxt.config.ts @@ -2,5 +2,6 @@ import { defineNuxtConfig } from 'nuxt/config' import NuxtSession from '../src/module' export default defineNuxtConfig({ - modules: [NuxtSession] + modules: [NuxtSession], + session: {} }) diff --git a/src/module.ts b/src/module.ts index 3f6355f..ce6d1e2 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1,6 +1,6 @@ import { addImportsDir, addServerHandler, createResolver, defineNuxtModule, useLogger } from '@nuxt/kit' -import { CreateStorageOptions } from 'unstorage' import { defu } from 'defu' +import { builtinDrivers } from 'unstorage' import type { FilledModuleOptions, ModuleOptions, @@ -18,7 +18,10 @@ const defaults: FilledModuleOptions = { idLength: 64, storePrefix: 'sessions', cookieSameSite: 'lax', - storageOptions: {} as CreateStorageOptions, + storageOptions: { + driver: 'memory', + options: {} + }, domain: null, ipPinning: false as boolean|SessionIpPinningOptions }, @@ -59,6 +62,13 @@ export default defineNuxtModule({ const publicConfig: ModulePublicRuntimeConfig = { session: { api: options.api } } nuxt.options.runtimeConfig.public = defu(nuxt.options.runtimeConfig.public, publicConfig) + // setup unstorage + nuxt.options.nitro.virtual = defu(nuxt.options.nitro.virtual, { + '#session-driver': `export { default } from '${ + builtinDrivers[options.session.storageOptions.driver] + }'` + }) + // 3. Locate runtime directory and transpile module const { resolve } = createResolver(import.meta.url) diff --git a/src/runtime/server/middleware/session/sessionStorage.ts b/src/runtime/server/middleware/session/sessionStorage.ts new file mode 100644 index 0000000..f537fca --- /dev/null +++ b/src/runtime/server/middleware/session/sessionStorage.ts @@ -0,0 +1,8 @@ +import { createStorage, prefixStorage } from 'unstorage' +import { useRuntimeConfig } from '#imports' +// @ts-ignore +import sessionDriver from '#session-driver' +const sessionConfig = useRuntimeConfig().session.session +const driver = sessionDriver(sessionConfig.storageOptions.options) +const storage = createStorage({ driver }).mount(sessionConfig.storePrefix, driver) +export const sessionStorage = prefixStorage(storage, sessionConfig.storePrefix) diff --git a/src/runtime/server/middleware/session/storage.ts b/src/runtime/server/middleware/session/storage.ts index 922534c..ce761e0 100644 --- a/src/runtime/server/middleware/session/storage.ts +++ b/src/runtime/server/middleware/session/storage.ts @@ -1,8 +1,5 @@ -import { createStorage, prefixStorage, StorageValue } from 'unstorage' -import { useRuntimeConfig } from '#imports' - -const storage = prefixStorage(createStorage(useRuntimeConfig().session.session.storageOptions), useRuntimeConfig().session.session.storePrefix) - -export const getStorageSession = (sessionId: string) => storage.getItem(sessionId) -export const setStorageSession = (sessionId: string, session: StorageValue) => storage.setItem(sessionId, session) -export const dropStorageSession = (sessionId: string) => storage.removeItem(sessionId) +import type { StorageValue } from 'unstorage' +import { sessionStorage } from './sessionStorage' +export const getStorageSession = (sessionId: string) => sessionStorage.getItem(sessionId) +export const setStorageSession = (sessionId: string, session: StorageValue) => sessionStorage.setItem(sessionId, session) +export const dropStorageSession = (sessionId: string) => sessionStorage.removeItem(sessionId) diff --git a/src/types.ts b/src/types.ts index 8588651..bb60379 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,8 +1,22 @@ -import type { CreateStorageOptions } from 'unstorage' +import type { BuiltinDriverName, CreateStorageOptions } from 'unstorage' +import type { FSStorageOptions } from 'unstorage/dist/drivers/fs' +import type { KVOptions } from 'unstorage/dist/drivers/cloudflare-kv-binding' +import type { KVHTTPOptions } from 'unstorage/dist/drivers/cloudflare-kv-http' +import type { GithubOptions } from 'unstorage/dist/drivers/github' +import type { HTTPOptions } from 'unstorage/dist/drivers/http' +import type { OverlayStorageOptions } from 'unstorage/dist/drivers/overlay' +import type { LocalStorageOptions } from 'unstorage/dist/drivers/localstorage' +import type { RedisOptions } from 'unstorage/dist/drivers/redis' export type SameSiteOptions = 'lax' | 'strict' | 'none' export type SupportedSessionApiMethods = 'patch' | 'delete' | 'get' | 'post' +export type UnstorageDriverOption = FSStorageOptions | KVOptions | KVHTTPOptions | GithubOptions | HTTPOptions | OverlayStorageOptions | LocalStorageOptions | RedisOptions + +export interface StorageOptions { + driver: BuiltinDriverName, + options?: UnstorageDriverOption +} export interface SessionIpPinningOptions { /** * The name of the HTTP header used to retrieve the forwarded (real) IP address of the user @@ -45,12 +59,11 @@ export interface SessionOptions { cookieSameSite: SameSiteOptions /** * Driver configuration for session-storage. Per default in-memory storage is used - * @default {} - * @example { driver: redisDriver({ base: 'storage:' }) } - * @type CreateStorageOptions + * @default { driver: 'memory', options: {} } + * @example { driver: 'redis', options: {url: 'redis://localhost:6739' } } * @docs https://github.com/unjs/unstorage */ - storageOptions: CreateStorageOptions, + storageOptions: StorageOptions, /** * Set the domain the session cookie will be receivable by. Setting `domain: null` results in setting the domain the cookie is initially set on. Specifying a domain will allow the domain and all its sub-domains. * @default null