npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

farrow-auth-session

v0.0.1

Published

Session-based authentication middleware for Farrow framework with flexible storage and parsing layers

Readme

farrow-auth-session

Session-based authentication middleware for Farrow HTTP framework with flexible storage and parsing layers.

Features

  • 🔐 Flexible Authentication Architecture - Support any authentication method via SessionStore and SessionParser interfaces
  • 🔒 Type Safety - Full TypeScript support with automatic type inference
  • 🏗️ Modular Design - Complete decoupling of storage and parsing layers for easy extension
  • 🎯 Context-Driven - Based on farrow-pipeline's Context system with request-level isolation
  • Automatic State Management - Tracks data modifications and intelligently determines when to save
  • 🚀 Native Integration - Designed specifically for Farrow framework with seamless middleware integration

Installation

npm install farrow-auth-session
# or
yarn add farrow-auth-session
# or
pnpm add farrow-auth-session

Quick Start

The following example demonstrates basic usage with the built-in Cookie Session components. You can also create custom authentication solutions by implementing the SessionStore and SessionParser interfaces.

Basic Cookie Session

import { Http } from 'farrow-http'
import { createSession, createSessionCtx, cookieSessionParser, cookieSessionStore } from 'farrow-auth-session'

// Define your user data type
type UserData = {
  userId?: string
  username?: string
  role?: string
}

// Create auth context
const sessionUserDataCtx = createSessionCtx<UserData>({})

// Setup authentication middleware
const sessionMiddleware = createSession({
  sessionUserDataCtx,
  sessionParser: cookieSessionParser(),
  sessionStore: cookieSessionStore<UserData>({
    secret: process.env.SESSION_SECRET || 'your-secret-key-min-32-chars-long!!!'
  }),
  autoSave: true,
  autoCreateOnMissing: true
})

// Create HTTP app
const app = Http()

// Method 1: Apply auth middleware globally
app.use(sessionMiddleware)

// Method 2: Apply to specific routes
const protectedRouter = Router()
protectedRouter.use(sessionMiddleware)  // Only use auth in this router group

protectedRouter.get('/profile').use(() => {
  const userData = sessionUserDataCtx.get()
  return Response.json(userData)
})

protectedRouter.post('/update').use((request) => {
  sessionUserDataCtx.set({ ...sessionUserDataCtx.get(), ...request.body })
  return Response.json({ success: true })
})

app.route('/api/protected').use(protectedRouter)  // Mount protected routes

// Use session in routes
app.post('/login').use(async (request) => {
  // Your login logic here
  const user = await validateUser(request.body)
  
  // Set user data in session
  sessionUserDataCtx.set({
    userId: user.id,
    username: user.name,
    role: user.role
  })
  
  return Response.json({ success: true })
})

app.get('/profile').use(() => {
  const userData = sessionUserDataCtx.get()
  
  if (!userData?.userId) {
    return Response.status(401).json({ error: 'Not authenticated' })
  }
  
  return Response.json(userData)
})

app.post('/logout').use(async () => {
  await sessionUserDataCtx.destroy()
  return Response.json({ success: true })
})

app.listen(3000)

Cookie Components

CookieSessionParser - Cookie Session Parser

Responsible for parsing session ID from HTTP request cookies and setting/removing cookies in responses.

Key Features:

  • Extract and decode session ID from request cookies
  • Set encrypted session ID in response
  • Support custom encoder/decoder
  • Manage cookie lifecycle

CookieSessionStore - Cookie Session Storage

⚠️ Security Warning: CookieSessionStore stores session data directly in client-side cookies. Although it uses AES-256-CBC encryption, there are still security risks:

  • Client can see the encrypted data
  • Cookie size limitation (typically 4KB)
  • Not suitable for storing sensitive information

Recommended Use Cases:

  • Development and testing environments
  • Storing non-sensitive user preferences
  • Small applications or prototyping

For production use, consider implementing a custom SessionStore with server-side storage (Redis, database, etc.) for better security and scalability.

Key Features:

  • AES-256-CBC encryption for session data
  • Support rolling/renew expiration strategies
  • Automatic session lifecycle management
  • Data integrity verification

Configuration Options

Cookie Session Parser Options

cookieSessionParser({
  sessionIdKey: 'sess:k',        // Cookie key for session ID
  cookieOptions: {
    maxAge: 30 * 60 * 1000,      // 30 minutes
    httpOnly: true,               // HTTP only cookie
    sameSite: 'lax',              // CSRF protection
    secure: true,                 // HTTPS only (production)
    domain: '.example.com',       // Cookie domain
    path: '/'                     // Cookie path
  },
  customCodec: {                  // Optional custom encoding
    encode: (id) => customEncode(id),
    decode: (encoded) => customDecode(encoded)
  }
})

Cookie Session Store Options

