38980-vm/app-9w9pd00g5j41/CLERK_JWT_FIX.md
2026-03-04 18:25:09 +00:00

6.9 KiB
Raw Permalink Blame History

Clerk JWT Authentication Fix - Supabase RLS Uyumluluğu

🔴 Problem

Clerk'in generic JWT'si Supabase tarafından authenticated role olarak tanınmıyor. Supabase, sadece kendi JWT secret'ı ile imzalanmış token'ları authenticated sayar. Clerk'in generic token'ı farklı bir key ile imzalı olduğu için, tüm RLS politikaları (profiles INSERT/UPDATE dahil) çalışmıyor.

Sorunun Nedeni

  1. Clerk Token: Clerk kendi secret key'i ile JWT imzalar
  2. Supabase Beklentisi: Supabase kendi JWT secret'ı ile imzalanmış token bekler
  3. Sonuç: Clerk token'ı anon role olarak kabul edilir, authenticated role'üne bağlı politikalar çalışmaz

Çözüm

1. Kısa Vadeli Çözüm (Uygulandı)

RLS politikalarını hem anon hem authenticated role'leri için çalışacak şekilde güncelledik:

Migration 00093: Profiles INSERT Policy

CREATE POLICY "Allow profile creation with clerk_user_id"
  ON profiles FOR INSERT
  TO public
  WITH CHECK (
    clerk_user_id IS NOT NULL
    AND clerk_user_id <> ''
    AND email IS NOT NULL
  );

Güvenlik Kontrolleri:

  • clerk_user_id boş olamaz
  • email zorunlu
  • Rastgele insert önlenir
  • Hem anon hem authenticated çalışır

Migration 00094: Profiles UPDATE Policy

CREATE POLICY "Allow profile update with email match"
  ON profiles FOR UPDATE
  TO public
  USING (
    email IS NOT NULL
    AND (clerk_user_id IS NULL OR clerk_user_id = '')
  )
  WITH CHECK (
    clerk_user_id IS NOT NULL
    AND clerk_user_id <> ''
  );

Güvenlik Kontrolleri:

  • Sadece henüz bağlanmamış profiller güncellenebilir
  • Email zorunlu
  • Güncelleme sonrası clerk_user_id dolu olmalı
  • Email ile profil eşleştirme güvenli

2. Uzun Vadeli Çözüm (Önerilen)

Clerk Dashboard'da Supabase JWT Template oluşturun:

Adım 1: Clerk Dashboard'a Gidin

  1. Clerk Dashboard → Projenizi seçin
  2. JWT TemplatesNew Template

Adım 2: Supabase Template Oluşturun

{
  "name": "supabase",
  "claims": {
    "aud": "authenticated",
    "exp": "{{user.created_at + 3600}}",
    "sub": "{{user.id}}",
    "email": "{{user.primary_email_address}}",
    "app_metadata": {
      "provider": "clerk"
    },
    "user_metadata": {
      "full_name": "{{user.first_name}} {{user.last_name}}",
      "avatar_url": "{{user.image_url}}"
    }
  },
  "lifetime": 3600,
  "signing_key": "YOUR_SUPABASE_JWT_SECRET"
}

Adım 3: Supabase JWT Secret'ı Alın

  1. Supabase Dashboard → Projenizi seçin
  2. SettingsAPIJWT Settings
  3. JWT Secret değerini kopyalayın

Adım 4: Template'i Kaydedin

  • Template adı: supabase
  • Signing key: Supabase JWT Secret
  • Lifetime: 3600 (1 saat)

Adım 5: Kod Güncellemesi (Zaten Mevcut)

useAuth.ts dosyasında zaten template desteği var:

const token = await getToken({ template: 'supabase' });

🔍 Nasıl Çalışır?

Şu Anki Durum (Kısa Vadeli Çözüm)

User Login → Clerk Generic JWT → Supabase (anon role)
                                    ↓
                        RLS Policy (public role)
                                    ↓
                        ✅ Profile Created/Updated

