@csrf-armor/core v1.0.3
@csrf-armor/core
Framework-agnostic CSRF protection with multiple security strategies and zero dependencies.
Built for modern web applications that need flexible, high-performance CSRF protection without vendor lock-in.
🚀 Quick Start
npm install @csrf-armor/core
import { generateSignedToken, parseSignedToken } from '@csrf-armor/core';
// Generate a secure token
const token = await generateSignedToken('your-32-char-secret', 3600);
// Validate the token later
const payload = await parseSignedToken(submittedToken, 'your-32-char-secret');
console.log('Token valid until:', new Date(payload.exp * 1000));
⚠️ SECURITY WARNING: Use a strong secret in production! Generate with
crypto.getRandomValues(new Uint8Array(32))
.
🛡️ Choose Your Strategy
Strategy | Security | Performance | Best For | Setup Complexity |
---|---|---|---|---|
Signed Double Submit ⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | E-commerce, finance | Medium |
Double Submit | ⭐ | ⭐⭐⭐⭐⭐ | Local development | Easy |
Signed Token | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | APIs, microservices | Medium |
Origin Check | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | Mobile backends | Easy |
Hybrid | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | Maximum security | Hard |
🔧 Framework Integration
Express.js
import { CsrfAdapter, createCsrfProtection } from '@csrf-armor/core';
class ExpressAdapter implements CsrfAdapter<express.Request, express.Response> {
extractRequest(req: express.Request) {
return {
method: req.method,
url: req.url,
headers: new Map(Object.entries(req.headers as Record<string, string>)),
cookies: new Map(Object.entries(req.cookies || {})),
body: req.body
};
}
applyResponse(res: express.Response, csrfResponse: CsrfResponse) {
if (csrfResponse.headers instanceof Map) {
for (const [key, value] of csrfResponse.headers) {
res.setHeader(key, value);
}
}
if (csrfResponse.cookies instanceof Map) {
for (const [name, { value, options }] of csrfResponse.cookies) {
res.cookie(name, value, options);
}
}
return res;
}
async getTokenFromRequest(request: CsrfRequest, config: RequiredCsrfConfig) {
const headers = request.headers instanceof Map
? request.headers
: new Map(Object.entries(request.headers));
return headers.get(config.token.headerName.toLowerCase());
}
}
// Usage
const csrfProtection = createCsrfProtection(new ExpressAdapter(), {
strategy: 'signed-double-submit',
secret: process.env.CSRF_SECRET!
});
app.use(async (req, res, next) => {
const result = await csrfProtection.protect(req, res);
if (result.success) {
req.csrfToken = result.token;
next();
} else {
res.status(403).json({ error: 'CSRF validation failed' });
}
});
Next.js
💡 Complete Next.js solution: @csrf-armor/nextjs with React hooks and simplified setup.
🔌 More framework examples and adapters: Advanced Configuration Guide →
⚙️ Configuration
Basic Setup
import { createCsrfProtection } from '@csrf-armor/core';
// Recommended for most applications
const csrfProtection = createCsrfProtection(adapter, {
strategy: 'signed-double-submit',
secret: process.env.CSRF_SECRET!, // ⚠️ Required in production
cookie: {
secure: true, // HTTPS only
sameSite: 'strict' // Strict same-site policy
}
});
Strategy-Specific Configuration
// High Security (Financial, Healthcare)
{ strategy: 'hybrid', secret: process.env.CSRF_SECRET!, allowedOrigins: ['https://app.com'] }
// High Performance (Public APIs)
{ strategy: 'origin-check', allowedOrigins: ['https://mobile.app'] }
// Balanced (Most Web Apps)
{ strategy: 'signed-double-submit', secret: process.env.CSRF_SECRET! }
// Development
{ strategy: 'double-submit', cookie: { secure: false } }
📚 Complete configuration options: Advanced Configuration Guide →
🔍 Common Issues
❓ Getting "Token mismatch" errors?
// Ensure your adapter extracts tokens from all sources
async getTokenFromRequest(request: CsrfRequest, config: RequiredCsrfConfig) {
const headers = request.headers instanceof Map
? request.headers
: new Map(Object.entries(request.headers));
// Try header first
const headerValue = headers.get(config.token.headerName.toLowerCase());
if (headerValue) return headerValue;
// Try form data
if (request.body && typeof request.body === 'object') {
const body = request.body as Record<string, unknown>;
const formValue = body[config.token.fieldName];
if (typeof formValue === 'string') return formValue;
}
return undefined;
}
❓ Tokens not working across subdomains?
const config = {
cookie: {
domain: '.yourdomain.com', // Note the leading dot
sameSite: 'lax' // 'strict' blocks cross-subdomain
}
};
❓ CSRF blocking legitimate requests?
const config = {
excludePaths: ['/api/webhooks', '/api/public', '/health'],
skipContentTypes: ['application/json'] // For JSON-only APIs
};
❓ Performance issues?
Choose a faster strategy or exclude read-only endpoints:
// Option 1: Faster strategy
{ strategy: 'double-submit' } // No crypto overhead
// Option 2: Exclude read-only paths
{ excludePaths: ['/api/read', '/api/search'] }
🧠 Core API
Token Functions
// Generate signed tokens
const token = await generateSignedToken('secret', 3600);
// Parse and validate
const payload = await parseSignedToken(token, 'secret');
console.log('Expires:', new Date(payload.exp * 1000));
// Generate random nonces
const nonce = generateNonce(32); // 64 hex characters
Protection Class
const protection = createCsrfProtection(adapter, config);
const result = await protection.protect(request, response);
if (result.success) {
console.log('CSRF token:', result.token);
} else {
console.error('Validation failed:', result.reason);
}
Error Handling
import { TokenExpiredError, TokenInvalidError, OriginMismatchError } from '@csrf-armor/core';
try {
await parseSignedToken(token, secret);
} catch (error) {
if (error instanceof TokenExpiredError) {
// Handle expired token
} else if (error instanceof TokenInvalidError) {
// Handle invalid signature
}
}
📖 Complete API documentation: Advanced Configuration Guide →
📚 Documentation
- Advanced Configuration Guide - Complex setups, custom strategies, all config options
- Security Analysis - Security model deep-dive and best practices
- Migration Guide - How to migrate from existing CSRF libraries
🤝 Contributing
Community contributions welcome! This project would benefit from:
🎯 High-Impact Contributions:
- Framework adapters: Express, Fastify, Koa, SvelteKit, Remix
- Performance optimizations: Benchmark improvements, edge cases
- Security enhancements: Vulnerability reports, new strategies
- Developer experience: Better examples, TypeScript improvements
🚀 Getting Started:
1. Fork the repository
2. Create a feature branch: git checkout -b feature/express-adapter
3. Make your changes with tests
4. Submit a PR with clear description
💬 Get Help:
📦 Related Packages
- @csrf-armor/nextjs - Next.js App Router middleware and React hooks
More framework packages coming based on community demand and contributions!
📄 License
MIT © Muneeb Samuels
Questions? Open an issue or start a discussion!