Skip to main content

Overview

SecurAtlas uses Supabase Edge Functions (Deno runtime) for operations that require external API calls, long-running processing, or elevated permissions. All functions live in supabase/functions/.

Function Catalog

sync-azure-ad

Syncs users, groups, MFA status, and conditional access policies from Microsoft Entra ID.

sync-google-workspace

Syncs users, 2SV status, admin roles, and device policies from Google Workspace.

sync-aws

Syncs IAM users, roles, MFA devices, and security configurations from AWS.

trigger-sync

Dispatcher that routes sync requests to the appropriate provider function.

process-integration-jobs

Processes queued integration sync jobs from integration_sync_jobs.

evidence_classify

Uses Anthropic Claude to classify uploaded evidence and suggest control mappings.

evidence_signed_upload

Generates presigned URLs for direct-to-storage evidence uploads.

evidence_finalize_upload

Finalizes an evidence upload: updates metadata, triggers classification.

send-invite

Sends invitation emails for tenant and partner membership invites.

Deployment

Deploy all functions:
supabase functions deploy
Deploy a specific function:
supabase functions deploy sync-azure-ad
Always deploy Edge Functions via the Supabase CLI, not through the Supabase Dashboard editor. The CLI ensures correct bundling of shared modules and dependencies.

Configuration

verify_jwt Settings

JWT verification is configured per function in supabase/config.toml:
[functions.trigger-sync]
verify_jwt = true

[functions.evidence_classify]
verify_jwt = true

[functions.send-invite]
verify_jwt = false  # Called from webhooks/cron
Functions called by pg_cron via pg_net or by webhook handlers typically set verify_jwt = false because there is no user session. These functions must validate requests through other means (e.g., checking a shared secret header).

Environment Secrets

Edge Functions access secrets via Deno.env.get():
const supabaseUrl = Deno.env.get('SUPABASE_URL')!;
const serviceRoleKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!;
const anthropicKey = Deno.env.get('ANTHROPIC_API_KEY')!;
Set secrets via CLI:
supabase secrets set ANTHROPIC_API_KEY=sk-ant-...
supabase secrets set INTEGRATION_STATE_SECRET=your-secret

Function Details

trigger-sync

The central dispatcher for all integration syncs:
// Simplified trigger-sync logic
const { connection_id } = await req.json();

// Look up connection and provider
const { data: connection } = await adminClient
  .from('integration_connections')
  .select('*, integration_providers(*)')
  .eq('id', connection_id)
  .single();

// Retrieve decrypted tokens from Vault
const { data: tokens } = await adminClient
  .rpc('rpc_integration_get_secret', { p_connection_id: connection_id });

// Dispatch to provider-specific function
const providerFn = `sync-${connection.integration_providers.slug}`;
await adminClient.functions.invoke(providerFn, {
  body: { connection_id, tokens },
});

evidence_classify

Sends evidence content to Anthropic Claude for classification:
  1. Reads the evidence file from Supabase Storage
  2. Sends content to Claude with a system prompt describing available controls
  3. Parses the response for control mappings and tags
  4. Updates tenant_evidence_items with classification results
  5. Creates suggested control_evidence_links

Sync Functions (azure-ad, google-workspace, aws)

All sync functions follow a common pattern:
1

Authenticate

Use stored OAuth tokens (refreshing if expired) to authenticate with the provider API.
2

Fetch entities

Pull users, groups, policies, and configurations from the provider.
3

Upsert entities

Store raw data in integration_entities with upsert to avoid duplicates.
4

Detect findings

Analyze entities for security issues (no MFA, stale accounts, weak policies).
5

Update job status

Mark the integration_sync_jobs record as completed or failed.

Shared Modules

Common utilities are shared across functions via the supabase/functions/_shared/ directory:
supabase/functions/
├── _shared/
│   ├── supabase-client.ts    # Admin client constructor
│   ├── cors.ts               # CORS headers
│   └── utils.ts              # Common utilities
├── sync-azure-ad/
│   └── index.ts
├── sync-google-workspace/
│   └── index.ts
└── trigger-sync/
    └── index.ts
Never redeclare supabaseUrl in sync functions. Import it from _shared/supabase-client.ts. Redeclaring has caused 503 errors in production.