ASAASA Standard
Not in Phase 1Production Foundation

Audit Log for Admin Actions

Admin Safety · ADM-04 · Priority: P1

Why It Matters

Without an audit log, you cannot answer the most basic security questions: Who deleted that user? When was the role changed? Who exported the data? When a breach or internal incident occurs, the absence of an audit trail turns investigation into guesswork.

AI code generators never create audit logs. They build admin panels with CRUD operations — create, read, update, delete — but record nothing about who did what, when, or why.

For any SaaS pursuing enterprise customers, SOC 2 Type II requires audit logging for privileged actions. Missing audit logs are an automatic compliance failure.

Priority: P1 — Required for incident response, compliance, and internal accountability.

Affected Stack: Supabase, Next.js, any framework with admin functionality


The Problem

// ❌ Admin action with no audit trail
export async function POST(req: Request) {
  const { userId } = await req.json();
  await supabase.from('users').delete().eq('id', userId);
  return new Response('Deleted'); // Who deleted? When? Why? No record.
}

The Fix

Create an append-only audit log table and write to it for every admin action.

-- ✅ Audit log table (append-only)
CREATE TABLE admin_audit_log (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  admin_id UUID NOT NULL REFERENCES auth.users(id),
  action TEXT NOT NULL,          -- 'user.delete', 'role.change', 'data.export'
  target_type TEXT,              -- 'user', 'subscription', 'organization'
  target_id TEXT,                -- ID of affected record
  details JSONB DEFAULT '{}',   -- Additional context
  ip_address TEXT,
  created_at TIMESTAMPTZ DEFAULT now()
);

-- Make it append-only: no UPDATE or DELETE allowed
ALTER TABLE admin_audit_log ENABLE ROW LEVEL SECURITY;
CREATE POLICY "No modifications" ON admin_audit_log
  FOR ALL USING (false);
CREATE POLICY "Insert only for service role" ON admin_audit_log
  FOR INSERT WITH CHECK (true);
// ✅ Admin action with audit logging
export async function POST(req: Request) {
  const admin = await getVerifiedAdmin(req);
  const { userId } = await req.json();

  // Perform the action
  await supabase.from('users').delete().eq('id', userId);

  // Log it (append-only, cannot be tampered with)
  await supabaseAdmin.from('admin_audit_log').insert({
    admin_id: admin.id,
    action: 'user.delete',
    target_type: 'user',
    target_id: userId,
    ip_address: req.headers.get('x-forwarded-for'),
  });

  return new Response('Deleted');
}

What to log:

  • User deletion, suspension, role changes
  • Data exports and bulk operations
  • Billing overrides and manual adjustments
  • Configuration changes
  • Impersonation sessions (start + end)
  • Failed admin auth attempts

References


Related Checks


Is your app safe? Run Free Scan →