@re-auth/http-adapters
v1.0.0-alpha.33
Published
HTTP protocol adapters for ReAuth - framework-agnostic integration with Express, Fastify, and Hono
Downloads
93
Maintainers
Readme
@re-auth/http-adapters-v2
V2 HTTP protocol adapters for ReAuth - framework-agnostic integration with Express, Fastify, and Hono.
Features
- 🚀 Framework Agnostic: Support for Express, Fastify, and Hono
- 🔒 Security First: Built-in CORS, rate limiting, and security headers
- 🛡️ Type Safe: Full TypeScript integration with V2 engine types
- 🔌 Plugin Aware: Automatically discovers and exposes V2 plugin endpoints
- 👤 User Access: Easy current user retrieval with middleware and utilities
- 📊 Introspection: Built-in API documentation and health checks
- ⚡ Performance: Optimized for high-throughput scenarios
Installation
npm install @re-auth/http-adapters-v2
# or
pnpm add @re-auth/http-adapters-v2
# or
yarn add @re-auth/http-adapters-v2Quick Start
Express.js
import express from 'express';
import { ReAuthEngineV2 } from '@re-auth/reauth';
import { createExpressAdapter } from '@re-auth/http-adapters-v2';
import { createDefaultLogger } from '@re-auth/logger';
const app = express();
// Create logger instance
const logger = createDefaultLogger({
prefix: 'MyApp',
enabledTags: ['auth', 'session', 'http']
});
const engine = new ReAuthEngineV2({
/* config */,
logger: logger // Required logger instance
});
// Create adapter with device info extraction
const adapter = createExpressAdapter({
engine,
basePath: '/api/auth',
cors: {
origin: ['https://app.example.com'],
credentials: true
},
rateLimit: {
windowMs: 15 * 60 * 1000,
max: 100
}
}, false, async (req) => {
// Extract device information for enhanced security
return {
ip: req.ip || req.connection.remoteAddress,
userAgent: req.get('User-Agent'),
fingerprint: req.get('X-Request-ID'),
geoLocation: req.get('CF-IPCountry'),
deviceType: req.get('CF-Device-Type'),
sessionId: req.session?.id,
};
});
app.use('/api/auth', adapter.createRouter());
// Enable user population on all requests (optional)
app.use(adapter.createUserMiddleware());
app.listen(3000);Fastify
import Fastify from 'fastify';
import { ReAuthEngineV2 } from '@re-auth/reauth';
import { createFastifyAdapter } from '@re-auth/http-adapters-v2';
const fastify = Fastify();
const engine = new ReAuthEngineV2({
/* config */
});
// Create adapter with device info extraction
const adapter = createFastifyAdapter(
{
engine,
basePath: '/api/auth',
},
async (request) => {
// Extract device information for enhanced security
return {
ip: request.ip,
userAgent: request.headers['user-agent'],
fingerprint: request.headers['x-request-id'],
geoLocation: request.headers['cf-ipcountry'],
deviceType: request.headers['cf-device-type'],
};
},
);
fastify.register(adapter.createPlugin());
// Enable user population on all requests (optional)
fastify.register(adapter.createUserPlugin());
fastify.listen({ port: 3000 });Hono
import { Hono } from 'hono';
import { ReAuthEngineV2 } from '@re-auth/reauth';
import { createHonoAdapter } from '@re-auth/http-adapters-v2';
import { createDefaultLogger } from '@re-auth/logger';
const app = new Hono();
// Create logger instance
const logger = createDefaultLogger({
prefix: 'MyApp',
enabledTags: ['auth', 'session', 'http']
});
const engine = new ReAuthEngineV2({
/* config */,
logger: logger // Required logger instance
});
// Create adapter with device info extraction
const adapter = createHonoAdapter({
engine,
basePath: '/api/auth'
}, async (c) => {
// Extract device information for enhanced security
return {
ip: c.env?.CF_CONNECTING_IP || c.req.header('x-forwarded-for'),
userAgent: c.req.header('user-agent'),
fingerprint: c.req.header('cf-ray'),
country: c.req.header('cf-ipcountry'),
deviceType: c.req.header('cf-device-type'),
timezone: c.req.header('cf-timezone'),
};
});
adapter.registerRoutes(app, '/api/auth');
// Enable user population on all requests (optional)
app.use('*', adapter.createUserMiddleware());
// or use a standalone app
// const authApp = adapter.createApp();
// app.route('/api/auth', authApp);Device Info Extraction
Device information extraction is a core security feature that allows you to capture and analyze request metadata for enhanced authentication, fraud prevention, and analytics. All adapters support customizable device info extraction through their constructor parameters.
Why Device Info Matters
Device information enables:
- Geographic Analysis: Track user locations and detect suspicious access patterns
- Device Fingerprinting: Identify and validate trusted devices
- Security Monitoring: Detect unusual access patterns or potential breaches
- Analytics: Gather insights about user behavior and platform usage
- Compliance: Meet regulatory requirements for access logging
Express.js Example
const adapter = createExpressAdapter(config, false, async (req) => {
return {
ip: req.ip || req.connection.remoteAddress,
userAgent: req.get('User-Agent'),
fingerprint: req.get('X-Request-ID'),
geoLocation: req.get('CF-IPCountry'),
deviceType: req.get('CF-Device-Type'),
sessionId: req.session?.id,
browserFingerprint: req.get('X-Browser-Fingerprint'),
};
});Fastify Example
const adapter = createFastifyAdapter(config, async (request) => {
return {
ip: request.ip,
userAgent: request.headers['user-agent'],
fingerprint: request.headers['x-request-id'],
geoLocation: request.headers['cf-ipcountry'],
deviceType: request.headers['cf-device-type'],
timezone: request.headers['cf-timezone'],
};
});Hono Example
const adapter = createHonoAdapter(config, async (c) => {
return {
ip: c.env?.CF_CONNECTING_IP || c.req.header('x-forwarded-for'),
userAgent: c.req.header('user-agent'),
fingerprint: c.req.header('cf-ray'),
country: c.req.header('cf-ipcountry'),
deviceType: c.req.header('cf-device-type'),
timezone: c.req.header('cf-timezone'),
datacenter: c.req.header('cf-ray')?.split('-')[0],
};
});Advanced Device Info
For comprehensive device analysis, you can extract extensive metadata:
const deviceInfoExtractor = async (request) => {
// Framework-specific extraction logic
const basicInfo = extractBasicInfo(request);
// Additional processing
const enhancedInfo = await enrichDeviceInfo(basicInfo);
// Database lookups, external API calls, etc.
const riskScore = await calculateRiskScore(enhancedInfo);
return {
...enhancedInfo,
riskScore,
trustLevel: riskScore < 0.3 ? 'high' : riskScore < 0.7 ? 'medium' : 'low',
lastSeen: new Date().toISOString(),
};
};Best Practices
- Privacy Compliance: Ensure device info collection complies with privacy regulations (GDPR, CCPA, etc.)
- Minimal Collection: Only collect what's necessary for your security and analytics needs
- Secure Storage: Handle sensitive device data appropriately
- Performance: Keep device info extraction fast to avoid impacting response times
- Error Handling: Gracefully handle extraction failures
Full Examples
Complete examples with detailed configurations are available in the examples/ directory:
- Express Example - Full Express.js integration with middleware, security, and error handling
- Fastify Example - High-performance Fastify setup with schema validation
- Hono Example - Edge-optimized Hono deployment for serverless environments
Getting Current User
The V2 HTTP adapters provide multiple ways to access the current authenticated user in your route handlers.
Method 1: Using User Middleware (Recommended)
Automatically populate user information on all requests:
Express.js
// Add user middleware globally
app.use(adapter.createUserMiddleware());
// Access user in any route handler
app.get('/api/profile', (req, res) => {
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
res.json({
message: 'Profile data',
user: req.user.subject,
sessionValid: req.user.valid,
});
});Fastify
// Register user plugin globally
fastify.register(adapter.createUserPlugin());
// Access user in any route handler
fastify.get('/api/profile', async (request, reply) => {
const user = (request as any).user;
if (!user) {
return reply.status(401).send({ error: 'Authentication required' });
}
return {
message: 'Profile data',
user: user.subject,
sessionValid: user.valid,
};
});Hono
// Add user middleware globally
app.use('*', adapter.createUserMiddleware());
// Access user in any route handler
app.get('/api/profile', (c) => {
const user = c.get('user');
if (!user) {
return c.json({ error: 'Authentication required' }, 401);
}
return c.json({
message: 'Profile data',
user: user.subject,
sessionValid: user.valid,
});
});Method 2: Manual User Lookup
Check for current user when needed:
// Express
app.get('/api/dashboard', async (req, res) => {
const user = await adapter.getCurrentUser(req);
if (!user) {
return res.status(401).json({ error: 'Authentication required' });
}
res.json({ user: user.subject });
});
// Fastify
fastify.get('/api/dashboard', async (request, reply) => {
const user = await adapter.getCurrentUser(request);
if (!user) {
return reply.status(401).send({ error: 'Authentication required' });
}
return { user: user.subject };
});
// Hono
app.get('/api/dashboard', async (c) => {
const user = await adapter.getCurrentUser(c);
if (!user) {
return c.json({ error: 'Authentication required' }, 401);
}
return c.json({ user: user.subject });
});Method 3: Optional Authentication
Handle routes that work with or without authentication:
app.get('/api/content', async (req, res) => {
const user = await adapter.getCurrentUser(req);
res.json({
message: 'Content data',
isAuthenticated: !!user,
user: user?.subject || null,
content: user ? 'Premium content' : 'Public content',
});
});AuthenticatedUser Type
The user object contains:
interface AuthenticatedUser {
subject: any; // The authenticated user data from the session
token: string; // The session token
valid: boolean; // Whether the session is valid
metadata?: {
// Optional session metadata
expiresAt?: string;
createdAt?: string;
lastAccessed?: string;
[key: string]: any;
};
}Session Token Sources
The adapters automatically check for session tokens in:
- Authorization header:
Bearer <token> - Cookies:
reauth-session=<token> - Request body:
{ "token": "<token>" }
📝 Logging
HTTP adapters provide comprehensive logging for all HTTP operations, authentication flows, and middleware execution. All adapters require a logger instance to be passed to the ReAuth engine.
Logger Setup
import { createDefaultLogger } from '@re-auth/logger';
// Create logger with HTTP-specific tags
const logger = createDefaultLogger({
prefix: 'MyApp',
prefixEnv: 'REAUTH_',
enabledTags: ['auth', 'session', 'http'],
timestampFormat: 'human',
emojis: true
});
// Pass to ReAuth engine
const engine = new ReAuthEngine({
/* config */,
logger: logger // Required
});HTTP-Specific Log Tags
http- HTTP adapter operations (requests, responses, middleware)request- Incoming HTTP requestsresponse- HTTP responses and status codesmiddleware- Middleware execution and authentication checkserror- HTTP errors and exception handling
Environment Variable Control
# Enable HTTP and authentication logging
REAUTH_DEBUG=http,auth,session
# Enable all logging
REAUTH_DEBUG=*
# Production logging (JSON format)
NODE_ENV=productionLog Examples
// Development output
[10:30:00am 15 Jan 2024] [MyApp] [http] ℹ️ POST /api/auth/login
[10:30:01am 15 Jan 2024] [MyApp] [auth] ✅ User authentication successful
[10:30:01am 15 Jan 2024] [MyApp] [http] ✅ 200 OK response
// Production output (JSON)
{"level":"info","tags":["http"],"message":"POST /api/auth/login","timestamp":"2024-01-15T10:30:00.000Z","prefix":"MyApp"}
{"level":"info","tags":["auth"],"message":"User authentication successful","timestamp":"2024-01-15T10:30:01.000Z","prefix":"MyApp"}Configuration
Basic Configuration
import { createHttpAdapterV2 } from '@re-auth/http-adapters-v2';
const adapter = createHttpAdapterV2({
engine: reAuthEngine,
framework: 'express', // 'express' | 'fastify' | 'hono'
basePath: '/api/v2',
cors: {
origin: ['https://app.example.com'],
credentials: true,
allowedHeaders: ['Content-Type', 'Authorization'],
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
},
rateLimit: {
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // requests per window
message: 'Too many requests, please try again later.',
},
security: {
helmet: true, // Enable security headers
csrf: false, // Disable CSRF (for API-only usage)
sanitizeInput: true,
sanitizeOutput: false,
},
validation: {
validateInput: true,
maxPayloadSize: 1024 * 1024, // 1MB
allowedFields: [], // Empty = allow all
sanitizeFields: ['email', 'username', 'name'],
},
});Advanced Configuration
const adapter = createExpressAdapter({
engine: reAuthEngine,
basePath: '/auth',
cors: {
origin: (origin, callback) => {
// Custom origin validation
const allowedOrigins = ['https://app.example.com', 'https://admin.example.com'];
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
},
rateLimit: {
windowMs: 15 * 60 * 1000,
max: (req) => (req.user?.isPremium ? 1000 : 100), // Dynamic limits
keyGenerator: (req) => `${req.ip}:${req.headers['user-agent']}`,
skipSuccessfulRequests: true,
},
security: {
helmet: {
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
},
},
},
},
});API Endpoints
Authentication Steps
Execute plugin-specific authentication steps:
POST /auth/:plugin/:step
GET /auth/:plugin/:step
PUT /auth/:plugin/:step
PATCH /auth/:plugin/:step
DELETE /auth/:plugin/:stepExample:
curl -X POST http://localhost:3000/api/auth/email-password/login \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]", "password": "secret"}'Session Management
GET /session # Check session validity
POST /session # Create new session
DELETE /session # Destroy current sessionPlugin Introspection
GET /plugins # List all plugins
GET /plugins/:plugin # Get plugin detailsSystem
GET /introspection # Full API introspection
GET /health # Health checkResponse Format
All endpoints return a consistent response format:
{
success: boolean;
data?: any;
error?: {
code: string;
message: string;
details?: any;
};
meta: {
timestamp: string;
requestId?: string;
};
}Success Response
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"subject": {
"id": "user123",
"email": "[email protected]"
}
},
"meta": {
"timestamp": "2024-01-01T00:00:00.000Z"
}
}Error Response
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid email format",
"details": {
"field": "email",
"value": "invalid-email"
}
},
"meta": {
"timestamp": "2024-01-01T00:00:00.000Z"
}
}Security Features
CORS
cors: {
origin: ['https://app.example.com'],
credentials: true,
allowedHeaders: ['Content-Type', 'Authorization'],
methods: ['GET', 'POST', 'PUT', 'DELETE']
}Rate Limiting
rateLimit: {
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // requests per window
keyGenerator: (req) => req.ip,
skipSuccessfulRequests: false
}Security Headers
security: {
helmet: true, // Enables:
// - X-Content-Type-Options: nosniff
// - X-Frame-Options: DENY
// - X-XSS-Protection: 1; mode=block
// - Referrer-Policy: strict-origin-when-cross-origin
// - Strict-Transport-Security (HTTPS only)
}Input Validation
validation: {
validateInput: true,
maxPayloadSize: 1024 * 1024, // 1MB
sanitizeFields: ['email', 'username', 'name'],
allowedFields: [] // Empty = allow all
}Error Handling
The adapter provides comprehensive error handling with specific error types:
import { HttpAdapterError, ValidationError, AuthenticationError, AuthorizationError, NotFoundError, RateLimitError } from '@re-auth/http-adapters-v2';
// Custom error handling
app.use((err, req, res, next) => {
if (err instanceof ValidationError) {
res.status(400).json({
success: false,
error: {
code: err.code,
message: err.message,
details: err.details,
},
});
}
// ... handle other error types
});Migration from V1
The V2 adapter is designed to work alongside V1 adapters. Key differences:
- Engine Integration: Uses
ReAuthEngineV2instead ofReAuthEngine - Plugin Discovery: Automatically discovers V2 plugins and their steps
- Type Safety: Enhanced TypeScript integration
- Response Format: Consistent JSON response format
- Security: Built-in security features
Migration Steps
- Install the V2 adapter package
- Update your engine to
ReAuthEngineV2 - Replace V1 adapter imports with V2 equivalents
- Update configuration format
- Test endpoints and response formats
License
MIT
