laravel-session-sdk
v1.4.9
Published
Universal SDK for validating and managing Laravel sessions in Node.js applications
Maintainers
Readme
Laravel Session SDK
Universal Node.js SDK for validating and managing Laravel sessions in any JavaScript/TypeScript project.
Table of Contents
- Features
- Installation
- Quick Start
- Usage Examples
- Configuration
- Laravel Setup
- API Reference
- How It Works
- Security
- Performance
- Testing
- Troubleshooting
- FAQ
- Contributing
- Support
- License
- Links
🚀 Features
- ✅ Zero Laravel Code Changes - Read-only session validation
- ✅ Multiple Frameworks - Express, Next.js, NestJS support
- ✅ Database & Redis Sessions - Full support for both session drivers
- ✅ TypeScript - Full type definitions included
- ✅ Session Decoding - Handles PHP serialization automatically
- ✅ Single Session Enforcement - Respects Laravel's session logic
- ✅ 2FA Support - Validates two-factor authentication
- ✅ Dual-Source Permissions - Extracts from session payload with database fallback
- ✅ Multiple Permissions Keys - Extract multiple custom keys from session (v1.3.0+)
- ✅ Nested Key Support - Dot notation for nested session keys (e.g.,
user.permissions) - ✅ Debug Mode - Conditional logging for development (v1.4.0+)
- ✅ Production Ready - Connection pooling, error handling, graceful shutdown
📦 Installation
Database Session Driver (Recommended)
If you're using database sessions:
npm install laravel-session-sdk mysql2Note: mysql2 is required as a peer dependency because the SDK needs it to connect to your database for session validation and user/role queries.
Redis Session Driver
If you're using Redis sessions:
npm install laravel-session-sdk mysql2 redisImportant: Both mysql2 and redis are required because:
redisis used to read session data from Redismysql2is still required because user information, roles, and permissions are stored in database tables (not in Redis)- The SDK uses Redis only for session storage, but queries the database for user/role/permission data
Why Peer Dependencies?
mysql2 and redis are peer dependencies (not automatically installed) because:
- You may already have them installed in your project
- Different projects may use different versions
- You only need to install what your driver requires
- Prevents version conflicts with your existing dependencies
🎯 Quick Start
import { LaravelSessionClient } from 'laravel-session-sdk';
const client = new LaravelSessionClient({
database: {
type: 'mysql',
host: 'localhost',
user: 'root',
password: 'password',
database: 'laravel_db',
},
session: {
driver: 'database',
lifetime: 1000, // minutes
},
});
// Validate session
const result = await client.validateSession(sessionId);
if (result.valid) {
console.log('User:', result.user);
console.log('Role:', result.role);
console.log('Permissions:', result.permissions);
}📖 Usage Examples
Express.js
const express = require('express');
const cookieParser = require('cookie-parser');
const { LaravelSessionClient, createExpressMiddleware } = require('laravel-session-sdk');
const app = express();
app.use(cookieParser());
const sessionClient = new LaravelSessionClient({
database: { /* config */ },
session: { driver: 'database' }
});
const authMiddleware = createExpressMiddleware(sessionClient);
app.get('/api/user', authMiddleware, (req, res) => {
res.json({
user: req.laravelSession.user,
role: req.laravelSession.role,
});
});
app.listen(3000);Next.js API Route
import { NextApiRequest, NextApiResponse } from 'next';
import { LaravelSessionClient, validateNextJsSession } from 'laravel-session-sdk';
const client = new LaravelSessionClient({ /* config */ });
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const result = await validateNextJsSession(req, client);
if (!result.valid) {
return res.status(401).json({ error: result.error });
}
return res.json({
user: result.user,
role: result.role,
});
}Next.js with Singleton Pattern
// lib/laravelSession.ts
import { LaravelSessionClient } from '@yourorg/laravel-session-sdk';
let client: LaravelSessionClient | null = null;
export function getLaravelSessionClient(): LaravelSessionClient {
if (!client) {
client = new LaravelSessionClient({
database: {
host: process.env.DB_HOST!,
user: process.env.DB_USER!,
password: process.env.DB_PASSWORD!,
database: process.env.DB_DATABASE!,
},
session: { driver: 'database' },
});
}
return client;
}// pages/api/user.ts
import { getLaravelSessionClient } from '@/lib/laravelSession';
import { validateNextJsSession } from '@yourorg/laravel-session-sdk';
export default async function handler(req, res) {
const client = getLaravelSessionClient();
const result = await validateNextJsSession(req, client);
if (!result.valid) {
return res.status(401).json({ error: result.error });
}
return res.json({ user: result.user });
}React Hook
// hooks/useLaravelAuth.ts
import { useEffect, useState } from 'react';
export function useLaravelAuth() {
const [state, setState] = useState({
user: null,
role: null,
loading: true,
authenticated: false,
});
useEffect(() => {
fetch('/api/auth/user', { credentials: 'include' })
.then(res => res.json())
.then(data => {
if (data.authenticated) {
setState({
user: data.user,
role: data.role,
loading: false,
authenticated: true,
});
}
});
}, []);
return state;
}NestJS
import { Module } from '@nestjs/common';
import { LaravelSessionClient, LaravelSessionGuard } from 'laravel-session-sdk';
const sessionClient = new LaravelSessionClient({ /* config */ });
@Module({
providers: [
{ provide: LaravelSessionClient, useValue: sessionClient },
LaravelSessionGuard,
],
})
export class AuthModule {}⚙️ Configuration
Database Session Driver
const client = new LaravelSessionClient({
database: {
type: 'mysql',
host: 'localhost',
port: 3306,
user: 'root',
password: 'password',
database: 'laravel_db',
connectionLimit: 10,
},
session: {
driver: 'database',
table: 'sessions',
lifetime: 1000, // minutes
cookieName: 'laravel_session',
},
debug: true,
});Redis Session Driver
const client = new LaravelSessionClient({
database: { /* still needed for user/role queries */ },
redis: {
host: '127.0.0.1',
port: 6379,
password: 'secret',
db: 0,
},
session: {
driver: 'redis',
prefix: 'laravel_session:',
lifetime: 1000,
},
});With Encrypted Sessions
const client = new LaravelSessionClient({
database: { /* ... */ },
session: { driver: 'database' },
appKey: process.env.LARAVEL_APP_KEY, // From Laravel's .env
});With Custom Permissions Key
const client = new LaravelSessionClient({
database: { /* ... */ },
session: { driver: 'database' },
// Single key (backward compatible)
permissionsKey: 'permissions',
// Or nested key with dot notation
permissionsKey: 'user.permissions',
});With Multiple Permissions Keys (v1.3.0+)
Extract multiple custom keys from the session at once:
const client = new LaravelSessionClient({
database: { /* ... */ },
session: { driver: 'database' },
// Extract multiple keys from session
permissionsKey: [
'permissions',
'competitionIds',
'competitionsData',
'competitionCategories'
],
});
// Result will be:
// {
// permissions: { role: '...', modules: [...], links: [...] },
// competitionIds: [1, 5, 12],
// competitionsData: [...],
// competitionCategories: [...]
// }With Debug Logging (v1.4.0+)
Enable detailed logging for development:
const client = new LaravelSessionClient({
database: { /* ... */ },
session: { driver: 'database' },
debug: process.env.NODE_ENV === 'development', // Enable debug logs
});Log Verbosity Levels (v1.4.4+)
Control how much sensitive data is shown in logs:
Secure Mode (Default - Recommended for Production):
const client = new LaravelSessionClient({
database: { /* ... */ },
session: { driver: 'database' },
debug: true,
logLevel: 'secure', // Sanitized logs, no sensitive data (default)
});Verbose Mode (Development Only - Shows All Data):
const client = new LaravelSessionClient({
database: { /* ... */ },
session: { driver: 'database' },
debug: true,
logLevel: 'verbose', // Full logs with sensitive data (use only in secure dev environments)
});⚠️ Security Warning:
logLevel: 'secure'(default) - Masks session IDs, tokens, passwords, and sensitive datalogLevel: 'verbose'- Shows full session data, decrypted values, user IDs, etc.- Never use
verbosemode in production - it exposes sensitive information - Use
verboseonly in secure development environments for debugging
🔒 Laravel Setup (Zero Code Changes!)
Option 1: Database Session Driver
Step 1: Change Session Driver
In your Laravel app's .env:
SESSION_DRIVER=database # Change from 'file'
SESSION_DOMAIN=.yourdomain.com
SESSION_SECURE_COOKIE=trueStep 2: Create Sessions Table
php artisan session:table
php artisan migrateStep 3: Clear Cache
php artisan config:cache
php artisan cache:clearOption 2: Redis Session Driver
Step 1: Change Session Driver
In your Laravel app's .env:
SESSION_DRIVER=redis
SESSION_CONNECTION=default
SESSION_DOMAIN=.yourdomain.com
SESSION_SECURE_COOKIE=trueStep 2: Configure Redis Connection
Ensure Redis is configured in config/database.php and running.
Step 3: Clear Cache
php artisan config:cache
php artisan cache:clearNote: Redis driver still requires database configuration in the SDK for user/role/permission queries.
That's it! No code changes needed in Laravel.
📚 API Reference
LaravelSessionClient
constructor(config: LaravelSessionConfig)
Creates a new client instance.
Configuration Options:
interface LaravelSessionConfig {
database?: {
type: 'mysql' | 'postgres';
host: string;
port?: number;
user: string;
password: string;
database: string;
connectionLimit?: number; // Default: 10
};
session: {
driver: 'database' | 'redis' | 'file';
table?: string; // Default: 'sessions'
lifetime?: number; // In minutes
cookieName?: string; // Default: 'laravel_session'
prefix?: string; // For Redis driver
};
appKey?: string; // Laravel APP_KEY for encrypted sessions
debug?: boolean; // Enable debug logging (v1.4.0+)
logLevel?: 'secure' | 'verbose'; // Log verbosity: 'secure' (default) or 'verbose' (v1.4.4+)
permissionsKey?: string | string[]; // Custom permissions key(s) (v1.3.0+)
}validateSession(sessionId: string): Promise<SessionValidationResult>
Validates a Laravel session and returns user data.
Parameters:
sessionId: The Laravel session cookie value (may be encrypted)
Returns:
{
valid: boolean;
user?: {
id: number;
email: string;
name?: string;
google2fa_enable?: number;
[key: string]: any;
};
role?: string;
permissions?: any; // Single key: object, Multiple keys: object with keys
sessionId?: string;
csrfToken?: string;
error?: string;
reason?: string;
}Example:
const result = await client.validateSession(cookieValue);
if (result.valid) {
console.log('User:', result.user);
console.log('Role:', result.role);
// Single permissionsKey: result.permissions is the value
// Multiple permissionsKey: result.permissions is an object with keys
console.log('Permissions:', result.permissions);
}getSessionCookieName(): string
Returns the session cookie name (default: laravel_session).
close(): Promise<void>
Closes all database connections. Call this during graceful shutdown.
Example:
// In your application shutdown handler
process.on('SIGTERM', async () => {
await client.close();
process.exit(0);
});Middleware Functions
createExpressMiddleware(client: LaravelSessionClient)
Creates Express.js middleware.
validateNextJsSession(req: NextApiRequest, client: LaravelSessionClient)
Helper for Next.js API routes.
createNextJsMiddleware(client: LaravelSessionClient)
Creates Next.js middleware (for custom middleware chains).
Permissions Extraction
The SDK supports flexible permissions extraction with multiple strategies:
Single Key (Default)
const client = new LaravelSessionClient({
permissionsKey: 'permissions', // Single string
});
// Result.permissions = { role: '...', modules: [...], links: [...] }Multiple Keys (v1.3.0+)
const client = new LaravelSessionClient({
permissionsKey: ['permissions', 'competitionIds', 'competitionsData'],
});
// Result.permissions = {
// permissions: { role: '...', modules: [...] },
// competitionIds: [1, 5, 12],
// competitionsData: [...]
// }Nested Keys (Dot Notation)
const client = new LaravelSessionClient({
permissionsKey: 'user.permissions', // Supports dot notation
});Dual-Source Strategy
The SDK automatically uses a dual-source strategy for permissions:
- Primary: Extract from session payload (fast, single query)
- Fallback: Query database tables if not found in session
This ensures permissions are always available, even if Laravel doesn't store them in the session. The fallback queries these tables:
user_roles→rolesmodule_permissions→moduleslink_permissions→links
See DUAL_SOURCE_PERMISSIONS.md for details.
🔍 How It Works
- Laravel handles authentication (login/logout)
- Sessions stored in database (via
SESSION_DRIVER=database) - Node.js reads from same database
- SDK decodes PHP-serialized session data
- Validates user, role, and permissions
- Returns user data to your app
┌─────────────┐ ┌─────────────┐ ┌──────────────┐
│ Browser │──────▶│ Next.js │──────▶│ MySQL │
│ Cookie │ │ SDK │ │ sessions │
└─────────────┘ └─────────────┘ └──────────────┘
│
▼
┌─────────────┐
│ Laravel │
│ (Auth) │
└─────────────┘🛡️ Security
- ✅ Read-only - SDK never modifies Laravel sessions
- ✅ HTTPS Required - Use
SESSION_SECURE_COOKIE=truein production - ✅ Session Validation - Checks expiration, user status, 2FA
- ✅ Single Session - Enforces Shooter single-session rule
- ✅ Connection Pooling - Prevents connection exhaustion
🚀 Performance
Connection Pooling
The SDK uses connection pooling by default to reuse database connections:
const client = new LaravelSessionClient({
database: {
// ...
connectionLimit: 10, // Default: 10 connections in pool
},
});Best Practices:
- Use singleton pattern to reuse client instance across requests
- Adjust
connectionLimitbased on your application's concurrency - Call
client.close()during graceful shutdown to release connections
Performance Characteristics
Session Payload Permissions (Primary):
- ⚡ ~10-20ms per validation
- Single database query
- Fastest option
Database Fallback Permissions:
- 🐢 ~50-100ms per validation
- 3-4 database queries with JOINs
- Always available, real-time data
Caching (Optional)
For high-traffic apps, cache validation results:
// Example with Redis caching
const cacheKey = `session:${sessionId}`;
let result = await redis.get(cacheKey);
if (!result) {
result = await client.validateSession(sessionId);
if (result.valid) {
await redis.setex(cacheKey, 300, JSON.stringify(result)); // 5 min cache
}
}Cache Invalidation:
- Invalidate cache when user logs out
- Consider shorter TTL for permissions if they change frequently
- Use session expiration time as cache TTL
🧪 Testing
npm testExample test:
import { LaravelSessionClient } from 'laravel-session-sdk';
const client = new LaravelSessionClient({ /* config */ });
const result = await client.validateSession('test-session-id');
expect(result.valid).toBe(true);
expect(result.user.email).toBe('[email protected]');🔧 Troubleshooting
Session Not Validating
Problem: validateSession() returns valid: false
Solutions:
- Check database connection credentials
- Verify
SESSION_DRIVER=databasein Laravel.env - Ensure sessions table exists (
php artisan session:table && php artisan migrate) - Check if session cookie is being sent from frontend
- Verify
SESSION_DOMAINandSESSION_SECURE_COOKIEsettings match your environment
Cookie Not Being Sent
Problem: Session cookie is not being sent from the browser
Solutions:
- Ensure
credentials: 'include'in fetch requests:fetch('/api/user', { credentials: 'include' }) - Set
SESSION_DOMAIN=.yourdomain.comin Laravel (include leading dot for subdomains) - For local development, use
SESSION_DOMAIN=localhostorSESSION_DOMAIN=null - Verify CORS settings allow credentials
Connection Pool Exhausted
Problem: Error: Connection pool exhausted
Solutions:
- Increase connection pool limit:
database: { connectionLimit: 20 } - Call
client.close()during graceful shutdown - Use singleton pattern to reuse client instance
- Check for connection leaks in your code
TypeScript Errors
Problem: TypeScript type errors when using the SDK
Solutions:
- Ensure you're importing types correctly:
import { LaravelSessionClient, SessionValidationResult } from 'laravel-session-sdk'; - Update to latest version:
npm update laravel-session-sdk - Check your
tsconfig.jsonincludes"moduleResolution": "node"
Sessions Not Decrypting
Problem: Encrypted sessions fail to validate
Solutions:
- Provide Laravel's APP_KEY:
appKey: process.env.LARAVEL_APP_KEY - Ensure APP_KEY format is correct (starts with
base64:) - Verify APP_KEY matches between Laravel and Node.js
- Enable debug mode to see decryption attempts:
debug: true
Permissions Not Found
Problem: permissions is null or undefined in validation result
Solutions:
- Check if Laravel stores permissions in session:
// In Laravel, after login session(['permissions' => [...]]); - Verify permissions key matches your Laravel session structure:
permissionsKey: 'permissions' // or 'user.permissions' for nested - The SDK will automatically fallback to database queries if not found in session
- Enable debug mode to see permission extraction process:
debug: true
Multiple Keys Not Working
Problem: When using array of permissionsKey, only some keys are returned
Solutions:
- Ensure all keys exist in Laravel session:
session([ 'permissions' => [...], 'competitionIds' => [...], 'competitionsData' => [...] ]); - Keys that don't exist will be
undefinedin the result object - Check debug logs to see which keys were found:
debug: true
❓ FAQ
Q: Do I need to modify my Laravel application?
A: No! This SDK is read-only and requires zero changes to your Laravel code. Just configure SESSION_DRIVER=database in your .env file.
Q: Can I use this with file-based sessions?
A: No, this SDK requires database or Redis sessions. File-based sessions cannot be reliably shared across applications.
Q: Does Redis driver require database configuration?
A: Yes, Redis driver still requires database configuration because user information, roles, and permissions are stored in database tables. Redis is only used for session storage, while database is used for user/role/permission queries.
Q: Does this work with Laravel Sanctum or Passport?
A: Yes! This SDK validates Laravel sessions regardless of the authentication system. It works with Laravel's built-in auth, Sanctum, Passport, or custom auth implementations.
Q: Can I modify sessions from Node.js?
A: This SDK is read-only for safety. Session modifications should only be done through Laravel to maintain data integrity. However, you can delete sessions directly from the database if needed (e.g., for logout).
Q: How do I extract multiple custom keys from the session?
A: Use the permissionsKey configuration option with an array of keys:
const client = new LaravelSessionClient({
permissionsKey: ['permissions', 'competitionIds', 'competitionsData'],
});
const result = await client.validateSession(sessionId);
// result.permissions.permissions - permissions data
// result.permissions.competitionIds - competition IDs
// result.permissions.competitionsData - competition dataSee MULTIPLE_PERMISSIONS_KEYS.md for detailed examples.
Q: How does the dual-source permissions strategy work?
A: The SDK first tries to extract permissions from the session payload (fast). If not found, it automatically falls back to querying database tables (user_roles, modules, links). This ensures permissions are always available. See DUAL_SOURCE_PERMISSIONS.md for details.
Q: How do I enable debug logging?
A: Set debug: true in your configuration:
const client = new LaravelSessionClient({
database: { /* ... */ },
session: { driver: 'database' },
debug: process.env.NODE_ENV === 'development',
});Debug logs will show session validation steps, permission extraction, and any errors. Disable in production for better performance.
Q: Can I see full logs with sensitive data for debugging?
A: Yes, but use with extreme caution! Set logLevel: 'verbose':
const client = new LaravelSessionClient({
database: { /* ... */ },
session: { driver: 'database' },
debug: true,
logLevel: 'verbose', // Shows full session data, decrypted values, etc.
});⚠️ Important Security Notes:
- Default is
logLevel: 'secure'- sanitizes sensitive data (recommended) logLevel: 'verbose'- shows everything including session IDs, tokens, passwords- Never use
verbosein production - it violates security best practices - Use
verboseonly in secure development environments when you need to debug session issues - The SDK defaults to
securemode to prevent CWE-312, CWE-359, and CWE-532 vulnerabilities
Q: What's the performance impact?
A: Minimal. With connection pooling enabled, validation takes ~5-20ms. For high-traffic apps, add caching to reduce database queries.
Q: Is this production-ready?
A: Yes! This SDK includes connection pooling, error handling, logging, and has been tested in production environments.
Q: Does it support multi-tenancy?
A: Yes, as long as your Laravel app uses database sessions. Each tenant can have their own database, or you can use a shared sessions table.
Q: Can I use this with Next.js 13+ App Router?
A: Yes! Use it in Server Components, Route Handlers, or Server Actions:
import { cookies } from 'next/headers';
import { getLaravelSessionClient } from '@/lib/laravelSession';
export async function GET() {
const sessionCookie = (await cookies()).get('laravel_session');
const client = getLaravelSessionClient();
const result = await client.validateSession(sessionCookie?.value || '');
return Response.json(result);
}Q: How do I handle session expiration on the frontend?
A: Check the valid field and redirect to login:
const response = await fetch('/api/auth/user', { credentials: 'include' });
const data = await response.json();
if (!data.valid || !data.authenticated) {
window.location.href = '/login';
}Q: Can I use this with Nuxt.js or Vue.js?
A: Yes! The SDK works with any Node.js backend. Use it in your Nuxt server middleware or API routes.
💬 Support
Getting Help
- Documentation: Read the full documentation
- Issues: Report bugs or request features on GitHub Issues
- Discussions: Ask questions in GitHub Discussions
- Security: Report vulnerabilities via Security Policy
Commercial Support
For enterprise support, custom integrations, or consulting services, contact: [email protected]
🤝 Contributing
Contributions welcome! Please read CONTRIBUTING.md.
📄 License
MIT © Aakash Kanojiya
🔗 Links
📚 Additional Documentation
- CHANGELOG.md - Version history and release notes
- MULTIPLE_PERMISSIONS_KEYS.md - Guide for extracting multiple session keys
- DUAL_SOURCE_PERMISSIONS.md - Understanding the dual-source permissions strategy
- PERMISSIONS_KEY_GUIDE.md - Permissions key configuration guide
- GETTING_STARTED.md - Step-by-step getting started guide
- LOCAL_TESTING_GUIDE.md - Guide for local development and testing
📝 Changelog
Version 1.4.7 (Latest)
- ✅ Added CONTRIBUTING.md with comprehensive contribution guidelines
- ✅ Enhanced documentation and developer onboarding
- ✅ Updated README with latest version information
Version 1.4.6
- ✅ Fixed TypeScript compilation errors
- ✅ Improved code quality and documentation
Version 1.4.5
- ✅ Fixed import path issues
- ✅ Resolved TypeScript compilation warnings
Version 1.4.4
- ✅ Security fixes: Fixed CWE-312, CWE-359, and CWE-532 vulnerabilities
- ✅ Log verbosity levels: Added
logLeveloption ('secure' or 'verbose') - ✅ Comprehensive log sanitization to prevent sensitive data exposure
- ✅ SecurityUtils module for sanitizing sensitive data in logs
Version 1.4.2
- ✅ Updated installation instructions for driver-based dependencies
- ✅ Enhanced documentation for peer dependencies
Version 1.4.1
- ✅ Redis session driver support enabled
- ✅ RedisStore implementation with debug logging
Version 1.4.0
- ✅ Conditional debug logging - all logging respects
debugconfiguration option - ✅ Better production performance by eliminating unnecessary logging
Version 1.3.0
- ✅ Multiple permissions keys support - extract multiple custom keys from session
- ✅ Array support for
permissionsKeyconfiguration
Version 1.2.0
- ✅ Dual-source permissions strategy - session payload with database fallback
- ✅ Configurable permissions key with dot notation support
See CHANGELOG.md for complete version history.
Made with ❤️ for Laravel + Node.js developers
