@revas-hq/kit-session
v0.0.16
Published
The `@revas-hq/kit-session` package provides flexible session management for TypeScript applications within the Revas Kit ecosystem. It allows storing session data associated with users across requests, integrating seamlessly with `@revas-hq/kit-context`,
Downloads
98
Readme
Revas Kit: Session (@revas-hq/kit-session)
The @revas-hq/kit-session package provides flexible session management for TypeScript applications within the Revas Kit ecosystem. It allows storing session data associated with users across requests, integrating seamlessly with @revas-hq/kit-context, @revas-hq/kit-http, and offering optional encryption.
Features
- Session Management API (
SessionManager): Provides functions to load, save, read, modify, and destroy session data. - Context Integration: Stores the current request's session state within the
@revas-hq/kit-context, making it available throughout the request lifecycle. - Storage Abstraction (
SessionStore): Decouples session logic from persistence via an interface, allowing different storage backends (DB, Redis, etc.). - Stateless Cookie Store (
createCookieSessionStore): Includes a built-in store that encodes session data directly into the session token (cookie value). Requires signing/encryption for security. - HTTP Middleware: Provides
RequestFunction(createSessionRequestFunction) andResponseFunction(createSessionResponseFunction) for easy integration with@revas-hq/kit-http. - Optional Encryption (
createEncryptSessionStoreMiddleware): Offers robust AES-GCM encryption for both the session token and optionally the stored session data, using the Web Crypto API. - Secure Cookie Configuration: Detailed options (
SessionCookieOptions) for setting secure cookie attributes (HttpOnly,Secure,SameSite, etc.).
Installation
# Install core package and essential peer dependencies
npm install @revas-hq/kit-session @revas-hq/kit-context @revas-hq/kit-http cookie
# or
yarn add @revas-hq/kit-session @revas-hq/kit-context @revas-hq/kit-http cookieOptional: If using encryption middleware with raw keys
No external crypto dependencies needed if environment supports Web Crypto API (Node >=15, Deno, CF Workers, Browsers)
Core Concepts
Session State (Session<T>)
An object stored in the context representing the current request's session data (values), unique identifier (token), expiry (expireTime), and modification status (state).
Session Manager (SessionManager)
The main API for interacting with sessions. It orchestrates loading state from storage via cookies (extractSession), saving state back (injectSession), and modifying the state (setValue, unsetValue, destroyValues). It operates on the Session<T> object found within the Context.
Session Store (SessionStore<T>)
An interface defining how session data is persisted (getSession, setSession, deleteSession). You provide an implementation (like createCookieSessionStore or a custom one for Redis/DB).
HTTP Middleware
Functions (createSessionRequestFunction, createSessionResponseFunction) hook into the kit-http lifecycle to call the manager's extractSession and injectSession methods automatically.
Encryption Middleware
Optional SessionStoreMiddleware (createEncryptSessionStoreMiddleware) wraps a SessionStore to add a layer of encryption using AES-GCM.
Important: Session State Mutability
This library uses @revas-hq/kit-context to store the session state object (Session<T>). However, unlike the immutable pattern typically encouraged by kit-context's withValue, the SessionManager methods like setValue, unsetValue, and destroyValues directly mutate the Session<T> object retrieved from the context.
How it works:
JavaScript objects are passed by reference. When the manager gets the session state from context, it modifies that specific object instance. Later, the injectSession method reads the same object instance from context, sees the mutated state (state and values), and saves accordingly.
Implication:
You do not need to call withContextValue after using manager.setValue, manager.unsetValue, or manager.destroyValues to make the changes effective for saving. The mutation handles this.
Be Aware:
While convenient, this deviates from pure functional immutability. Ensure you understand that session modifications within the lifecycle affect the state seen by subsequent operations in the same request.
Configuration
1. Session Store
Choose or implement a SessionStore.
Using the Stateless Cookie Store:
Note: This store is insecure by itself. Use createEncryptSessionStoreMiddleware or another signing/encryption method.
import { createCookieSessionStore } from '@revas-hq/kit-session/storage/cookie'; // Adjust path if needed
const cookieStore = createCookieSessionStore<{ userId?: string; theme?: string }>();
// or with default Record<string, any>
// const cookieStore = createCookieSessionStore();2. Encryption (Optional but Recommended for Cookie Store)
import { createEncryptSessionStoreMiddleware, importRawKey } from '@revas-hq/kit-session/encrypt'; // Adjust path
// WARNING: Load key securely (e.g., from environment variables/secrets manager). NEVER hardcode keys.
// Key must be Base64 encoded corresponding to 16, 24, or 32 bytes for AES-128/192/256.
const base64Key = process.env.SESSION_SECRET_KEY_BASE64;
if (!base64Key) throw new Error("SESSION_SECRET_KEY_BASE64 is not set!");
const cryptoKey = await importRawKey(base64Key);
// Create the encryption middleware (encrypts token AND values)
const encryptMiddleware = createEncryptSessionStoreMiddleware(cryptoKey, { encryptValues: true });
// Wrap the base store
const secureCookieStore = encryptMiddleware(cookieStore);3. Session Manager
Create the manager, providing a context key name, cookie options, and the (potentially wrapped) store.
import { createSessionManager, SessionManagerOptions, SessionCookieOptions } from '@revas-hq/kit-session';
const cookieOptions: SessionCookieOptions = {
name: '__Host-Session', // Recommended secure prefix
path: '/',
httpOnly: true,
secure: true, // Ensure your site uses HTTPS
sameSite: 'lax',
maxAge: 60 * 60 * 24 * 7, // 1 week in seconds (optional)
};
const managerOptions: SessionManagerOptions = {
name: 'myAppSession', // Unique key for storing Session state in context
cookie: cookieOptions,
storage: secureCookieStore, // Use the encrypted store
// storage: cookieStore, // Or use the unencrypted store (NOT RECOMMENDED without signing)
};
const sessionManager = createSessionManager(managerOptions);4. HTTP Middleware
Create the request/response functions using the manager.
import { createSessionRequestFunction, createSessionResponseFunction } from '@revas-hq/kit-session/http'; // Adjust path
const loadSession = createSessionRequestFunction(sessionManager);
const saveSession = createSessionResponseFunction(sessionManager);Usage
Integration with kit-http-react-router
Add the loadSession and saveSession functions to the beforeFunctions and afterFunctions arrays respectively when using createLoader or createAction.
// ~/main.ts (Example)
import { createLoader } from '@revas-hq/kit-http-react-router';
// ... import loadSession, saveSession, endpoint, decoder etc.
const myLoader = createLoader({
endpoint: myEndpoint,
decodeRequest: myDecoder,
beforeFunctions: [
// captureAuthorizationHeader, // Example: Run auth first if needed
loadSession, // Load session state into context
],
afterFunctions: [
saveSession, // Save session state (if modified) to cookie
],
// ... other options
});
// Register myLoader in the service container...Accessing and Modifying Session Data (e.g., in Endpoints)
Use the SessionManager instance (or helper functions if you create them) to interact with the session state stored in the context.
import type { Endpoint, EndpointArgs } from '@revas-hq/kit-endpoint';
import type { Context } from '@revas-hq/kit-context';
// Assuming sessionManager is accessible (e.g., via container or closure)
const myEndpoint: Endpoint</* TRequest, TResponse */> = async ({ context, request }) => {
// Get session data using the manager and current context
const userId = sessionManager.getValue<string>(context, 'userId');
const counter = sessionManager.getValue<number>(context, 'counter') ?? 0;
if (!userId) {
// Handle unauthenticated user...
// Maybe set a flash message?
sessionManager.setValue(context, 'flash', { type: 'error', message: 'Please log in'});
// Note: setValue mutated the session state in the current context
// No need for withContextValue here for the session change itself.
// If returning early, make sure saveSession middleware still runs if needed!
// EndpointResult doesn't carry context modifications intrinsically here,
// relying on middleware order and the shared context object.
return { success: false, context, error: { code: 401, message: 'Unauthorized' } };
}
// Modify session data
sessionManager.setValue(context, 'counter', counter + 1);
sessionManager.setValue(context, 'lastVisit', new Date().toISOString());
// To destroy the session on response:
// sessionManager.destroyValues(context);
// Check modification status (optional)
// console.log('Session Modified?', sessionManager.isModified(context));
// Continue with business logic...
const response = { /* ... */ };
// The mutated session state in `context` will be picked up by `saveSession` middleware
return { success: true, context, response };
};Security Considerations
- Secrets: Keep your
SessionMiddlewareOptions.secret(if using signing directly) or the raw key forimportRawKey(if using encryption) secure and confidential. Load them from environment variables or a secrets management system. Never hardcode secrets. - Cookie Options: Use secure cookie settings:
name: Use__Host-or__Secure-prefixes.httpOnly: true(Default): Prevents client-side script access.secure: true(Default in prod): Ensures cookie is only sent over HTTPS. Requires your site to use HTTPS.sameSite: 'lax'(Default) or'strict': Mitigates CSRF attacks. Avoid'none'unless strictly necessary andsecure: trueis set.path: Use the most restrictive path possible.domain: Omit unless cross-subdomain access is needed (incompatible with__Host-).
- Cookie Store: The default
CookieSessionStoreputs all session data in the cookie.- Size Limit: Cookies are limited to ~4KB. Do not store large amounts of data.
- Sensitivity: Do not store sensitive data in the cookie unless using strong encryption (like the provided
createEncryptSessionStoreMiddleware). Even with encryption, prefer storing only essential identifiers (likeuserId) and fetching sensitive data from your backend when needed. Signing prevents tampering but not reading.
- Encryption: AES-GCM (
encrypt.ts) provides strong authenticated encryption. Ensure your key management is secure. - Web Crypto API: The encryption middleware relies on the Web Crypto API. Ensure your target environment (Node.js 15+, Deno, Cloudflare Workers, modern browsers) supports
crypto.subtle.
License
This project is licensed under the MIT License. See the LICENSE file for details.