JWT Template Sonrası (Uzun Vadeli Çözüm)

User Login → Clerk Supabase JWT → Supabase (authenticated role)
                                      ↓
                          RLS Policy (authenticated role)
                                      ↓
                          ✅ Profile Created/Updated

🛡️ Güvenlik

Kısa Vadeli Çözüm Güvenliği

  • clerk_user_id zorunlu: Rastgele insert önlenir
  • email zorunlu: Kimlik doğrulama gerekli
  • UPDATE kısıtlaması: Sadece bağlanmamış profiller güncellenebilir
  • WITH CHECK: Güncelleme sonrası clerk_user_id dolu olmalı

Uzun Vadeli Çözüm Güvenliği

  • Supabase JWT Secret: Token Supabase tarafından doğrulanır
  • authenticated role: Tüm RLS politikaları normal çalışır
  • Token expiration: 1 saatlik geçerlilik süresi
  • Clerk verification: Token Clerk tarafından imzalanır

📊 Test Senaryoları

Test 1: Yeni Kullanıcı Kaydı

// 1. Clerk'e kayıt ol
const { user } = await clerk.signUp({ email, password });

// 2. useAuth hook otomatik profil oluşturur
// ✅ INSERT policy çalışır (anon role)
// ✅ clerk_user_id ve email dolu
// ✅ Profile başarıyla oluşturulur

Test 2: Mevcut Profil Bağlama

// 1. Email ile profil var (clerk_user_id boş)
// 2. Clerk'e giriş yap
const { user } = await clerk.signIn({ email, password });

// 3. useAuth hook profili bulur ve bağlar
// ✅ UPDATE policy çalışır (anon role)
// ✅ clerk_user_id güncellenir
// ✅ Profile başarıyla bağlanır

Test 3: JWT Template ile Giriş

// 1. JWT Template kurulu
// 2. Clerk'e giriş yap
const token = await getToken({ template: 'supabase' });

// 3. Token authenticated role ile gelir
// ✅ Tüm RLS politikaları normal çalışır
// ✅ authenticated role özellikleri kullanılabilir

🔧 Troubleshooting

Problem: Profile oluşturulamıyor

Çözüm:

  1. Migration 00093 uygulandı mı kontrol edin
  2. clerk_user_id ve email değerleri dolu mu kontrol edin
  3. Console'da hata mesajlarını kontrol edin

Problem: Profile güncellenemiyor

Çözüm:

  1. Migration 00094 uygulandı mı kontrol edin
  2. Profile'da clerk_user_id boş mu kontrol edin
  3. Email eşleşmesi doğru mu kontrol edin

Problem: JWT Template çalışmıyor

Çözüm:

  1. Template adı supabase olmalı
  2. Supabase JWT Secret doğru mu kontrol edin
  3. Template lifetime 3600 olmalı
  4. getToken({ template: 'supabase' }) kullanıldığından emin olun

📝 Notlar

Clerk Webhook

  • Webhook service role kullanır
  • RLS politikalarından etkilenmez
  • Değişiklik gerektirmez

useAuth Hook

  • Fallback mekanizması mevcut
  • Template yoksa generic token kullanır
  • Her iki durumda da çalışır

Admin Kullanıcılar

  • Admin politikaları ayrı
  • is_admin() fonksiyonu kullanır
  • Değişiklik gerektirmez

🎯 Sonuç

Şu Anki Durum

  • Profile oluşturma çalışıyor (anon role)
  • Profile güncelleme çalışıyor (anon role)
  • Güvenlik korunuyor
  • Kullanıcı deneyimi kesintisiz

Önerilen Adım

  • 📌 Clerk Dashboard'da Supabase JWT Template oluşturun
  • 📌 Uzun vadeli çözüm için daha güvenli
  • 📌 authenticated role özellikleri kullanılabilir
  • 📌 Tüm RLS politikaları normal çalışır

📚 Referanslar