{"slug":"connectors/AUTH_MIGRATION_DESIGN","title":"Auth Migration Design: Supabase Auth → Cloudflare Zero Trust","section":"connectors","sectionLabel":"Connectors","status":"unclassified","type":"document","path":"connectors/AUTH_MIGRATION_DESIGN.mdx","bytes":15070,"description":null,"updated":null,"source":"integratewise/Integratewise-docs-wiki","content":"\n**Status:** COMPLETE — Cloudflare Access (Google + GitHub) + Gateway D1 JWT is live. Zero Supabase auth.  \n**Authority:** Nirmal, Founder  \n**Date:** June 3, 2026 (migrated; updated 2026-06-09)\n\n> **Outcome:** All authentication now flows through Cloudflare Access SSO and the\n> Gateway-issued D1 JWT (RS256). `auth-provider.tsx` was rewritten with zero\n> Supabase auth imports. Supabase remains ONLY the fortress DATA record of truth\n> (RLS, durability) — it is never an auth path. The inventory below is retained\n> as the historical migration record.\n\n---\n\n## Current State (historical — pre-migration)\n\nEvery authenticated request in IntegrateWise currently passes through Supabase Auth in some form. The migration target is Cloudflare Zero Trust (Access) for all authentication, with the Gateway remaining the single point of tenant resolution and authorization.\n\n### Auth Touchpoint Inventory\n\n| Layer             | File                                                  | What It Does                                                                                            | Migration Action                                      |\n| ----------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |\n| **Frontend**      | `apps/web/src/utils/spine/client.ts`                  | Creates Supabase client with PKCE auth flow, `autoRefreshToken`, `persistSession`, `detectSessionInUrl` | Replace with CF Access JWT auth                       |\n| **Frontend**      | `apps/web/src/utils/supabase/client.ts`               | Supabase client creation                                                                                | Remove or repurpose                                   |\n| **Frontend**      | `apps/web/src/utils/supabase/info.tsx`                | Supabase project ID + anon key                                                                          | Remove                                                |\n| **Frontend**      | `apps/web/src/components/core/auth/auth-provider.tsx` | Auth provider wrapping app                                                                              | Rewrite for CF Access                                 |\n| **Frontend**      | `apps/web/src/hooks/use-action-realtime.ts`           | Realtime channel with Supabase                                                                          | Keep (database, not auth)                             |\n| **Gateway**       | `services/gateway/src/index.ts:66-67`                 | `SUPABASE_URL` + `SUPABASE_SERVICE_ROLE_KEY` bindings                                                   | Remove after migration                                |\n| **Gateway**       | `services/gateway/src/index.ts:1580`                  | `verifySupabaseDevToken()` call — dev fallback auth                                                     | Remove entirely                                       |\n| **Gateway**       | `services/gateway/src/index.ts:1959`                  | `verifySupabaseDevToken()` function definition                                                          | Remove entirely                                       |\n| **Gateway**       | `services/gateway/src/index.ts:1964`                  | `fetch(\\`${SUPABASE_URL}/auth/v1/user\\`)` — token verification                                          | Replace with CF Access JWT verification               |\n| **Gateway**       | `services/gateway/src/index.ts:1985-2014`             | Profile resolution via Supabase REST API                                                                | Move to CF Workers KV/D1                              |\n| **MCP Connector** | `services/mcp-connector/src/handlers/tools.ts:31-32`  | `SUPABASE_URL` + `SUPABASE_SERVICE_ROLE_KEY` bindings                                                   | Keep — this is DATA access, not auth                  |\n| **MCP Connector** | `services/mcp-connector/src/lib/jwt-verify.ts`        | Supabase JWT verification (243 lines)                                                                   | Remove — replace with CF Access JWT                   |\n| **MCP Connector** | `services/mcp-connector/src/lib/access-auth.ts`       | Cloudflare Access OAuth (223 lines)                                                                     | Keep — this IS the target auth                        |\n| **Workflow**      | `services/workflow/src/index.ts` (15+ locations)      | `createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY)`                                                 | Keep — database access, not auth                      |\n| **Pipeline**      | `services/pipeline/src/index.ts`                      | `createClient` from Supabase                                                                            | Keep — database access, only service with write creds |\n| **Spine-v2**      | `services/spine-v2/src/index.ts`                      | `createClient` from Supabase                                                                            | Keep — database access                                |\n| **Normalizer**    | `services/normalizer/src/index.ts`                    | Dynamic `createClient` import                                                                           | Keep — database access                                |\n| **Loader**        | `services/loader/src/index.ts`                        | Dynamic `createClient` import                                                                           | Keep — database access                                |\n| **Connector**     | `services/connector/src/token-refresh.ts`             | `createClient` for token refresh                                                                        | Keep — database access                                |\n| **Connector**     | `services/connector/src/routes/internal.ts`           | Dynamic `createClient` import                                                                           | Keep — database access                                |\n| **Packages**      | `packages/supabase/src/server.ts`                     | Server-side Supabase client                                                                             | Remove or repurpose                                   |\n| **Packages**      | `packages/supabase/src/client.ts`                     | Client-side Supabase client                                                                             | Remove or repurpose                                   |\n| **Packages**      | `packages/supabase/src/hooks/index.ts`                | Auth hooks (useAuth, useSession)                                                                        | Rewrite for CF Access                                 |\n| **Packages**      | `packages/rbac/src/engine.ts`                         | `createClient` for RBAC queries                                                                         | Keep — database access                                |\n| **Packages**      | `packages/db/src/supabase.ts`                         | Database client                                                                                         | Keep — database access                                |\n\n### What Is Auth vs. What Is Data Access\n\nThe critical distinction:\n\n- **AUTH** (must migrate): JWT verification, session management, login/logout, token refresh, profile resolution via auth user\n- **DATA ACCESS** (keep as-is): Supabase REST queries with service role key, `createClient` for database operations, realtime subscriptions\n\nMost Supabase usage in this codebase is **data access**, not auth. The actual auth surface is small:\n\n1. **Frontend PKCE login flow** — 1 file\n2. **Gateway `verifySupabaseDevToken()`** — 1 function + 1 call site\n3. **Gateway profile resolution** — 1 function (fetches profile by auth user email/id)\n4. **MCP Connector JWT verification** — 1 file (jwt-verify.ts, 243 lines)\n5. **Frontend auth provider** — 1 component\n6. **Supabase auth hooks** — 1 package\n\n---\n\n## Target State\n\n```\nExternal Request\n    │\n    ▼\nCloudflare Access (auth.integratewise.ai)\n    │  Verifies caller identity\n    │  Issues JWT with aud: https://mcp.integratewise.ai\n    ▼\nGateway (services/gateway)\n    │  Validates JWT (issuer, audience, expiry)\n    │  Resolves tenant from token claims or KV\n    │  Enforces authorization\n    ▼\nInternal Services (via Service Bindings)\n    │  No auth needed — private network\n    ▼\nSupabase (data access only)\n    │  Pipeline has write credentials\n    │  Other services read via Pipeline or Service Bindings\n```\n\n### Auth Flow: External MCP Client\n\n```\n1. Client hits mcp.integratewise.ai\n2. Server returns 401 + WWW-Authenticate: Bearer resource_metadata=...\n3. Client fetches /.well-known/oauth-protected-resource\n4. Client redirected to auth.integratewise.ai for OAuth\n5. Client receives JWT (aud: mcp.integratewise.ai, sub: client_id, scopes: [...])\n6. Client calls mcp.integratewise.ai with Authorization: Bearer <token>\n7. Gateway validates JWT, resolves tenant, serves MCP tools\n```\n\n### Auth Flow: Frontend (apps/web)\n\n```\n1. User opens app.integratewise.ai\n2. Cloudflare Access intercepts, handles login\n3. CF Access issues JWT\n4. Frontend receives JWT (via CF Access cookie or header)\n5. Frontend sends JWT to Gateway with every API call\n6. Gateway validates JWT, resolves tenant\n```\n\n### Auth Flow: Internal Worker-to-Worker\n\n```\n1. Worker A calls Worker B via Service Binding\n2. No JWT needed — private Cloudflare runtime\n3. Tenant context passed via binding parameters\n4. No public ingress traversed\n```\n\n---\n\n## Implementation Phases\n\n### Phase 1: Gateway OAuth Authorization Server (DONE)\n\nThe gateway already has OAuth authorization server routes at `auth.integratewise.ai`:\n\n- `services/gateway/src/index.ts` — OAuth metadata, authorize, token endpoints\n- `services/gateway/src/lib/jwt.ts` — JWT signing/verification\n- `services/gateway/wrangler.toml` — Routes for `auth.integratewise.ai` and `mcp.integratewise.ai`\n\nStatus: **Implemented, needs testing and deployment.**\n\n### Phase 2: Remove `verifySupabaseDevToken()` from Gateway\n\nThis is the simplest migration step:\n\n1. Remove `verifySupabaseDevToken()` function definition (line 1959)\n2. Remove the call site (line 1580)\n3. Make CF Access JWT the ONLY auth path in production\n4. Keep dev auth as a separate mechanism (e.g., CF Access service tokens for local dev)\n\n**Files to change:**\n\n- `services/gateway/src/index.ts` — Remove function + call site\n\n### Phase 3: Replace MCP Connector JWT Verification\n\nReplace `jwt-verify.ts` (Supabase JWT verification) with CF Access JWT verification:\n\n1. Remove `services/mcp-connector/src/lib/jwt-verify.ts` (243 lines)\n2. Update `services/mcp-connector/src/index.ts` to use CF Access JWT validation\n3. The existing `access-auth.ts` (Cloudflare Access OAuth, 223 lines) already implements the target pattern\n\n**Files to change:**\n\n- `services/mcp-connector/src/lib/jwt-verify.ts` — Delete\n- `services/mcp-connector/src/index.ts` — Update auth middleware\n\n### Phase 4: Move Profile Resolution Off Supabase Auth\n\nCurrently the gateway resolves profiles by calling Supabase REST API with the auth user's email/id. Move this to CF Workers KV or D1:\n\n1. Pipeline writes profile data to KV/D1 when users are created/updated\n2. Gateway reads profile from KV/D1 instead of Supabase REST\n3. Remove `SUPABASE_URL` + `SUPABASE_SERVICE_ROLE_KEY` from Gateway bindings\n\n**Files to change:**\n\n- `services/gateway/src/index.ts` — Replace `fetchProfileById()` and `fetchProfileByEmail()` with KV/D1 reads\n- `services/gateway/wrangler.toml` — Add KV/D1 binding for profiles\n- `services/pipeline/src/index.ts` — Add profile write to KV/D1 on user creation/update\n\n### Phase 5: Rewrite Frontend Auth\n\nReplace Supabase PKCE auth with CF Access:\n\n1. Remove `apps/web/src/utils/spine/client.ts` — Supabase PKCE client\n2. Remove `apps/web/src/utils/supabase/client.ts` and `info.tsx`\n3. Rewrite `apps/web/src/components/core/auth/auth-provider.tsx` for CF Access\n4. Rewrite `packages/supabase/src/hooks/index.ts` auth hooks\n5. Frontend reads JWT from CF Access cookie/header\n6. Frontend sends JWT to Gateway with every API call\n\n**Files to change:**\n\n- `apps/web/src/utils/spine/client.ts` — Remove Supabase client creation\n- `apps/web/src/utils/supabase/` — Remove directory\n- `apps/web/src/components/core/auth/auth-provider.tsx` — Rewrite\n- `packages/supabase/src/hooks/index.ts` — Rewrite auth hooks\n\n### Phase 6: Cleanup\n\n1. Remove `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` from Gateway `Env` interface\n2. Remove `packages/supabase/` package or repurpose for database-only usage\n3. Update `apps/web/.env.example` to remove Supabase auth vars\n4. Update `AGENTS.md` auth architecture section\n5. Verify all services still work with Gateway as sole auth entry point\n\n---\n\n## Risk Assessment\n\n| Risk                                                      | Mitigation                                                                                           |\n| --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |\n| Breaking login for existing users                         | Keep Supabase auth running in parallel during migration; switchover only after CF Access is verified |\n| CF Access not supporting all MCP client auth patterns     | Test with Cursor, Claude Desktop, and custom agents before cutover                                   |\n| Profile resolution latency increase (KV vs Supabase REST) | KV reads are sub-millisecond; this is actually faster                                                |\n| Dev workflow disruption                                   | Keep dev auth separate; do not require CF Access for local development                               |\n| Token refresh for long-lived sessions                     | CF Access handles session management; no custom refresh needed                                       |\n\n---\n\n## Canonical Wording\n\n> IntegrateWise authenticates all external traffic through Cloudflare Zero Trust (Access). The Gateway validates JWTs, resolves tenant context from token claims, and enforces authorization. Internal Worker-to-Worker communication uses Service Bindings with no auth overhead. Profile resolution moves from Supabase Auth to Cloudflare Workers KV, populated by the Pipeline on user creation and update. Supabase remains the data fortress — only the Pipeline holds write credentials; all other services access data through the Gateway or Service Bindings.\n\n---\n\n_Document: Auth Migration Design_  \n_Author: Nirmal, Founder_  \n_Status: Design complete, ready for implementation._  \n_Date: June 3, 2026_\n"}