• Skip to main content
  • Skip to navigation
  • Skip to search
    Petunia™
    FeaturesPricingIntegrationsAboutContact
    Log inStart free trialSign up
    Loading
    Petunia™

    Reimagining customer communication for the modern business.

    Product

    • Features
    • Pricing
    • Integrations
    • Roadmap
    • What's New

    Resources

    • Help Center
    • Documentation
    • Guides
    • API Reference
    • Community
    • Support

    Company

    • About Us
    • Careers
    • Blog
    • Press
    • Contact

    © 2026 Gray Group International LLC. All rights reserved.·
    Made by gardenpatch 🌱

    Privacy PolicyTerms of ServiceCookie Policy

    Petunia™ is a trademark of Gray Group International LLC. The Petunia name, brand, product design, and content are proprietary. Unauthorized use, imitation, or copying is prohibited.

    Documentation

    SECURITY_GUIDE

    docs/SECURITY_GUIDE.md
    Docs homeGuidesSupport
    Quick links
    Start here
    How the docs are organized.
    Environment setup
    Configure env + run locally.
    Unified Inbox
    Inbox concepts & behavior.
    Voice AI setup
    Providers, Twilio, testing.
    Pricing model
    Source-of-truth pricing.
    Operations runbook
    How to operate safely.

    Security Best Practices Guide

    Version: 1.0.0 Last Updated: 2025-12-13 Author: Petunia Team Status: Production

    Table of Contents

    1. Overview
    2. Security Architecture
    3. Authentication
    4. Authorization (RBAC)
    5. Encryption
    6. Secret Management
    7. Rate Limiting & DDoS Protection
    8. CSRF Protection
    9. Session Management
    10. Audit Logging
    11. Security Headers
    12. API Security
    13. Security Review Checklist

    Overview

    Petunia implements defense-in-depth security with multiple layers of protection:

    • Authentication: Supabase Auth with MFA support
    • Authorization: Role-based access control (RBAC) with tenant isolation
    • Encryption: AES-256-GCM with tenant-specific keys (HKDF-derived)
    • Session Security: Automatic rotation on privilege changes
    • Rate Limiting: IP and endpoint-based throttling
    • Audit Logging: Comprehensive tracking of all security events
    • CSRF Protection: Double-submit cookie pattern
    • Security Headers: CSP, HSTS, X-Frame-Options, etc.

    Security Principles

    1. Zero Trust: Never trust, always verify
    2. Least Privilege: Grant minimal necessary permissions
    3. Defense in Depth: Multiple security layers
    4. Fail Secure: Security failures should deny access
    5. Audit Everything: Log all security-relevant events

    Security Architecture

    Layers of Defense

    ┌─────────────────────────────────────────────────────────────┐
    │                    Edge Middleware                          │
    │  - Rate Limiting                                            │
    │  - CSRF Validation                                          │
    │  - Security Headers                                         │
    └─────────────────────────────────────────────────────────────┘
                                │
    ┌─────────────────────────────────────────────────────────────┐
    │              Authentication Layer (Supabase)                │
    │  - Email/Password + OAuth                                   │
    │  - MFA (TOTP)                                              │
    │  - Email Verification                                       │
    │  - Device Fingerprinting                                    │
    └─────────────────────────────────────────────────────────────┘
                                │
    ┌─────────────────────────────────────────────────────────────┐
    │           Authorization Layer (RBAC + RLS)                  │
    │  - Role-based permissions                                   │
    │  - Tenant isolation (clientId)                             │
    │  - Database Row-Level Security                              │
    └─────────────────────────────────────────────────────────────┘
                                │
    ┌─────────────────────────────────────────────────────────────┐
    │              Data Protection Layer                          │
    │  - Encryption at rest (AES-256-GCM)                        │
    │  - Encryption in transit (TLS 1.3)                         │
    │  - Tenant-isolated encryption keys                          │
    └─────────────────────────────────────────────────────────────┘
                                │
    ┌─────────────────────────────────────────────────────────────┐
    │                 Audit & Monitoring                          │
    │  - Comprehensive audit logs                                 │
    │  - Security event monitoring                                │
    │  - Anomaly detection                                        │
    └─────────────────────────────────────────────────────────────┘
    

    Key Security Files

    ComponentLocation
    Encryption/lib/utils/encryption.ts
    Authentication/lib/auth/helpers.ts, /lib/auth/server.ts
    Authorization/lib/auth/permissions.ts
    Rate Limiting/lib/auth/otp-rate-limit.ts, /lib/security/edge-rate-limiter.ts
    CSRF/lib/security/edge-csrf.ts
    Audit Logging/lib/auth/audit.ts
    Session Management/lib/auth/session-manager.ts, /lib/auth/sessionRotation.ts
    API Security/lib/utils/api-security.ts
    Security Headers/lib/security/edge-security-headers.ts
    Middleware/proxy.ts

    Authentication

    Supabase Auth Integration

    Petunia uses Supabase Auth for user authentication with the following features:

    • Email/password authentication
    • OAuth providers (Google, Facebook)
    • Email verification
    • Password reset
    • Multi-factor authentication (MFA)

    Server-Side Auth Helpers

    // File: /lib/auth/helpers.ts
    import { auth } from '@/lib/auth/server';
    
    // Require authentication
    const session = await requireAuth();
    
    // Require specific role
    const session = await requireRole('admin');
    
    // Require client access
    const session = await requireClientAccess();
    

    Client-Side Auth

    // File: /lib/hooks/auth/useAuth.ts
    import { useAuth } from '@/hooks/auth/useAuth';
    
    function MyComponent() {
      const { user, session, isLoading } = useAuth();
    
      if (isLoading) return <Loading />;
      if (!user) return <SignIn />;
    
      return <Dashboard user={user} />;
    }
    

    Multi-Factor Authentication (MFA)

    Setup Flow

    1. User enables MFA in settings
    2. Generate TOTP secret: generateSecret()
    3. Encrypt secret before storing: encryptMfaSecret(secret)
    4. Display QR code to user
    5. Verify initial code: verifyTOTP(token, secret)
    6. Store encrypted secret in database

    Verification Flow

    1. User enters TOTP code
    2. Retrieve encrypted secret from database
    3. Decrypt secret: decryptMfaSecret(encryptedSecret)
    4. Verify code: verifyTOTP(token, secret, window)
    5. Log audit event: audit.twoFAVerified(userId)

    Implementation

    // File: /lib/utils/totp.ts
    
    // Generate a new TOTP secret
    const secret = generateSecret();
    
    // Build OTP auth URL for QR code
    const otpauthURL = buildOtpauthURL(secret, user.email, 'Petunia');
    
    // Encrypt secret before storing
    const encryptedSecret = encryptMfaSecret(secret);
    await db.user.update({ mfaSecret: encryptedSecret });
    
    // Verify TOTP code
    const isValid = verifyTOTP(userCode, decryptMfaSecret(user.mfaSecret));
    

    Security Features

    • Encrypted Storage: MFA secrets stored using AES-256-GCM
    • Time Window: Configurable window (default: ±1 period = 60s)
    • Backup Codes: Generate one-time backup codes for recovery
    • Rate Limiting: Max 3 attempts per OTP session

    Device Fingerprinting

    Track trusted devices to detect anomalous logins:

    // File: /lib/auth/device-fingerprint.ts
    
    // Generate device fingerprint
    const fingerprint = await generateDeviceFingerprint();
    
    // Check if device is trusted
    const trustLevel = await checkDeviceTrust(userId, fingerprint.fingerprint);
    
    // Record device login
    await recordDeviceLogin(userId, fingerprint);
    
    // Trust a device
    await trustDevice(userId, fingerprint, 'My MacBook Pro', 30); // 30 days
    

    Trust Levels

    • UNKNOWN: First time seeing this device
    • TRUSTED: Explicitly trusted by user
    • SUSPICIOUS: Anomalous behavior detected
    • BLOCKED: Explicitly blocked

    Email Verification

    All new users must verify their email:

    1. User signs up
    2. Verification email sent with token
    3. User clicks link: /auth/verify-email?token=xxx
    4. Token verified, email confirmed
    5. Audit log: audit.emailVerified(userId, email)

    Authorization (RBAC)

    Role Hierarchy

    owner (100)      - Full access to everything
      ↓
    admin (80)       - Manage team, integrations, settings
      ↓
    manager (60)     - View analytics, manage automation
      ↓
    agent (40)       - Send messages, view assigned leads
      ↓
    viewer (20)      - Read-only access
    

    Permissions

    Each role has specific permissions:

    // File: /lib/auth/permissions.ts
    
    export type Permission =
      | 'manage_team'
      | 'manage_billing'
      | 'manage_integrations'
      | 'view_analytics'
      | 'send_messages'
      | 'manage_automation'
      | 'export_data'
      | 'delete_data'
      | 'view_all_conversations'
      | 'manage_settings'
      | 'manage_api_keys'
      | 'view_audit_logs';
    

    Usage

    import { permissionsService } from '@/lib/auth/permissions';
    
    // Check permission
    const canExport = await permissionsService.hasPermission(userId, 'export_data');
    
    // Check any permission
    const hasAccess = await permissionsService.hasAnyPermission(userId, [
      'manage_team',
      'manage_settings'
    ]);
    
    // Check all permissions
    const isFullAdmin = await permissionsService.hasAllPermissions(userId, [
      'manage_team',
      'manage_billing',
      'manage_integrations'
    ]);
    
    // Check if user can manage another user
    const canManage = await permissionsService.canManageUser(managerId, targetUserId);
    

    Decorator Pattern

    class MyService {
      @requirePermission('export_data')
      async exportData(request: { userId: string }) {
        // Only called if user has permission
      }
    }
    

    Tenant Isolation

    All data is isolated by clientId:

    • Database queries filtered by RLS policies
    • Encryption keys derived per tenant (HKDF)
    • API responses filtered by client access
    // Always verify client access
    const session = await requireClientAccess();
    const clientId = session.user.clientId;
    
    // All queries scoped to client
    const leads = await prisma.lead.findMany({
      where: { clientId }
    });
    

    Encryption

    Overview

    Petunia uses AES-256-GCM (Galois/Counter Mode) for all encryption:

    • Key Size: 256 bits
    • IV: 16 bytes (random per encryption)
    • Auth Tag: 16 bytes (prevents tampering)
    • Format: iv:authTag:encryptedData (base64)

    Encryption Key Derivation

    Master Key (CREDENTIALS_ENCRYPTION_KEY)
        ↓
      PBKDF2 (100,000 iterations) + Salt
        ↓
    Derived Master Key (32 bytes)
        ↓
      HKDF (tenant ID as salt) [OPTIONAL]
        ↓
    Tenant-Specific Key (32 bytes)
    

    Tenant-Isolated Encryption

    For multi-tenant isolation, use tenant-specific keys:

    // File: /lib/utils/encryption.ts
    
    // Encrypt with tenant isolation
    const encrypted = encryptData(data, { tenantId: clientId });
    
    // Decrypt with tenant isolation
    const decrypted = decryptData(encrypted, { tenantId: clientId });
    
    // Convenience functions
    const encrypted = encryptForTenant(data, clientId);
    const decrypted = decryptForTenant(encrypted, clientId);
    

    Security Benefit: Even if master key is compromised, tenant data remains isolated because each tenant uses a cryptographically derived unique key.

    Use Cases

    1. API Keys & OAuth Tokens

    // File: /lib/utils/integration-token-encryption.ts
    
    // Encrypt integration token (with tenant isolation)
    const encryptedToken = encryptIntegrationToken(accessToken, {
      integrationId: 'GoogleConnection',
      tenantId: clientId
    });
    
    // Decrypt integration token (with tenant isolation)
    const accessToken = decryptIntegrationToken(encryptedToken, {
      integrationId: 'GoogleConnection',
      tenantId: clientId
    });
    

    2. MFA Secrets

    // File: /lib/utils/totp.ts
    
    // Encrypt MFA secret
    const encryptedSecret = encryptMfaSecret(totpSecret);
    
    // Decrypt MFA secret
    const totpSecret = decryptMfaSecret(user.mfaSecret);
    

    3. Sensitive User Data

    // Encrypt any sensitive data
    const encrypted = encryptData(sensitiveData);
    
    // Encrypt objects
    const encrypted = encryptObject({ ssn, taxId }, { tenantId: clientId });
    const data = decryptObject<{ ssn: string; taxId: string }>(encrypted, { tenantId: clientId });
    

    Validation

    // Check if data is encrypted
    const isEncrypted = isValidEncryptedFormat(data);
    
    // Expected format: "iv:authTag:encryptedData"
    // Example: "dGVzdA==:dGVzdA==:dGVzdGRhdGE="
    

    Key Rotation

    When rotating encryption keys:

    1. Generate new CREDENTIALS_ENCRYPTION_KEY
    2. Run migration script to re-encrypt all data
    3. Update environment variable
    4. Clear key cache: clearKeyCache()
    # Rotate encryption key
    node scripts/rotate-encryption-key.ts
    

    Security Best Practices

    • Never log decrypted data
    • Never expose encryption keys in client code
    • Use tenant isolation for multi-tenant data
    • Rotate keys annually or after security incidents
    • Validate encrypted format before decryption
    • Handle legacy plaintext gracefully during migration

    Secret Management

    Environment Variables

    All secrets stored in environment variables:

    # Critical secrets (rotate regularly)
    CREDENTIALS_ENCRYPTION_KEY=""  # openssl rand -base64 32
    SESSION_SECRET=""              # openssl rand -base64 32
    CSRF_SECRET=""                 # openssl rand -base64 32
    SUPABASE_SERVICE_ROLE_KEY=""   # From Supabase dashboard
    
    # Database
    DATABASE_URL=""                # Supabase connection pool
    DIRECT_DATABASE_URL=""         # Direct connection (migrations)
    
    # Auth
    SUPABASE_JWT_SECRET=""         # From Supabase dashboard
    NEXT_PUBLIC_SUPABASE_ANON_KEY="" # From Supabase dashboard
    NEXT_PUBLIC_SUPABASE_URL=""    # From Supabase dashboard
    
    # Third-party API keys
    OPENAI_API_KEY=""
    STRIPE_SECRET_KEY=""
    MAILGUN_API_KEY=""
    

    Secret Rotation Schedule

    SecretRotation FrequencyPriority
    CREDENTIALS_ENCRYPTION_KEYAnnuallyCritical
    SESSION_SECRETAnnuallyCritical
    CSRF_SECRETAnnuallyHigh
    SUPABASE_SERVICE_ROLE_KEYQuarterlyCritical
    API Keys (Stripe, OpenAI, etc.)When compromisedHigh
    OAuth SecretsWhen compromisedHigh

    Security Guidelines

    1. Never commit secrets to git

      • Use .env.local (gitignored)
      • Check .env.example for required variables
    2. Use strong secrets

      • Minimum 32 characters
      • Generate with: openssl rand -base64 32
      • No weak patterns (password, test, 12345, etc.)
    3. Production secrets

      • Store in Vercel environment variables
      • Use different secrets per environment
      • Enable "Encrypted" option in Vercel
    4. Key validation

      • System validates key strength on startup
      • Rejects weak patterns in production
      • Logs warnings in development
    // Automatic validation on encryption
    // File: /lib/utils/encryption.ts
    validateEncryptionKey(key, 'CREDENTIALS_ENCRYPTION_KEY');
    // Throws if:
    // - Length < 32 characters
    // - Contains weak patterns
    // - All same character
    

    Rate Limiting & DDoS Protection

    Overview

    Multi-layer rate limiting protects against abuse:

    1. Edge Middleware: IP-based rate limits
    2. API Routes: Endpoint-specific limits
    3. Auth Routes: Aggressive limits on sensitive endpoints
    4. OTP/2FA: Specialized limits for verification attempts

    Edge Rate Limiting

    // File: /lib/security/edge-rate-limiter.ts
    // Applied in proxy.ts
    
    const rateLimitType = pathname.startsWith('/api/auth/')
      ? 'auth'
      : pathname.startsWith('/api/')
        ? 'api'
        : 'page';
    
    const result = await rateLimit(request, rateLimitType);
    

    Rate Limit Tiers

    TierMax RequestsWindowUse Case
    health10001 minuteHealth checks, static assets
    page1001 minutePage navigation
    api601 minuteStandard API calls
    auth101 minuteAuthentication endpoints
    heavy101 minuteExpensive operations

    OTP Rate Limiting

    // File: /lib/auth/otp-rate-limit.ts
    
    // Check OTP request limit (5 per hour)
    const result = await checkOTPRequestLimit(email, 'email');
    
    if (!result.allowed) {
      // Rate limit exceeded
      return { error: result.message, resetAt: result.resetAt };
    }
    
    // Track OTP generation for verification limits
    await trackOTPGeneration(otpId);
    
    // Check verification attempts (3 max per OTP)
    const verifyResult = await checkOTPVerificationLimit(otpId);
    

    API Security Middleware

    // File: /lib/utils/api-security.ts
    
    export const middleware = createApiSecurityMiddleware({
      rateLimit: {
        max: 100,
        windowSec: 60,
        keyGen: 'ip',
        specialLimits: {
          '/api/auth/signin': { max: 5, windowSec: 300 },
          '/api/auth/signup': { max: 3, windowSec: 3600 }
        }
      }
    });
    

    Response Headers

    Rate limit information included in responses:

    X-RateLimit-Limit: 100
    X-RateLimit-Remaining: 73
    X-RateLimit-Reset: 2025-12-13T14:30:00.000Z
    

    When rate limited:

    HTTP 429 Too Many Requests
    Retry-After: 45
    

    Best Practices

    1. Use distributed cache (Redis) for production
    2. Monitor rate limit hits for DDoS attacks
    3. Whitelist trusted IPs (health checks, monitoring)
    4. Adjust limits per endpoint based on usage
    5. Log rate limit violations to audit log

    CSRF Protection

    Double-Submit Cookie Pattern

    Petunia uses the double-submit cookie pattern:

    1. Server sets secure, HTTP-only cookie with CSRF token
    2. Client includes same token in X-CSRF-Token header
    3. Server verifies tokens match

    Implementation

    // File: /lib/security/edge-csrf.ts
    
    // Generate CSRF token
    const token = generateCSRFToken();
    
    // Set cookie
    setCSRFCookie(response, token);
    
    // Verify CSRF token (in middleware)
    const result = await verifyCSRFToken(request);
    
    if (!result.valid) {
      return new NextResponse(
        JSON.stringify({ error: 'CSRF validation failed' }),
        { status: 403 }
      );
    }
    

    Protected Methods

    CSRF required for:

    • POST
    • PUT
    • PATCH
    • DELETE

    Exempt methods:

    • GET
    • HEAD
    • OPTIONS

    Exempt Routes

    CSRF validation skipped for:

    • /api/webhooks/* (HMAC signature auth)
    • /api/health/* (public health checks)
    • /api/auth/signin (initial auth)
    • /api/auth/signup (initial auth)

    Client Usage

    // React component
    import { useCSRF } from '@/hooks/useCSRF';
    
    function MyForm() {
      const { csrfToken } = useCSRF();
    
      const handleSubmit = async () => {
        await fetch('/api/data', {
          method: 'POST',
          headers: {
            'X-CSRF-Token': csrfToken
          },
          body: JSON.stringify(data)
        });
      };
    }
    

    Session Management

    Session Lifecycle

    1. Creation: User signs in, session created
    2. Validation: Each request validates session
    3. Refresh: Session refreshed periodically (30 min)
    4. Rotation: Session rotated on privilege changes
    5. Expiration: Session expires after max age (7 days)

    Session Rotation

    Automatic rotation occurs on:

    • Periodic: Every 7 days
    • Privilege Change: Role or permission changes
    • Password Change: All sessions invalidated
    • Suspicious Activity: Force rotation
    // File: /lib/auth/sessionRotation.ts
    
    // Check if rotation needed
    const { needsRotation, reason } = await sessionRotation.needsRotation(
      userId,
      sessionCreatedAt
    );
    
    // Rotate session
    const result = await sessionRotation.rotateSession(
      userId,
      'privilege_change',
      { ipAddress, userAgent }
    );
    
    // Force logout all sessions
    await sessionRotation.forceLogoutAll(userId, 'password_change');
    

    Rotation Policy

    const DEFAULT_ROTATION_POLICY = {
      maxAge: 7 * 24 * 60 * 60 * 1000,        // 7 days
      rotationInterval: 7 * 24 * 60 * 60 * 1000, // 7 days
      gracePeriod: 5 * 60 * 1000,              // 5 minutes
      rotateOnPasswordChange: true,
      rotateOnPrivilegeChange: true
    };
    

    Session Storage

    Sessions stored in:

    • Database: Session table (source of truth)
    • Cookies: Encrypted JWT token
    • Local Storage: Backup for recovery (client)

    Concurrent Session Management

    Users can have multiple active sessions (different devices):

    // List user's sessions
    const sessions = await prisma.session.findMany({
      where: { userId, expires: { gt: new Date() } }
    });
    
    // Revoke specific session
    await prisma.session.update({
      where: { id: sessionId },
      data: { expires: new Date() }
    });
    

    Audit Logging

    Comprehensive Event Tracking

    All security-relevant events logged to database:

    // File: /lib/auth/audit.ts
    
    // Authentication events
    await audit.loginSuccess(userId, email);
    await audit.loginFailed(email, 'Invalid password');
    await audit.logout(userId);
    
    // 2FA events
    await audit.twoFAEnabled(userId);
    await audit.twoFAVerified(userId);
    await audit.twoFAFailed(userId, 'Invalid code');
    
    // Security events
    await audit.suspiciousActivity(userId, 'Multiple failed logins', 'high');
    await audit.rateLimitExceeded(identifier, '/api/auth/signin');
    await audit.permissionDenied(userId, 'settings', 'delete');
    
    // Admin events
    await audit.adminImpersonate(adminId, targetUserId, 'Support request');
    await audit.adminUserUpdated(adminId, targetUserId, { role: 'admin' });
    
    // Data events
    await audit.dataExport(userId, 'leads', 'csv', 1000);
    await audit.dataDelete(userId, 'lead', leadId, 'User request');
    

    Event Types

    export enum AuditEventType {
      // Auth
      LOGIN_SUCCESS = 'auth.login_success',
      LOGIN_FAILED = 'auth.login_failed',
      LOGOUT = 'auth.logout',
      SIGNUP = 'auth.signup',
    
      // 2FA
      TWO_FA_ENABLED = 'auth.2fa_enabled',
      TWO_FA_VERIFIED = 'auth.2fa_verified',
      TWO_FA_FAILED = 'auth.2fa_failed',
    
      // Security
      SUSPICIOUS_ACTIVITY = 'security.suspicious_activity',
      RATE_LIMIT_EXCEEDED = 'security.rate_limit_exceeded',
      PERMISSION_DENIED = 'security.permission_denied',
    
      // Admin
      ADMIN_IMPERSONATE = 'admin.impersonate',
      ADMIN_USER_UPDATED = 'admin.user_updated',
    
      // Data
      DATA_EXPORT = 'data.export',
      DATA_DELETE = 'data.delete'
    }
    

    Audit Log Schema

    CREATE TABLE "AuditLog" (
      "id" TEXT PRIMARY KEY,
      "userId" TEXT,
      "action" TEXT NOT NULL,
      "resource" TEXT NOT NULL,
      "resourceId" TEXT,
      "ipAddress" TEXT,
      "userAgent" TEXT,
      "success" BOOLEAN NOT NULL,
      "error" TEXT,
      "details" JSONB,
      "timestamp" TIMESTAMP NOT NULL DEFAULT NOW()
    );
    
    -- Indexes for efficient queries
    CREATE INDEX idx_audit_user ON "AuditLog"("userId", "timestamp");
    CREATE INDEX idx_audit_action ON "AuditLog"("action", "timestamp");
    CREATE INDEX idx_audit_timestamp ON "AuditLog"("timestamp");
    

    Querying Audit Logs

    // Get user's audit trail
    const logs = await prisma.auditLog.findMany({
      where: { userId },
      orderBy: { timestamp: 'desc' },
      take: 100
    });
    
    // Security events in last 24 hours
    const securityEvents = await prisma.auditLog.findMany({
      where: {
        resource: 'security',
        timestamp: { gte: new Date(Date.now() - 24 * 60 * 60 * 1000) }
      }
    });
    
    // Failed login attempts
    const failedLogins = await prisma.auditLog.findMany({
      where: {
        action: 'auth.login_failed',
        timestamp: { gte: new Date(Date.now() - 60 * 60 * 1000) }
      }
    });
    

    Best Practices

    1. Log everything security-relevant
    2. Include context (IP, user agent, metadata)
    3. Never log secrets (passwords, tokens, API keys)
    4. Set retention policy (1 year minimum for compliance)
    5. Monitor for anomalies (unusual patterns)
    6. Export for compliance (GDPR, SOC 2)

    Security Headers

    Content Security Policy (CSP)

    // File: /lib/security/edge-security-headers.ts
    
    const csp = {
      'default-src': ["'self'"],
      'script-src': ["'self'"],
      'style-src': ["'self'", "'unsafe-inline'"],
      'img-src': ["'self'", 'data:', 'https:'],
      'font-src': ["'self'"],
      'connect-src': ["'self'", 'https://app.petunia.gardenpatch.xyz/api'],
      'frame-ancestors': ["'none'"],
      'object-src': ["'none'"]
    };
    

    Complete Header Set

    Content-Security-Policy: [See above]
    Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
    X-Frame-Options: DENY
    X-Content-Type-Options: nosniff
    X-XSS-Protection: 1; mode=block
    Referrer-Policy: strict-origin-when-cross-origin
    Permissions-Policy: camera=(), microphone=(), geolocation=()
    

    Custom Headers

    X-Request-ID: req_12345_abc
    X-Processing-Time: 45ms
    X-RateLimit-Limit: 100
    X-RateLimit-Remaining: 73
    X-Petunia-User-Type: returning
    

    API Security

    Request Validation

    import { z } from 'zod';
    
    // Define schema
    const createLeadSchema = z.object({
      name: z.string().min(1).max(100),
      email: z.string().email(),
      phone: z.string().optional()
    });
    
    // Validate in API route
    export async function POST(request: Request) {
      const body = await request.json();
      const validation = createLeadSchema.safeParse(body);
    
      if (!validation.success) {
        return new Response(
          JSON.stringify({ error: validation.error }),
          { status: 400 }
        );
      }
    
      const data = validation.data;
      // ...
    }
    

    API Authentication

    // API routes require authentication
    import { requireAuth, requireRole } from '@/lib/auth/helpers';
    
    export async function GET() {
      const session = await requireAuth();
      // User is authenticated
    }
    
    export async function DELETE() {
      const session = await requireRole('admin');
      // User has admin role
    }
    

    API Key Authentication

    For external API access:

    // Validate API key
    const apiKey = request.headers.get('X-API-Key');
    if (!apiKey) {
      return new Response('API key required', { status: 401 });
    }
    
    const validKey = await prisma.apiKey.findUnique({
      where: { key: hashString(apiKey), active: true }
    });
    
    if (!validKey) {
      return new Response('Invalid API key', { status: 401 });
    }
    

    Webhook Security (HMAC)

    // Verify webhook signature
    import { createHmac } from 'crypto';
    
    function verifyWebhookSignature(
      payload: string,
      signature: string,
      secret: string
    ): boolean {
      const expectedSignature = createHmac('sha256', secret)
        .update(payload)
        .digest('hex');
    
      return constantTimeCompare(signature, expectedSignature);
    }
    
    // In webhook handler
    const signature = request.headers.get('X-Webhook-Signature');
    const rawBody = await request.text();
    
    if (!verifyWebhookSignature(rawBody, signature, WEBHOOK_SECRET)) {
      return new Response('Invalid signature', { status: 401 });
    }
    

    Security Review Checklist

    Pre-Deployment

    • All secrets in environment variables (not hardcoded)
    • Strong encryption keys generated (openssl rand -base64 32)
    • CSRF protection enabled for all state-changing endpoints
    • Rate limiting configured for all public endpoints
    • Security headers applied (CSP, HSTS, X-Frame-Options)
    • Database RLS policies enabled and tested
    • Audit logging enabled for all security events
    • MFA available for all users
    • Email verification required for new accounts
    • Session rotation enabled
    • Input validation on all API routes
    • SQL injection protection (Prisma ORM)
    • XSS protection (React escaping + CSP)
    • TLS 1.3 enforced in production

    Code Review

    • No secrets in code or logs
    • Authentication required for protected routes
    • Authorization checks for all data access
    • Tenant isolation verified (clientId filtering)
    • Sensitive data encrypted at rest
    • Passwords never logged or displayed
    • Error messages don't leak sensitive info
    • File uploads validated (type, size, content)
    • SQL queries use parameterized statements
    • User input sanitized before display

    API Routes

    • Authentication middleware applied
    • Rate limiting configured
    • Input validation with Zod schemas
    • CSRF token verified for mutations
    • Audit logging on sensitive operations
    • Proper HTTP status codes
    • Error handling without information leakage
    • Tenant isolation enforced

    Authentication & Authorization

    • Strong password requirements
    • Password reset flow secure (timed tokens)
    • Email verification required
    • MFA available and encouraged
    • Session expiration configured
    • Session rotation on privilege changes
    • Device tracking enabled
    • Failed login attempts logged
    • Account lockout after N failed attempts
    • Role-based access control enforced

    Data Protection

    • Sensitive data encrypted (API keys, tokens, MFA secrets)
    • Tenant-specific encryption keys used
    • Encryption key rotation plan in place
    • Database backups encrypted
    • TLS enforced for all connections
    • Secure cookie settings (httpOnly, secure, sameSite)
    • GDPR compliance (data export, deletion)

    Monitoring & Response

    • Audit logs enabled and retained (1 year minimum)
    • Security event monitoring configured
    • Rate limit violations logged
    • Failed authentication attempts tracked
    • Anomaly detection alerts
    • Incident response plan documented
    • Security contact information published

    Third-Party Integrations

    • OAuth secrets stored securely
    • Webhook signatures verified (HMAC)
    • API keys encrypted in database
    • Integration tokens use tenant-isolated keys
    • Rate limits respect provider limits
    • Error handling doesn't expose secrets

    Compliance

    • SOC 2 controls implemented
    • GDPR requirements met (consent, export, deletion)
    • HIPAA compliance (if handling health data)
    • PCI DSS (if handling payment data)
    • Audit trail for all data access
    • Privacy policy published
    • Terms of service published

    Additional Resources

    Internal Documentation

    • /docs/features/AUTHENTICATION.md - Authentication architecture
    • /docs/features/ENCRYPTION.md - Encryption implementation
    • /docs/RLS_POLICIES.md - Row-level security policies
    • /docs/TESTING.md - Security testing guidelines

    Security Contacts

    • Security Team: support@gardenpatch.xyz
    • Bug Bounty: [Link to program]
    • Security Incidents: support@gardenpatch.xyz

    External Standards

    • OWASP Top 10
    • NIST Cybersecurity Framework
    • CIS Controls

    Last Updated: 2025-12-13 Next Review: 2026-03-13 (Quarterly)

    On this page
    Table of ContentsOverviewSecurity PrinciplesSecurity ArchitectureLayers of DefenseKey Security FilesAuthenticationSupabase Auth IntegrationServer-Side Auth HelpersClient-Side AuthMulti-Factor Authentication (MFA)Device FingerprintingEmail VerificationAuthorization (RBAC)Role HierarchyPermissionsUsageDecorator PatternTenant IsolationEncryptionOverviewEncryption Key DerivationTenant-Isolated EncryptionUse CasesValidationKey RotationSecurity Best PracticesSecret ManagementEnvironment VariablesSecret Rotation ScheduleSecurity GuidelinesRate Limiting & DDoS Protection