Clockify Per-User Integration Plan

Status: Draft – plan for team review Created: 2026-03-03


Context

The Calendar-Clockify sync tool (/ai-tools/calendar-clockify) is configured via env vars (CLOCKIFY_API_KEY, CLOCKIFY_WORKSPACE_ID) only. Multiple users sharing the platform need individual Clockify accounts. This plan proposes per-user API keys stored in Supabase (encrypted), with env vars as fallback.


Phase 1: Per-User Storage (Plan)

Security design

  • Encryption at rest: API keys encrypted with AES-256-GCM before storage. Key from ENCRYPTION_KEY env (32 bytes: 64 hex chars or 44 base64 chars). Generate: openssl rand -hex 32.
  • RLS: Row Level Security on user_clockify_integrations so users can only read/write their own row (auth.uid() = user_id).
  • No client exposure: API key never returned to the client; decrypted only server-side when making Clockify API calls.

1. Migration

File: apps/platform/supabase/migrations/006_user_clockify_integrations.sql

CREATE TABLE user_clockify_integrations (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
  encrypted_api_key TEXT NOT NULL,
  workspace_id TEXT NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  UNIQUE(user_id)
);
CREATE INDEX idx_user_clockify_integrations_user_id ON user_clockify_integrations(user_id);
 
-- RLS: users can only access their own row
ALTER TABLE user_clockify_integrations ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can view own" ON user_clockify_integrations FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "Users can insert own" ON user_clockify_integrations FOR INSERT WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Users can update own" ON user_clockify_integrations FOR UPDATE USING (auth.uid() = user_id);
CREATE POLICY "Users can delete own" ON user_clockify_integrations FOR DELETE USING (auth.uid() = user_id);
 
-- Trigger for updated_at (assumes update_updated_at_column exists from migration 001)
CREATE TRIGGER update_user_clockify_integrations_updated_at
  BEFORE UPDATE ON user_clockify_integrations
  FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

Apply to Internal AI Core Supabase project.

2. Encryption util

File: apps/platform/src/lib/encryption.ts

  • encrypt(plaintext: string): string – AES-256-GCM, returns base64 (iv:authTag:ciphertext).
  • decrypt(encoded: string): string – reverse.
  • Key from ENCRYPTION_KEY; throw clear error if missing or invalid format.

3. Integration API

File: apps/platform/src/app/api/brainforge/clockify/integration/route.ts

MethodPurpose
GETReturns { configured, workspaceId? } – never returns API key.
POSTBody: { apiKey, workspaceId }. Encrypts and upserts.
DELETERemoves user’s integration.

All require authenticated user (Supabase session).

4. Config helper

File: apps/platform/src/app/api/brainforge/clockify/_shared.ts

Add getClockifyConfigForUser(userId: string | null): Promise<ClockifyConfig>:

  1. If userId, query user_clockify_integrations for that user.
  2. If row exists, decrypt encrypted_api_key, return config.
  3. Else fall back to getClockifyConfig() (env vars).

5. Clockify route updates

Files: projects/route.ts, user/route.ts, time-entries/route.ts

  • Get user from Supabase session.
  • Call getClockifyConfigForUser(user?.id ?? null) instead of getClockifyConfig().

6. UI – Connect form

File: apps/platform/src/components/CalendarClockifySync.tsx

  • On mount, fetch GET /api/brainforge/clockify/integration.
  • If configured: false, show form: API Key (password), Workspace ID, Save button.
  • Link to Clockify → User preferences → API.
  • On save, POST to integration API, then reload projects/user/week data.

Decisions

DecisionRationale
AES-256-GCM for encryptionStandard, built-in Node crypto; no extra deps.
Provider token after refresh tokenSupabase session provider_token can be stale; GOOGLE_REFRESH_TOKEN yields fresh token. Refresh path must run first.
Env fallback retainedSupports demo users, shared admin, and gradual rollout.

References

  • Clockify API
  • Playbook setup doc to add on implementation: standards/03-knowledge/engineering/setup/clockify-per-user-setup.md