1.0.3 • Published 5 months ago

@csrf-armor/core v1.0.3

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

@csrf-armor/core

CodeQL CI npm version TypeScript License: MIT

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

StrategySecurityPerformanceBest ForSetup Complexity
Signed Double Submit⭐⭐⭐⭐⭐⭐⭐⭐⭐E-commerce, financeMedium
Double Submit⭐⭐⭐⭐⭐Local developmentEasy
Signed Token⭐⭐⭐⭐⭐⭐⭐⭐APIs, microservicesMedium
Origin Check⭐⭐⭐⭐⭐⭐⭐⭐Mobile backendsEasy
Hybrid⭐⭐⭐⭐⭐⭐⭐⭐Maximum securityHard

🔧 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


🤝 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

More framework packages coming based on community demand and contributions!


📄 License

MIT © Muneeb Samuels

Questions? Open an issue or start a discussion!

1.0.3

5 months ago

1.0.2

5 months ago

1.0.1

5 months ago

1.0.0

5 months ago

0.0.1

5 months ago