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_KEYenv (32 bytes: 64 hex chars or 44 base64 chars). Generate:openssl rand -hex 32. - RLS: Row Level Security on
user_clockify_integrationsso 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
| Method | Purpose |
|---|---|
| GET | Returns { configured, workspaceId? } – never returns API key. |
| POST | Body: { apiKey, workspaceId }. Encrypts and upserts. |
| DELETE | Removes 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>:
- If
userId, queryuser_clockify_integrationsfor that user. - If row exists, decrypt
encrypted_api_key, return config. - 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 ofgetClockifyConfig().
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
| Decision | Rationale |
|---|---|
| AES-256-GCM for encryption | Standard, built-in Node crypto; no extra deps. |
| Provider token after refresh token | Supabase session provider_token can be stale; GOOGLE_REFRESH_TOKEN yields fresh token. Refresh path must run first. |
| Env fallback retained | Supports 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