6.9 KiB
6.9 KiB
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
- Clerk Token: Clerk kendi secret key'i ile JWT imzalar
- Supabase Beklentisi: Supabase kendi JWT secret'ı ile imzalanmış token bekler
- Sonuç: Clerk token'ı
anonrole olarak kabul edilir,authenticatedrole'ü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_idboş olamaz - ✅
emailzorunlu - ✅ 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
- Clerk Dashboard → Projenizi seçin
- JWT Templates → New 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
- Supabase Dashboard → Projenizi seçin
- Settings → API → JWT Settings
- 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:
- Migration 00093 uygulandı mı kontrol edin
clerk_user_idveemaildeğerleri dolu mu kontrol edin- Console'da hata mesajlarını kontrol edin
Problem: Profile güncellenemiyor
Çözüm:
- Migration 00094 uygulandı mı kontrol edin
- Profile'da
clerk_user_idboş mu kontrol edin - Email eşleşmesi doğru mu kontrol edin
Problem: JWT Template çalışmıyor
Çözüm:
- Template adı
supabaseolmalı - Supabase JWT Secret doğru mu kontrol edin
- Template lifetime 3600 olmalı
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