Jak jsem vytvořil Supabase driver pro Keboola Connection

dev

Poslední týdny jsem pracoval na integraci Supabase jako externího datového zdroje do platformy Keboola Connection. Vznikl kompletní driver s OAuth 2.0 autentizací, automatickým discovery schémat a napojením přes Supabase Marketplace. Cestou jsem narazil na pár opravdu zákeřných bugů -- tady je celý příběh.

Supabase + Keboola

Supabase je open-source alternativa k Firebase postavená nad PostgreSQL. Nabízí autentizaci, real-time subscriptions, storage a především plnohodnotnou PostgreSQL databázi. Keboola je platforma pro datové pipeline, která umožňuje ETL procesy nad různými datovými zdroji.

Cílem bylo umožnit uživatelům připojit svůj Supabase projekt jako externí datový zdroj do Keboola a automaticky z něj číst data pro další zpracování v pipeline.

Pivot: Z CLI na webový controller

Původní plán byl CLI příkaz pro manuální registraci OAuth credentials. Jenže testovat kompletní OAuth flow z terminálu je utrpení -- kopírujete URL tam a zpátky, ručně řešíte přesměrování a nemáte žádnou vizuální zpětnou vazbu.

Takže jsem brzy změnil směr: místo CLI nástroje jsem vytvořil webový test harness na /supabase/connect. Jednoduchý formulář, kde zadáte client_id a client_secret, vygeneruje redirect URL z aktuálního hosta, přesměruje vás na Supabase k autorizaci a obslouží callback. Tohle rozhodnutí se ukázalo jako klíčové -- iterativní debugging, který následoval, by z terminálu nebyl možný.

Stateless OAuth hlavolam

První skutečná výzva přišla z architekury Symfony. Routy označené #[AsPublicAction] běží na stateless firewallu -- žádná session k dispozici. Jenže OAuth flow typicky potřebuje uložit PKCE verifier mezi autorizačním požadavkem a callbackem. Symfony vyhodilo: "Session was used while the request was declared stateless."

Řešení bylo zakódovat všechno -- client credentials, PKCE verifier, redirect URI -- do OAuth state parametru samotného, podepsaného HMAC-SHA256 pomocí kernel.secret. Callback stav dekóduje, ověří podpis a extrahuje PKCE verifier. Kompletně stateless, kompletně bezpečný.

Tento pattern se ukázal jako elegantní: žádná závislost na session, žádné ukládání do databáze, HMAC brání manipulaci a state parametr je z principu validován proti CSRF útokům.

Záludnosti OAuth API

Dvě menší překážky přišly vzápětí:

Záhada approval_prompt. League OAuth2 knihovna přidává approval_prompt do autorizačních požadavků automaticky (konvence Google OAuth). Supabase to odmítlo: "Unrecognized key(s) in object: 'approval_prompt'." Fix: override getAuthorizationParameters() a parametr odfiltrovat.

Projektové vs. účtové OAuth aplikace. Zpočátku jsem vytvořil OAuth aplikaci na úrovni projektu (/project/{ref}/auth/oauth-apps), což vracelo "Unrecognized client_id." Supabase má dva odlišné OAuth scopy -- projektové aplikace a integrační aplikace na úrovni účtu (/account/integrations). Pro marketplace integraci s přístupem napříč projekty je potřeba to druhé.

PKCE Double-Handling Bug

Tohle byl nejtěžší bug celého projektu. Po vyřešení stateless flow a API záludností stále selhávala výměna kódu za tokeny: "Invalid or expired OAuth authorization."

Zkoušel jsem systematicky:

  1. Přidal detailní error logging -- stejná chyba
  2. Přepnul na HTTP Basic Auth pro token endpoint -- stále selhávalo
  3. Přidal Accept: application/json header -- stále selhávalo
  4. Napsal kompletně vlastní exchangeCodeForTokens() metodu obcházející knihovnu -- stále selhávalo

Autorizační flow fungovalo. Callback přijal kód. Ale výměna za tokeny konzistentně vracela tu samou kryptickou chybu.

Průlom: League OAuth2 knihovna má vestavěnou podporu PKCE. Můj kód PKCE taky řešil manuálně kvůli stateless flow. Dvě korektní PKCE implementace běžící současně -- knihovna poslala jeden code_verifier, můj stateless kód poslal jiný. Supabase vidělo nesoulad a výměnu odmítlo.

Fix byl jeden řádek:

protected $pkceMethod = null; // Vypnout vestavěné PKCE knihovny