cookieSessionStore<UserData>({
  secret: process.env.SESSION_SECRET,  // Required: encryption secret key
  sessionStoreKey: 'sess:data',        // Cookie key for session data
  rolling: true,                        // Reset expiry on every request
  renew: false,                         // Renew only when near expiration
  renewBefore: 10 * 60 * 1000,         // Renew 10 minutes before expiry
  cookieOptions: {
    maxAge: 60 * 60 * 1000,             // 1 hour
    httpOnly: true,
    sameSite: 'strict'
  },
  dataCreator: (request, userData) => {
    // Initialize session data
    return {
      createdAt: Date.now(),
      ip: request.headers['x-forwarded-for'],
      ...userData
    }
  }
})

Session Expiration Strategies

Rolling Sessions

Resets expiration time on every request. Best for "keep alive" scenarios.

cookieSessionStore({
  secret: process.env.SESSION_SECRET,
  rolling: true,
  cookieOptions: { maxAge: 30 * 60 * 1000 } // 30 minutes
})

Renewing Sessions

Only updates expiration when close to expiry. Better performance.

cookieSessionStore({
  secret: process.env.SESSION_SECRET,
  renew: true,
  renewBefore: 10 * 60 * 1000, // Renew 10 minutes before expiry
  cookieOptions: { maxAge: 60 * 60 * 1000 } // 1 hour
})

Fixed Sessions

Session expires at a fixed time regardless of activity.

cookieSessionStore({
  secret: process.env.SESSION_SECRET,
  rolling: false,
  renew: false,
  cookieOptions: { maxAge: 8 * 60 * 60 * 1000 } // 8 hours
})

Route-Level Usage

Flexible Route Configuration

You can use authentication middleware in different route groups as needed:

import { Http, Router } from 'farrow-http'

const app = Http()

// Public routes (no auth required)
const publicRouter = Router()
publicRouter.get('/about').use(() => {
  return Response.json({ message: 'About us' })
})

// Protected routes (auth required)
const protectedRouter = Router()
protectedRouter.use(sessionMiddleware)  // Only use in this router group

protectedRouter.get('/<userId:string>').use((request) => {
  const userData = sessionUserDataCtx.get()
  if (!userData) {
    return Response.status(401).json({ error: 'Login required' })
  }
  
  // Store route params in auth context
  sessionUserDataCtx.set({ ...userData, currentUserId: request.params.userId })
  
  return Response.json({ 
    message: `User ${userData.username} is viewing ${request.params.userId}'s info` 
  })
})

protectedRouter.get('/dashboard').use(() => {
  const userData = sessionUserDataCtx.get()
  return Response.json({ 
    dashboard: 'User dashboard data',
    user: userData 
  })
})

// Admin routes (special permissions required)
const adminRouter = Router()
adminRouter.use(sessionMiddleware)
adminRouter.use((request, next) => {
  const userData = sessionUserDataCtx.get()
  if (!userData?.isAdmin) {
    return Response.status(403).json({ error: 'Admin access required' })
  }
  return next(request)
})

adminRouter.get('/users').use(() => {
  return Response.json({ users: getAllUsers() })
})

// Mount routes
app.route('/public').use(publicRouter)
app.route('/user').use(protectedRouter)
app.route('/admin').use(adminRouter)

app.listen(3000)

Conditional Authentication

Decide whether to use authentication based on different conditions:

const apiRouter = Router()

// Optional auth: logged-in users get more permissions
apiRouter.use((request, next) => {
  // Check for token or cookie
  const hasAuth = request.headers.authorization || request.cookies?.['sess:k']
  
  if (hasAuth) {
    // Has auth info, apply auth middleware
    return sessionMiddleware(request, next)
  }
  
  // No auth info, continue without auth
  return next(request)
})

apiRouter.get('/posts').use(() => {
  const userData = sessionUserDataCtx.get()
  
  if (userData) {
    // Logged-in user: return personalized content
    return Response.json({ 
      posts: getPersonalizedPosts(userData.userId),
      recommended: true 
    })
  } else {
    // Guest user: return public content
    return Response.json({ 
      posts: getPublicPosts(),
      recommended: false 
    })
  }
})

Advanced Usage

SessionUserDataCtx Core Methods

sessionUserDataCtx provides complete authentication data management functionality:

1. get() - Get current user data

app.get('/profile').use(() => {
  const userData = sessionUserDataCtx.get()
  if (!userData) {
    return Response.status(401).json({ error: 'Not authenticated' })
  }
  return Response.json(userData)
})

2. set(data) - Set user data

app.post('/login').use(async (request) => {
  const user = await validateUser(request.body)
  
  // Set user data (automatically marked as modified)
  sessionUserDataCtx.set({
    userId: user.id,
    username: user.name,
    role: user.role
  })
  
  return Response.json({ success: true })
})

3. regenerate() - Regenerate session

Used for security-sensitive operations like privilege escalation or session refresh before important operations.

app.post('/admin/login').use(async () => {
  // Regenerate session ID while preserving existing data
  const success = await sessionUserDataCtx.regenerate()
  
  if (success) {
    // Update permissions
    const current = sessionUserDataCtx.get()
    sessionUserDataCtx.set({ ...current, isAdmin: true })
    return Response.json({ message: 'Admin privileges activated' })
  }
  
  return Response.status(500).json({ error: 'Failed to regenerate session' })
})

