232 lines
6.9 KiB
Markdown
232 lines
6.9 KiB
Markdown
# 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
|
||
```sql
|
||
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
|
||
```sql
|
||
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](https://dashboard.clerk.com) → Projenizi seçin
|
||
2. **JWT Templates** → **New Template**
|
||
|
||
#### Adım 2: Supabase Template Oluşturun
|
||
```json
|
||
{
|
||
"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](https://supabase.com/dashboard) → Projenizi seçin
|
||
2. **Settings** → **API** → **JWT 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:
|
||
|
||
```typescript
|
||
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ı
|
||
```typescript
|
||
// 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
|
||
```typescript
|
||
// 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ş
|
||
```typescript
|
||
// 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
|
||
|
||
- [Clerk JWT Templates](https://clerk.com/docs/backend-requests/making/jwt-templates)
|
||
- [Supabase RLS](https://supabase.com/docs/guides/auth/row-level-security)
|
||
- [Clerk + Supabase Integration](https://clerk.com/docs/integrations/databases/supabase)
|