Klasický integrační bug -- dva nezávisle správné systémy v konfliktu při kombinaci. Debugging zabral hodiny, protože každý jednotlivý dílek vypadal správně.

Dva režimy připojení

Driver podporuje dva způsoby, jak se k datům v Supabase dostat:

Přímé PostgreSQL připojení -- klasické databázové připojení přes connection pooler (port 6543). Plný přístup k SQL dotazům, vhodné pro větší objemy dat.

REST API (PostgREST) -- připojení přes Supabase REST endpoint s service_role klíčem. Jednodušší nastavení bez nutnosti sdílet databázové heslo.

Obě varianty podporují šifrované ukládání credentials s oddělenými šifrovacími klíči pro databázové hesla, API klíče a OAuth tokeny.

Automatické schema discovery

Po úspěšném OAuth propojení se na pozadí spustí SupabaseProjectSetupJob, který:

  1. Přes protobuf rozhraní zavolá ListSchemasCommand na Supabase driver
  2. Objeví dostupná schémata v databázi
  3. Pro každé schéma vytvoří externí bucket v Keboola
  4. Nastaví automatický refresh každých 6 hodin

Protobuf komunikace je architektonicky zajímavá -- příkazy se definují ve sdíleném storage-driver-common monorepu a implementují v storage-driver-postgres driveru. Čisté hranice mezi komponentami, žádné přímé provázání.

Uživatel tak po připojení okamžitě vidí svá data v Keboola bez nutnosti ruční konfigurace.

Supabase Management API

Součástí je klient pro Supabase Management API, který umožňuje:

  • Získat detaily projektu (region, konfigurace)
  • Stáhnout API klíče (anon, service_role)
  • Přečíst konfiguraci connection pooleru
  • Vylistovat projekty a organizace

Tyto informace se využívají při automatickém nastavení credentials po OAuth flow.

Celý uživatelský flow

End-to-end zážitek poté, co všechny díly do sebe zapadly:

  1. Uživatel klikne na "Připojit Supabase" a autorizuje přes OAuth
  2. Callback uloží tokeny, přesměruje na setup stránku
  3. Uživatel vybere svůj Supabase projekt a volitelně zadá PostgreSQL DSN
  4. Keboola vytvoří organizaci (s názvem Supabase organizace) a projekt se Supabase backendem
  5. Background job objeví tabulky, zaregistruje buckety, nastaví auto-refresh
  6. Uživatel přistane na dashboardu projektu s daty, která už tečou

Rozsah projektu

Celý driver vznikl za jeden týden v přibližně 60 hodinách intenzivní práce s Claude Code. Výsledkem je kolem 25 000 změn -- nové třídy, controllery, migrace, testy, CLI příkazy a dokumentace. Od nuly ke kompletní integraci.

Vývoj probíhal vysoce iterativně: definovat co je potřeba, implementovat, okamžitě otestovat v prohlížeči, debugovat z reálných chybových hlášek. Rychlé feedback smyčky umožnily vyřešit bugy jako PKCE double-handling problém v řádu hodin místo dnů.

AI agent výrazně zrychlil implementaci opakujících se vzorů (controllery, DTO, testy), generování boilerplate kódu a navigaci v rozsáhlém codebase Keboola Connection. Klíčová architektonická rozhodnutí a bezpečnostní model ale samozřejmě vyžadovaly lidský úsudek.

Co jsem se naučil

  • Stateless OAuth je možný s pečlivým designem state parametru -- zakódovat vše do HMAC-podepsaného stavu, žádná session není potřeba
  • Knihovní abstrakce mohou kolidovat s manuální implementací -- PKCE double-handling bug mě naučil vždy zkontrolovat, co knihovna dělá automaticky
  • OAuth API se mezi providery výrazně liší -- approval_prompt, auth metody, scopy aplikací se liší provider od providera
  • PKCE je nutnost pro OAuth flow v kontextu, kde nelze plně zabezpečit client secret (marketplace integrace, CLI nástroje)
  • Oddělená šifrovací schémata pro různé typy credentials výrazně zlepšují bezpečnostní model
  • Webový test harness poráží CLI testování OAuth flow o řád
  • AI agenti dramaticky mění rychlost vývoje -- 25 000 změn za týden by bez nich bylo nemyslitelné

Integrace je momentálně ve stavu pull requestu a prochází code review. Těším se, až ji uvidím v produkci.

Předchozí příspěvek Následující příspěvek

Related Posts