Welcome to the Jose Madrid Salsa developer docs — explore features, APIs, and deployment guides.
Jose Madrid SalsaJMS Docs

Security Best Practices

Security checklist and best practices for the Jose Madrid Salsa platform

Security Best Practices

This guide covers the security measures built into the Jose Madrid Salsa platform and best practices for maintaining a secure deployment.

Authentication Security

Password Hashing

All passwords are hashed with bcrypt before storage:

import bcrypt from 'bcryptjs'

// Registration
const hashedPassword = await bcrypt.hash(password, 10)

// Verification
const isValid = await bcrypt.compare(providedPassword, user.password)

Salt Rounds

The platform uses 10 bcrypt salt rounds. Do not lower this value. Higher values increase security but slow down login.

JWT Session Security

// In lib/auth.ts
session: {
  strategy: 'jwt',
  maxAge: 30 * 24 * 60 * 60, // 30 days
},
useSecureCookies: process.env.NODE_ENV === 'production',
  • Sessions use JWTs, not database-backed sessions
  • Secure cookies are enforced in production
  • NEXTAUTH_SECRET is validated at startup

Rate Limiting on Auth

Login and password reset endpoints have strict rate limits:

AUTH_LOGIN:     { maxRequests: 5, windowSeconds: 900 }    // 5 per 15 min
PASSWORD_RESET: { maxRequests: 3, windowSeconds: 3600 }   // 3 per hour

Input Validation

Zod Schema Validation

All API inputs are validated using Zod schemas before processing:

import { CreateOrderSchema } from '@/lib/validations/orders'

const parsed = CreateOrderSchema.safeParse(json)
if (!parsed.success) {
  return NextResponse.json(
    { error: 'Invalid payload', details: parsed.error.flatten() },
    { status: 400 }
  )
}

Validation schemas exist for:

  • Order creation and updates (lib/validations/orders.ts)
  • Cart operations (lib/validations/cart.ts)
  • Payment processing (lib/validations/payment.ts)

HTML Escaping

User-generated content is escaped before rendering in emails:

function escapeHtml(str: string) {
  return str
    .replace(/&/g, '&')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;')
}

Authorization

Role-Based Access Control

Every admin endpoint verifies user roles and permissions:

// Throws 403 if user lacks permission
const user = await requirePermission('orders:write')

// Check specific roles
const user = await requireRole(['ADMIN', 'DEVELOPER'])

// Check admin panel access
const hasAccess = await canAccessAdmin() // ADMIN, DEVELOPER, STAFF

Principle of Least Privilege

The permission system follows least privilege:

RolePermissions
CUSTOMERNone (storefront only)
WHOLESALENone (storefront + wholesale pricing)
FUNDRAISEROwn dashboard, page editing, asset uploads
STAFFOrders, products, content, messaging
ADMINEverything

Secret Management

Environment Variables

Secrets are never hardcoded. All sensitive values come from environment variables:

# Required secrets
NEXTAUTH_SECRET=          # JWT signing
MASTER_KEY=               # AES-256 encryption
STRIPE_SECRET_KEY=        # Payment processing
STRIPE_WEBHOOK_SECRET=    # Webhook verification
RESEND_API_KEY=           # Email service
SHIPPING_API_KEY=         # Carrier API

Encryption

The admin panel uses AES-256 encryption for stored credentials:

# Generate a MASTER_KEY
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

The lib/encryption.ts and lib/crypto.ts modules handle encryption/decryption operations.

Secret Scanning

The project includes Gitleaks for automated secret detection:

npm run security:scan      # Scan repository
npm run security:protect   # Pre-commit check (staged files)
npm run security:baseline  # Generate baseline report

The pre-commit hook automatically runs lint-staged && npm run security:protect.

API Security

Rate Limiting

All API endpoints are protected by rate limiting:

export const POST = withRateLimit(handlePost, RATE_LIMITS.API_GENERAL)
export const GET = withRateLimit(handleGet, RATE_LIMITS.API_GENERAL)

Rate limit headers inform clients of their remaining quota.

CORS and Request Validation

API routes validate the request method and content type. The Next.js middleware (proxy) handles CORS and header validation.

Audit Logging

All admin actions are recorded in the audit log:

await logAuditWithRequest({
  userId: user.id,
  action: 'create',
  entityType: 'Order',
  entityId: order.id,
  changes: { orderNumber, total },
}, request)

Audit logs capture: user ID, action type, entity type/ID, change details, IP address, and user agent.

Payment Security

Stripe Integration

  • Payment processing happens server-side via Stripe API
  • The client only receives a clientSecret for the PaymentIntent
  • Webhook signatures are verified with STRIPE_WEBHOOK_SECRET
  • No card data is stored in the application database

Webhook Verification

const event = stripe.webhooks.constructEvent(
  body,
  sig,
  process.env.STRIPE_WEBHOOK_SECRET!
)

Data Protection

Unsubscribe Management

Email unsubscribe preferences are stored per-user and checked before sending:

// Check suppression before sending
const suppressed = await checkSuppression(email)
if (suppressed) return { skipped: true }

Password Reset Tokens

Reset tokens are time-limited (1 hour) and single-use:

model PasswordResetToken {
  id        String   @id @default(cuid())
  userId    String
  token     String   @unique
  expiresAt DateTime
  usedAt    DateTime?
}

Security Checklist

Environment Validation

Verify all required secrets are set:

  • NEXTAUTH_SECRET (32+ characters)
  • MASTER_KEY (64 hex characters)
  • STRIPE_SECRET_KEY
  • STRIPE_WEBHOOK_SECRET

Pre-Commit Checks

Ensure Gitleaks is running:

  • Husky hooks installed (npm run prepare)
  • security:protect runs on commit
  • No secrets in committed code

Access Control

Verify RBAC is configured:

  • Permission tables are seeded
  • Admin users have correct roles
  • API routes check permissions

Monitoring

Set up error and security monitoring:

  • Sentry configured for error tracking
  • Audit logs capturing admin actions
  • Rate limiting active on all endpoints

Key Files

auth.ts
rbac.ts
encryption.ts
crypto.ts
rate-limiter.ts
audit.ts
validation.ts

How is this guide?

Edit on GitHub

Last updated on

On this page