Return values:

  • true: Successfully regenerated
  • false: Operation failed (e.g., no data exists)
  • undefined: Internal error

4. destroy() - Destroy session

Completely clears user authentication data and session.

app.post('/logout').use(async () => {
  const result = await sessionUserDataCtx.destroy()
  
  if (result) {
    return Response.json({ message: 'Successfully logged out' })
  }
  
  return Response.status(500).json({ error: 'Logout failed' })
})

Return values:

  • true: Successfully destroyed
  • false: Operation failed (e.g., session doesn't exist)
  • undefined: Internal error

5. saveToStore() - Manually save to storage

When autoSave: false, you need to manually call this method to save data.

const sessionMiddleware = createAuth({
  sessionUserDataCtx,
  authParser: cookieSessionParser(),
  authStore: cookieSessionStore({ secret: 'secret-key' }),
  autoSave: false  // Disable auto-save
})

app.post('/save-progress').use(async () => {
  sessionUserDataCtx.set({ ...userData, progress: 50 })
  
  // Manual save
  const saved = await sessionUserDataCtx.saveToStore()
  if (saved) {
    return Response.json({ message: 'Progress saved' })
  }
  
  return Response.status(500).json({ error: 'Save failed' })
})

Return values:

  • true: Successfully saved
  • false: Save failed
  • undefined: Internal error

6. isModified - Check if data was modified

Read-only property to check if data was modified in the current request.

app.use((request, next) => {
  const response = next()
  
  // Log session modification status
  if (sessionUserDataCtx.isModified) {
    console.log(`Session modified for ${request.pathname}`)
  }
  
  return response
})

Custom Adapter Development

Core Concepts

farrow-auth-session achieves decoupling through two interfaces:

  • SessionStore: Data storage (Redis, database, cookies, etc.)
  • SessionParser: Credential parsing (extract from request, set in response)

They communicate via sessionMetaDataCtx to pass session metadata (sessionId, expiration time).

Implementing SessionStore

import { SessionStore, sessionMetaDataCtx } from 'farrow-auth-session'

class RedisStore<T> implements SessionStore<T, string> {
  async get(sessionId: string) {
    const data = await redis.get(sessionId)
    if (!data) return null  // Not exists
    
    // Set metadata for Parser
    sessionMetaDataCtx.set({ 
      sessionId, 
      expiresTime: data.expires 
    })
    return data.value
  }
  
  async set(userData: T) {
    const meta = sessionMetaDataCtx.get()
    if (!meta) return false
    
    await redis.set(meta.sessionId, userData)
    return true
  }
  
  async create(userData?: T) {
    const sessionId = generateId()
    const expiresTime = Date.now() + 3600000
    
    await redis.set(sessionId, userData || {})
    sessionMetaDataCtx.set({ sessionId, expiresTime })
    return userData || {} as T
  }
  
  async destroy() {
    const meta = sessionMetaDataCtx.get()
    if (!meta) return false
    
    await redis.del(meta.sessionId)
    return true
  }
  
  // Optional: Update expiry only
  async touch() {
    const meta = sessionMetaDataCtx.get()
    if (!meta) return false
    
    await redis.expire(meta.sessionId, 3600)
    return true
  }
}

Implementing SessionParser

import { SessionParser, sessionMetaDataCtx, Response } from 'farrow-auth-session'

class HeaderParser implements SessionParser<string> {
  async get(request) {
    // Extract from request header
    return request.headers?.['x-session-id'] || null
  }
  
  async set() {
    const meta = sessionMetaDataCtx.get()
    if (!meta) return Response
    
    // Set response header
    return Response.header('X-Session-Id', meta.sessionId)
  }
  
  async remove() {
    return Response.header('X-Session-Id', '')
  }
}

Return Value Convention

  • Success: Return data or true
  • Failure: Return null or false
  • Error: Return undefined

Real-world Example

See fa-session-redis (experimental but tested)

API Reference

createSession(config)

Creates authentication middleware.

  • config.sessionUserDataCtx - Context for user data storage
  • config.sessionParser - Parser for credentials (cookies, headers, etc.)
  • config.sessionStore - Storage backend for session data
  • config.autoSave - Automatically save modified sessions
  • config.autoCreateOnMissing - Automatically create new sessions when missing

createSessionCtx(defaultData)

Creates a typed authentication context.

cookieSessionParser(options?)

Creates a cookie-based session ID parser.

cookieSessionStore(options?)

Creates an encrypted cookie-based session store.

SessionStore<UserData, Credit>

Interface for custom storage implementations.

SessionParser

Interface for custom credential parsers.

Utilities

import { oneMinute, oneHour, oneDay, oneWeek } from 'farrow-auth-session'

// Time constants in seconds
const sessionDuration = 2 * oneHour * 1000 // 2 hours in milliseconds

TypeScript Support

The library provides full TypeScript support with type inference:

import { InferUserData, InferCredit } from 'farrow-auth-session'

// Infer types from config
type MyUserData = InferUserData<typeof authConfig>
type MyCredit = InferCredit<typeof authConfig>

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.