İçeriğe atla

Auth0 vs Firebase Auth vs Cognito vs Supabase Auth: Hangisini Seçmeli?

Auth0, Firebase Auth, Supabase Auth, AWS Cognito ve özel JWT: hangisi varsayılan olmalı, fiyat modelleri nasıl ayrışıyor, hangi tuzaklara hazırlıklı olmalı.

Ayhan Sipahi Ayhan Sipahi

Kimlik doğrulama sağlayıcısı seçimi; geliştirme hızını, uyumluluk tavanını ve aylık faturayı yıllarca belirler. Sonradan sağlayıcı değiştirmek, bir ürünün göze alabileceği en riskli geçişlerden biridir. Başlangıç için doğru varsayılan, ana platformunuza zaten yerleşik olan yönetilen sağlayıcıdır: mobil öncelikli tüketici uygulamaları için Firebase Auth, PostgreSQL tabanlı ürünler için Supabase Auth, AWS-native serverless yığınlar içinde Cognito, SAML/SSO ve uyumluluk belgeleri sözleşme şartı olduğunda Auth0. Özel JWT bir öğrenme alıştırması olarak kalır.

Yaygın başlangıç noktası ise bunun tam tersidir: web uygulaması için Auth0, mobil için Firebase Auth, API için özel JWT ve üç ayrı kullanıcı tablosu. Kullanıcılar web’den kayıt olur, sonra mobilde “kullanıcı bulunamadı” duvarına toslar ve konsolidasyon seçenek olmaktan çıkar. Hangi sağlayıcıda birleşeceğinizi altı boyut belirler; maliyet bunlardan yalnızca ilkidir.

Kararı Ne Belirler#

Bunlardan her biri tek başına bir sağlayıcıyı eleyebilir; fiyat bu yüzden kötü bir eşitlik bozucudur. Maliyet yapısı sabit ücretleri, kullanıcı başına veya kimlik doğrulama başına fiyatlandırmayı ve sonradan çıkışın maliyetini kapsar. Teknik entegrasyon kurulum süresini, web-mobil-API kapsamını ve özelleştirmenin akışı yeniden yazmadan ne kadar ileri gidebildiğini kapsar. Kurumsal hazırlık SOC 2, GDPR, HIPAA kapsamını, SAML/SSO ve MFA’yı kapsar. Operasyonel özellikler SLA taahhütlerini, yük altında gecikmeyi ve bakım yükünü kapsar. Geliştirici deneyimi SDK ve dokümantasyon kalitesini, katılım süresini kapsar. Stratejik uyum sağlayıcının halihazırda çalışan yığına uyumunu ve ekibin sağlayıcı bağımlılığına ne kadar tahammül edebileceğini kapsar.

Sağlayıcı Analizi#

Her sağlayıcı bu boyutların farklı bir alt kümesinde kazanır:

Auth0#

Auth0; kurumsal B2B uygulamalara, uyumluluk düzenlemesine tabi sektörlere ve kapsamlı SSO entegrasyonu gereken organizasyonlara uyar. Maliyete duyarlı erken aşama uygulamalar veya basit kimlik doğrulama gereksinimleri için doğru seçim değildir.

İstemci yapılandırması:

// Auth0 SPA istemci yapılandırması
const auth0Config = {
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  audience: process.env.AUTH0_AUDIENCE,
  // Kritik: API erişimi için uygun scope'ları ayarlayın
  scope: 'openid profile email read:users write:users',
  // Rate limit'lerden kaçınmak için token'ları düzgün önbelleğe alın
  cacheLocation: 'localstorage',
  useRefreshTokens: true,
  // Token süresinin dolmasını zarif bir şekilde ele alın
  onRedirectCallback: (appState) => {
    window.history.replaceState(
      {},
      document.title,
      appState?.returnTo || window.location.pathname
    );
  }
};

Auth0, SOC 2, GDPR ve HIPAA uyumluluğunu hazır sunar; buna kapsamlı SAML, LDAP, MFA ve SSO yetenekleri eşlik eder. Admin paneli gelişmiş kullanıcı yönetimini iyi karşılar, geniş dokümantasyon ve kurumsal düzeyde destek de bunu tamamlar.

Maliyet eğrisi dik: 25.000 MAU’ya kadar ücretsiz, sonrasında 500 MAU için 35$/ay’dan başlayan B2C Essentials veya 240$/ay’dan başlayan Professional. Self-servis planlar B2C’de 50.000 MAU, B2B’de 20.000 MAU’da biter; bu çizgilerin ötesinde artık bir Enterprise sözleşmesi pazarlığı yaparsınız. Özellik zenginliği basit senaryoların ihtiyaç duymadığı bir karmaşıklık ekler, Actions üzerinden yapılan kapsamlı özelleştirme de geçişi zorlaştırır. Eski Rules ve Hooks, Actions lehine kullanımdan kaldırıldı, dolayısıyla eski entegrasyonlar taşınmadan önce yeniden yazılmalıdır. Yüksek eşzamanlı yük altında token doğrulama gecikmesi de artabilir.

Ekiplerin takıldığı yer: İstemci her çağrıda yeni token istemeye başladığında doğrulama gecikmesi tırmanır ve Auth0’ın tenant rate limit’lerine çarparsınız. Token’ı SDK içinde önbelleğe alıp süresi dolana kadar yeniden kullanın; servisler arası kimlik bilgilerinde paylaşımlı bir Redis önbelleği aynı işi sunucu tarafında yapar, karşılığında yığına bir bağımlılık daha ekler.

Firebase Auth#

Optimal Kullanım Durumları: Mobil öncelikli tüketici uygulamaları, Google Cloud ekosistem entegrasyonu, hızlı prototipleme Kaçınılacak Durumlar: Çok kiracılı B2B gereksinimleri, katı kurumsal uyumluluk ihtiyaçları, Google dışı bulut ortamları

Üretim yapılandırması:

// React Native + Web için Firebase Auth kurulumu
import { initializeApp } from 'firebase/app';
import { getAuth, connectAuthEmulator } from 'firebase/auth';

const firebaseConfig = {
  apiKey: process.env.FIREBASE_API_KEY,
  authDomain: process.env.FIREBASE_AUTH_DOMAIN,
  projectId: process.env.FIREBASE_PROJECT_ID,
  // Kritik: Bunları istemci tarafı kodda açığa çıkarmayın
  storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.FIREBASE_APP_ID
};

const app = initializeApp(firebaseConfig);
const auth = getAuth(app);

// Üretime hazır hata işleme
auth.onAuthStateChanged((user) => {
  if (user) {
    // Token'ı her zaman sunucu tarafında doğrulayın
    user.getIdToken(true).then((token) => {
      // Doğrulama için backend'inize gönderin
      verifyTokenOnServer(token);
    });
  }
});

Spark planı 50.000 aylık aktif kullanıcıyı ücretsiz kapsar; React Native destekli yerel iOS/Android SDK’ları mobil entegrasyonu kolaylaştırır. Google Cloud servislerine bağlantı sorunsuzdur, e-posta ve sosyal giriş de bir sprint’te değil bir öğleden sonrada bağlanır.

Sınırlamalar:

  • Ekosistem Bağımlılığı: Google servislerinden uzaklaşmak karmaşıklık yaratır
  • Özelleştirme Kısıtları: Kimlik doğrulama akışını Auth0 kadar esnek özelleştiremezsiniz
  • Yönetim Özellikleri: Kurumsal çözümlere kıyasla temel bir yönetim arayüzü
  • Uyumluluk Boşlukları: Sınırlı kurumsal uyumluluk ve denetim yetenekleri; ayrıca SAML/OIDC federasyonu ücretsiz hakkı 50.000 MAU’dan 50’ye düşürür

Maliyet Analizi: 50.000 aylık aktif kullanıcıda Firebase Auth hâlâ ücretsizdir; aynı noktada Auth0 25.000 MAU’luk ücretsiz planını çoktan aşmış, Cognito Essentials ise 40.000 MAU faturalandırıyordur. 50.000’in ötesinde sayaç, Blaze planında Google Cloud Identity Platform tarifelerine geçer.

Supabase Auth#

Optimal Kullanım Durumları: PostgreSQL merkezli mimariler, maliyete duyarlı startup’lar, kendi sunucusunda barındırma seçeneği arayan açık kaynak projeler Kaçınılacak Durumlar: Kurumsal uyumluluk zorunlulukları, karmaşık çok kiracılı mimariler, kritik üretim iş yükleri

Üretim kurulumu:

// Uygun hata işleme ile Supabase Auth
import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_ANON_KEY!
);

// Üretime hazır auth hook'ları
export const useAuth = () => {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // İlk oturumu al
    supabase.auth.getSession().then(({ data: { session } }) => {
      setUser(session?.user ?? null);
      setLoading(false);
    });

    // Auth değişikliklerini dinle
    const { data: { subscription } } = supabase.auth.onAuthStateChange(
      async (event, session) => {
        setUser(session?.user ?? null);
        setLoading(false);
      }
    );

    return () => subscription.unsubscribe();
  }, []);

  return { user, loading };
};

Güçlü Yanları:

  • Maliyet Yapısı: 100.000 aylık aktif kullanıcıya kadar 25$/ay
  • Açık Kaynak: Tam kaynak koduna erişimle kendi sunucunuzda barındırma imkânı
  • Veritabanı Entegrasyonu: Özel kimlik doğrulama mantığı için doğrudan PostgreSQL erişimi
  • Gerçek Zamanlı Özellikler: Canlı güncellemeler için yerleşik WebSocket abonelikleri

Ekosistem Auth0 veya Firebase’e kıyasla daha az olgun, kurumsal uyumluluk ve denetim yetenekleri de sınırlı. Destek, adanmış bir kurumsal ekipten değil topluluktan gelir; gelişmiş özellikler de daha fazla manuel kurulum ister.

Ölçekleme notları: Ücretsiz plan 50.000 MAU taşır; bu, çoğu ürünü gelir elde edene kadar idare eder. 25$/ay’lık Pro planı 100.000 MAU içerir, yani Auth0’ın sizi satış ekibine yönlendirdiği aralık boyunca auth faturanız sabit kalır.

AWS Cognito#

Cognito; AWS merkezli mimariler, serverless uygulamalar ve yüksek ölçekte maliyet optimizasyonu için doğal bir seçim. Çoklu bulut dağıtımlarında, hızlı prototiplemede ve AWS uzmanlığı olmayan ekiplerde ise zorlanır.

Altyapı tanımı:

// CDK ile AWS Cognito
import { UserPool, Mfa, AccountRecovery } from 'aws-cdk-lib/aws-cognito';
import { Duration } from 'aws-cdk-lib';

const userPool = new UserPool(this, 'MyUserPool', {
  userPoolName: 'my-app-users',
  selfSignUpEnabled: true,
  signInAliases: {
    email: true,
    phone: true,
  },
  standardAttributes: {
    email: {
      required: true,
      mutable: true,
    },
  },
  passwordPolicy: {
    minLength: 8,
    requireLowercase: true,
    requireUppercase: true,
    requireDigits: true,
    requireSymbols: true,
  },
  accountRecovery: AccountRecovery.EMAIL_ONLY,
  // Üretim için kritik: MFA'yı etkinleştir
  mfa: Mfa.REQUIRED,
  mfaSecondFactor: {
    sms: true,
    otp: true,
  },
  // Token yapılandırması
  accessTokenValidity: Duration.hours(1),
  idTokenValidity: Duration.hours(1),
  refreshTokenValidity: Duration.days(30),
});

Kasım 2024’ten beri üç fiyat katmanı geçerli: Lite ve Essentials 10.000 MAU’yu ücretsiz içerir, 22 Kasım 2024’ten sonra oluşturulan kullanıcı havuzları Lite’ta 50.000 alır. Lambda, API Gateway ve diğer AWS servisleriyle yerel entegrasyon hazır gelir, milyonlarca kullanıcıya ölçeklenme otomatiktir, AWS güvenlik altyapısı ve uyumluluk sertifikaları da üstüne binmiş olur. Bunun karşılığı; AWS uzmanlığı gerektiren dik bir öğrenme eğrisi, çoğu ekibi özel frontend yazmaya zorlayacak kadar temel bir barındırılan UI ve buna alışkın olmayan bir ekibi bunaltabilecek CloudWatch günlükleridir. Hiçbiri AWS altyapısı dışında çalışmaz.

Maliyet Analizi: Seçtiğiniz katman faturayı neredeyse 3 katına taşır. 100.000 MAU’da Lite, 90.000 kullanıcıyı 0,0055$‘dan faturalandırır (yaklaşık 495$/ay); Essentials aynı kullanıcıları 0,015$‘dan faturalandırır (yaklaşık 1.350$/ay); Plus’ın ise hiç ücretsiz hakkı yoktur. SMS ve e-posta gönderimi SNS ve SES üzerinden ayrıca faturalanır, dolayısıyla SMS tabanlı MFA kendi başına bir kalem olur.

Özel JWT#

Optimal Kullanım Durumları: Basit uygulamalar, öğrenme projeleri, tam kontrolün gerekli olduğu durumlar Kaçınılacak Durumlar: Üretim uygulamaları, uyumluluk gereksinimleri, takım projeleri

Asgari uygulama:

// Uygun güvenlikle özel JWT auth
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';

class CustomAuthService {
  private readonly JWT_SECRET = process.env.JWT_SECRET!;
  private readonly JWT_EXPIRES_IN = '1h';
  private readonly REFRESH_TOKEN_EXPIRES_IN = '7d';

  async generateTokens(userId: string, email: string) {
    const accessToken = jwt.sign(
      { userId, email, type: 'access' },
      this.JWT_SECRET,
      { expiresIn: this.JWT_EXPIRES_IN }
    );

    const refreshToken = jwt.sign(
      { userId, type: 'refresh' },
      this.JWT_SECRET,
      { expiresIn: this.REFRESH_TOKEN_EXPIRES_IN }
    );

    // Refresh token hash'ini veritabanında sakla
    const refreshTokenHash = await bcrypt.hash(refreshToken, 12);
    await this.storeRefreshToken(userId, refreshTokenHash);

    return { accessToken, refreshToken };
  }

  async verifyToken(token: string) {
    try {
      const decoded = jwt.verify(token, this.JWT_SECRET) as any;

      // Token'ın kara listelenip listelenmediğini kontrol et
      const isBlacklisted = await this.isTokenBlacklisted(token);
      if (isBlacklisted) {
        throw new Error('Token kara listede');
      }

      return decoded;
    } catch (error) {
      throw new Error('Geçersiz token');
    }
  }

  async refreshAccessToken(refreshToken: string) {
    try {
      const decoded = jwt.verify(refreshToken, this.JWT_SECRET) as any;

      // Refresh token'ın veritabanında var olduğunu doğrula
      const isValid = await this.verifyRefreshToken(decoded.userId, refreshToken);
      if (!isValid) {
        throw new Error('Geçersiz refresh token');
      }

      // Yeni access token oluştur
      const user = await this.getUserById(decoded.userId);
      return this.generateTokens(user.id, user.email);
    } catch (error) {
      throw new Error('Geçersiz refresh token');
    }
  }
}

Güçlü Yanları:

  • Tam kontrol: Auth akışlarının tam özelleştirmesi ve istediğiniz herhangi bir pattern’i uygulama özgürlüğü
  • Maliyet: Sadece altyapı maliyetleri
  • Öğrenme: Auth kavramlarını anlamak için harika

Sınırlamalar:

  • Güvenlik riskleri: Güvenlik hataları yapmak kolay ve sonrasında her şeyden siz sorumlu olursunuz
  • Uyumluluk: Yerleşik uyumluluk özellikleri yok
  • Zaman yatırımı: Önemli geliştirme süresi gerekli

Detaylı Karşılaştırma Matrisi#

ÖzellikAuth0Firebase AuthSupabase AuthAWS CognitoÖzel JWT
Kurulum Süresi2-4 saat30 dakika1-2 saat4-8 saat1-2 hafta
Maliyet (100k MAU)Enterprise teklifiIdentity Platform tarifesi25$/ay495-1.350$/aySadece altyapı
Mobil DesteğiMükemmelMükemmelİyiİyiManuel
Web DesteğiMükemmelİyiMükemmelTemelManuel
API DesteğiMükemmelİyiİyiMükemmelManuel
Kurumsal ÖzelliklerMükemmelTemelSınırlıİyiManuel
UyumlulukSOC2, GDPR, HIPAATemelSınırlıSOC2, GDPRManuel
ÖzelleştirmeYüksekOrtaYüksekOrtaSınırsız
Sağlayıcı BağımlılığıYüksekYüksekOrtaYüksekYok
Öğrenme EğrisiOrtaDüşükOrtaYüksekYüksek

Senaryolara Göre Sağlayıcı Eşleştirme#

Senaryo 1: Kurumsal Müşterilerle B2B SaaS#

Kurumsal müşterilere satış yapan bir B2B SaaS; SAML/SSO, uyumluluk, kullanıcı yönetimi ve denetim logları ister, ki bu tam olarak Auth0’ın en güçlü olduğu zemindir. SAML bağlantıları Auth0 tenant yapılandırmasında tanımlanır. Uygulama kodunda yeri olan şey, girişten sonra okunan kiracı claim’idir; o da giriş sonrası bir Action içine yazılır:

// Auth0 giriş sonrası Action: kurumsal kiracıyı ID token'a ekle
exports.onExecutePostLogin = async (event, api) => {
  const tenant = event.user.app_metadata?.enterprise;
  if (!tenant) {
    return;
  }

  api.idToken.setCustomClaim('https://myapp.com/enterprise', tenant);
  api.accessToken.setCustomClaim('https://myapp.com/enterprise', tenant);
};

Senaryo 2: Mobil Öncelikli Tüketici Uygulaması#

Sosyal girişe ve ücretsiz katmanda hızlı geliştirmeye ihtiyaç duyan bir tüketici uygulaması neredeyse otomatik olarak Firebase Auth’a çıkar.

Sosyal giriş kurulumu:

// Sosyal giriş ile Firebase Auth
import {
  signInWithPopup,
  GoogleAuthProvider,
  FacebookAuthProvider
} from 'firebase/auth';

const googleProvider = new GoogleAuthProvider();
const facebookProvider = new FacebookAuthProvider();

// Sağlayıcıları yapılandır
googleProvider.addScope('email');
googleProvider.addScope('profile');
facebookProvider.addScope('email');

// Sosyal giriş uygulaması
const signInWithGoogle = async () => {
  try {
    const result = await signInWithPopup(auth, googleProvider);
    const user = result.user;

    // Token'ı doğrulama için backend'e gönder
    const token = await user.getIdToken();
    await verifyTokenOnBackend(token);

    return user;
  } catch (error) {
    console.error('Google giriş hatası:', error);
    throw error;
  }
};

Senaryo 3: Maliyet Bilinçli Startup#

PostgreSQL üzerinde çalışan ve hızlı iterasyon yapan bir startup, sabit Pro plan ücretinin üstüne kullanıcı tablosuna doğrudan SQL erişimi kazanır.

Özel metadata ile kayıt:

// Özel kullanıcı metadata'sı ile Supabase Auth
const { data: { user }, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'securepassword',
  options: {
    data: {
      full_name: 'John Doe',
      company: 'Startup Inc',
      role: 'admin'
    }
  }
});

// Özel mantık için doğrudan veritabanı sorguları
const { data: users, error } = await supabase
  .from('users')
  .select('*')
  .eq('company_id', companyId)
  .order('created_at', { ascending: false });

Senaryo 4: AWS Ağırlıklı Mimari#

Zaten Lambda ve API Gateway üzerine kurulu bir serverless yığın, yerel token doğrulama ve kullanım arttıkça öngörülebilir kalan Lite katmanı fiyatlamasını kazanır.

Lambda’da token doğrulama:

// Lambda trigger'ları ile Cognito
import { CognitoJwtVerifier } from 'aws-jwt-verify';

const verifier = CognitoJwtVerifier.create({
  userPoolId: process.env.COGNITO_USER_POOL_ID!,
  tokenUse: 'access',
  clientId: process.env.COGNITO_CLIENT_ID!,
});

// Cognito auth ile Lambda fonksiyonu
export const handler = async (event) => {
  try {
    // API Gateway v2 header adlarını küçük harfe çevirir; v1 orijinal yazımı korur
    const header = event.headers.authorization ?? event.headers.Authorization;
    const token = header?.replace('Bearer ', '');
    const payload = await verifier.verify(token);

    // Kullanıcı kimliği doğrulandı, iş mantığı ile devam et
    const userId = payload.sub;
    const result = await processUserRequest(userId, event.body);

    return {
      statusCode: 200,
      body: JSON.stringify(result)
    };
  } catch (error) {
    return {
      statusCode: 401,
      body: JSON.stringify({ error: 'Yetkisiz' })
    };
  }
};

Fiyatlandırma Modelleri Yan Yana#

Her sağlayıcı farklı bir birim üzerinden faturalandırır; manşet karşılaştırmaların yanıltıcı olmasının sebebi budur. Aşağıdakiler, kaynaklardaki dört fiyatlandırma sayfasından alınan 2025 liste fiyatlarıdır.

Auth0#

  • Ücretsiz plan: 25.000 aylık aktif kullanıcı
  • B2C planları: 500 MAU’da 35$/ay’dan başlayan Essentials; 240$/ay’dan başlayan Professional
  • B2B planları: 150$/ay’dan başlayan Essentials; 800$/ay’dan başlayan Professional
  • Self-servis tavanı: B2C’de 50.000 MAU, B2B’de 20.000 MAU; ötesinde fiyat Enterprise sözleşmesiyle teklif edilir

Firebase Auth#

  • Ücretsiz katman: Spark planında 50.000 aylık aktif kullanıcı
  • Ücretsiz katmanın ötesi: Blaze planında Google Cloud Identity Platform tarifesiyle faturalanır
  • SAML/OIDC federasyonu: 50 MAU ücretsiz, sonrasında her federe kullanıcı için Identity Platform tarifesi

Supabase Auth#

  • Ücretsiz plan: 50.000 aylık aktif kullanıcı
  • Pro planı: 100.000 MAU dahil olmak üzere 25$/ay
  • Dahil MAU’nun ötesi: MAU başına 0,00325$
  • Hesaplanmış örnek: 150.000 MAU = 25$ + (50.000 × 0,00325$) = 187,50$/ay

AWS Cognito#

  • Lite: 10.000 MAU ücretsiz (22 Kasım 2024’ten sonra oluşturulan kullanıcı havuzlarında 50.000), sonra 100.000’e kadar MAU başına 0,0055$
  • Essentials: 10.000 MAU ücretsiz, sonra MAU başına 0,015$
  • Plus: ücretsiz hak yok, MAU başına 0,020$
  • Federe kullanıcılar: SAML ve OIDC kimlikleri her katmanda 50 ücretsiz MAU’nun üzerinde MAU başına 0,015$
  • Dahil değil: SNS ve SES üzerinden faturalanan SMS ve e-posta gönderimi

Geçiş Stratejileri#

Sağlayıcı geçişleri ciddi risk taşır: her kullanıcının diğer uçta çalışan kimlik bilgileriyle çıkması gerekir ve yarım kalan bir geçiş insanları kendi hesaplarının dışında bırakır. İki kalıp vakaların çoğunu kapsar.

Özel JWT’den Auth0’a Geçiş#

// Kullanıcı verisi için geçiş script'i
const migrateUsersToAuth0 = async () => {
  const users = await getUsersFromCustomDB();

  for (const user of users) {
    try {
      // Auth0'da kullanıcı oluştur. `connection` zorunludur:
      // kullanıcının oluşturulacağı veritabanı bağlantısını adlandırır.
      const { data: auth0User } = await auth0Management.users.create({
        connection: 'Username-Password-Authentication',
        email: user.email,
        password: generateTemporaryPassword(),
        email_verified: user.emailVerified,
        user_metadata: {
          migrated_from: 'custom_jwt',
          original_user_id: user.id
        }
      });

      // Yerel veritabanını Auth0 kullanıcı ID'si ile güncelle
      await updateUserAuth0Id(user.id, auth0User.user_id);

      console.log(`Kullanıcı taşındı: ${user.email}`);
    } catch (error) {
      console.error(`${user.email} kullanıcısını taşıma başarısız:`, error);
    }
  }
};

Firebase’den Auth0’a Geçiş#

Zor kısım parolalardır. Aşağıdaki döngü profilleri ve claim’leri taşır ama kimlik bilgilerini taşımaz; onun için Firebase parola hash’lerini firebase auth:export ile dışa aktarıp scrypt hash’lerini kabul eden Auth0 toplu kullanıcı içe aktarımına verin. Kullanıcıları kimlik bilgisi olmadan oluşturmak tüm kullanıcı tabanınıza parola sıfırlatır ve kimsenin planlamadığı bir destek kuyruğu doğurur.

// Firebase'den Auth0'a geçiş
const migrateFromFirebase = async () => {
  const firebaseUsers = await getFirebaseUsers();

  for (const firebaseUser of firebaseUsers) {
    try {
      // Auth0'da kullanıcı oluştur
      const { data: auth0User } = await auth0Management.users.create({
        connection: 'Username-Password-Authentication',
        email: firebaseUser.email,
        email_verified: firebaseUser.emailVerified,
        user_metadata: {
          firebase_uid: firebaseUser.uid,
          migrated_at: new Date().toISOString()
        }
      });

      // Özel claim'leri taşı. Parametre adı `user_id` değil, `id`.
      if (firebaseUser.customClaims) {
        await auth0Management.users.update(
          { id: auth0User.user_id },
          { app_metadata: firebaseUser.customClaims }
        );
      }

    } catch (error) {
      console.error(`${firebaseUser.email} için geçiş başarısız:`, error);
    }
  }
};

Üretimde Güvenlik#

Token Depolama#

Web ve yerel istemciler farklı cevaplar ister. Yerel uygulamaların bir platform keystore’u vardır; tarayıcının yoktur ve yaygın çözüm yanlıştır: document.cookie ile HttpOnly çerez ayarlanamaz, çünkü bayrağın bütün amacı JavaScript’i dışarıda tutmaktır. Bu çerezi yalnızca sunucu, Set-Cookie ile verebilir.

// Güvenli token işleme
const secureTokenStorage = {
  // Yerel istemciler: iOS'ta Keychain, Android'de Keystore
  storeMobileTokens: async (accessToken: string, refreshToken: string) => {
    await SecureStore.setItemAsync('access_token', accessToken);
    await SecureStore.setItemAsync('refresh_token', refreshToken);
  },

  // Web: refresh token tarayıcıya hiç girmez. Yetkilendirme kodunu backend
  // takas eder ve HttpOnly, Secure, SameSite bayraklı Set-Cookie döner.
  exchangeCodeForSession: async (code: string, codeVerifier: string) => {
    const response = await fetch('/api/auth/callback', {
      method: 'POST',
      credentials: 'include',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ code, codeVerifier })
    });

    return response.ok;
  },

  // Yenileme çerez üzerinden çalışır, yani hiçbir token JavaScript'ten geçmez
  rotateSession: async () => {
    const response = await fetch('/api/auth/refresh', {
      method: 'POST',
      credentials: 'include'
    });

    return response.ok;
  }
};

Rate Limiting#

// Auth endpoint'leri için rate limiting
import rateLimit from 'express-rate-limit';
import { RedisStore } from 'rate-limit-redis';

const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 dakika
  limit: 5, // Pencere başına 5 deneme
  message: 'Çok fazla kimlik doğrulama denemesi, lütfen daha sonra tekrar deneyin',
  standardHeaders: true,
  legacyHeaders: false,
  // Dağıtık sistemler için rate limit verisini Redis'te sakla
  store: new RedisStore({
    sendCommand: (...args: string[]) => redisClient.sendCommand(args),
    prefix: 'auth_rate_limit:'
  })
});

app.use('/api/auth/login', authLimiter);
app.use('/api/auth/register', authLimiter);

Performans Optimizasyonu#

Servisler Arası Token Önbellekleme#

Yukarıda anlatılan Auth0 rate limit sorununu çözen önbellek budur. Yalnızca backend servislerinin client-credentials token’larını tutar; son kullanıcı token’larının paylaşımlı bir önbellekte işi yoktur. Bu Redis’i birden fazla servis paylaştığı için her kayıt; issuer, client ID, audience ve normalize edilmiş scope kümesine göre anahtarlanır. Yalnızca audience’a göre anahtarlamak, bir servise başka bir client’ın yetkileriyle üretilmiş token’ı verir. Kayıt, token’dan biraz önce düşer, böylece hiçbir istek süresi zaten dolmuş bir token’la yola çıkmaz.

// Client-credentials token'ları için Redis tabanlı önbellek
import Redis from 'ioredis';
import { createHash } from 'node:crypto';

interface TokenRequest {
  issuer: string;
  clientId: string;
  audience: string;
  scopes: string[];
}

class M2MTokenCache {
  private redis: Redis;
  private readonly EXPIRY_SKEW_SECONDS = 60;

  constructor() {
    this.redis = new Redis(process.env.REDIS_URL!);
  }

  // Anahtar; issuer, client, audience ve normalize edilmiş scope kümesini kapsar.
  // Hash almak, `read:users` gibi scope adlarının ayırıcıyla çakışmasını önler.
  private cacheKey(request: TokenRequest): string {
    const scopes = [...new Set(request.scopes)].sort().join(' ');
    const fingerprint = createHash('sha256')
      .update([request.issuer, request.clientId, request.audience, scopes].join('\n'))
      .digest('hex');

    return `m2m:${fingerprint}`;
  }

  async cacheToken(request: TokenRequest, token: string, expiresIn: number): Promise<void> {
    const ttl = Math.max(expiresIn - this.EXPIRY_SKEW_SECONDS, 1);
    await this.redis.setex(this.cacheKey(request), ttl, token);
  }

  async getCachedToken(request: TokenRequest): Promise<string | null> {
    return await this.redis.get(this.cacheKey(request));
  }

  async invalidate(request: TokenRequest): Promise<void> {
    await this.redis.del(this.cacheKey(request));
  }
}

Bağlantı Havuzu#

// Auth için veritabanı bağlantı havuzu
const pool = new Pool({
  host: process.env.DB_HOST,
  port: parseInt(process.env.DB_PORT ?? '5432', 10),
  database: process.env.DB_NAME,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  // Auth sorguları için optimize et
  max: 20,
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
  // Üretimde sunucu sertifikasını doğrula. rejectUnauthorized'ı false yapmak
  // TLS'i açmanın anlamını ortadan kaldırır.
  ssl: process.env.NODE_ENV === 'production'
    ? { rejectUnauthorized: true, ca: process.env.DB_CA_CERT }
    : false
});

Entegrasyonun Kırıldığı Noktalar#

Webhook Yarışı#

Belirti: Hesaplar Auth0 panelinde görünür, uygulama veritabanında eşleşen satır yoktur Kök neden: Kayıt sonrası webhook ile ilk kimlik doğrulanmış istek aynı yerel kullanıcıyı oluşturmaya çalışır Çözüm: Yerel kullanıcı oluşturmayı idempotent yapın ve tekillik kısıtı ihlalini başarılı sonuç sayın

// Idempotent kullanıcı oluşturma
const createUserIfNotExists = async (auth0User: any) => {
  const existingUser = await db.user.findUnique({
    where: { auth0Id: auth0User.user_id }
  });

  if (existingUser) {
    return existingUser;
  }

  try {
    return await db.user.create({
      data: {
        auth0Id: auth0User.user_id,
        email: auth0User.email,
        emailVerified: auth0User.email_verified,
        metadata: auth0User.user_metadata
      }
    });
  } catch (error) {
    // Yarış durumunu ele al
    if (error.code === 'P2002') {
      return await db.user.findUnique({
        where: { auth0Id: auth0User.user_id }
      });
    }
    throw error;
  }
};

Saat Kayması#

API çağrılarının küçük bir kısmı token doğrulamada düşer ve aynı isteği tekrarlamak genelde başarılı olur. Neden çoğunlukla saat kaymasıdır: doğrulayan sunucunun saati token’ı üretenden kayar, dolayısıyla nbf veya exp yanlış saniyeye göre değerlendirilir. Doğrulamada sınırlı bir saat toleransı tanımak ve her makinede NTP’yi çalışır tutmak bu farkı kapatır.

// Saat kayması toleransı ile token doğrulama
const validateToken = async (token: string) => {
  try {
    const decoded = jwt.verify(token, process.env.AUTH0_PUBLIC_KEY, {
      algorithms: ['RS256'],
      clockTolerance: 30, // 30 saniye tolerans
      issuer: `https://${process.env.AUTH0_DOMAIN}/`,
      audience: process.env.AUTH0_AUDIENCE
    });

    return decoded;
  } catch (error) {
    console.error('Token doğrulama hatası:', error);
    throw new Error('Geçersiz token');
  }
};

Karar Çerçevesi#

Sağlayıcıyı Seçen Sinyaller#

Genellikle tek bir sinyal baskın çıkar. Özellik tablolarını karşılaştırmadan önce kendi sinyalinizi bulun:

  • Ürün mobil bir tüketici uygulaması ve analitiğiniz zaten Google Cloud’da → Firebase Auth
  • Ürün verisi PostgreSQL’de ve ekip SQL yazmaktan rahatsız değil → Supabase Auth
  • İşlem katmanı API Gateway arkasında Lambda ve her şeyi zaten IAM kapılıyor → Cognito, bir özellik Essentials’a zorlayana kadar Lite katmanında
  • İmzalı bir sözleşme SAML, SCIM veya denetim raporu adını geçiriyor → Auth0; 50.000 MAU üstü için Enterprise görüşmesini bütçeye yazın
  • İki sinyal aynı anda yanıyorsa → ana platformunuza bağlı olanı izleyin, çünkü her gün bakımını yapacağınız entegrasyon odur

Mevcut Projeler İçin#

  • Zorlayan bir sebep yoksa geçmeyin: Kimlik doğrulama geçişleri görünen kapsamıyla orantısız risk taşır
  • Taşımadan önce ölçün: Mevcut giriş başarı oranını ve gecikmesini ölçmezseniz yeni sağlayıcının daha kötü olup olmadığını anlayamazsınız
  • Devir sırasında iki sağlayıcıyı birlikte çalıştırın: Önce yeni sağlayıcıda doğrulayın, düşerse eskisine geri düşün; geri düşme oranı sıfırlanınca eskisini kaldırın
  • Kimlik bilgilerini profillerle birlikte dışa aktarın: Toplu parola sıfırlamaya yol açan bir geçiş, kullanıcı tablosu ne derse desin başarısızdır

Uygulama Kuralları#

  1. Token’ları sunucu tarafında doğrulayın: İstemci tarafı doğrulama size yalnızca istemcinin inanmanızı istediği şeyi söyler
  2. Refresh token’ı JavaScript’ten uzak tutun: Web’de httpOnly çerez, yerel uygulamada platform keystore’u
  3. Giriş ve kayıt için ayrı rate limit koyun: Credential stuffing ile kullanıcı sayımı saldırılarının şekli farklıdır
  4. Yalnızca servisler arası token’ları önbelleğe alın: Sağlayıcı rate limit’leri asıl burada ısırır ve kullanıcı token’larının paylaşımlı önbellekte yeri yoktur
  5. Bütçeyi ölçülen MAU üzerinden kurun: Her sağlayıcı aylık aktif kullanıcıyı biraz farklı sayar ve federe kimlikler çoğu zaman ayrı fiyatlanır
  6. Çıkış yolunu yazılı tutun: Bir sağlayıcıdan nasıl ayrılacağınızı, bunu bir kesinti sırasında çözmek zorunda kalmadan önce yazın

Sağlayıcı fiyatları sık değişir. Yukarıdaki rakamlar kaynaklardaki fiyatlandırma sayfalarından alınan 2025 liste fiyatlarıdır; toplam sahip olma maliyeti ise MAU kaleminin üzerine geliştirme süresini ve ileride yapılacak geçişi de ekler.

Platforma yerleşik varsayılan, ürününüzde tek bir platform baskın olduğu sürece geçerlidir. Üç şey bu varsayılanı değiştirmelidir ve üçü de kendi çözümünüzü yazmanız için gerekçe değildir: sağlayıcınızın karşılayamadığı bir kimlik şartını adıyla yazan bir sözleşme, ikinci bir platformun hiçbir ekosistemi baskın bırakmayacak kadar büyümesi veya MAU eğrisinin self-servis tavanını aşıp Enterprise teklifinin geçiş maliyetini geçmesi.

Kaynaklar#

İlgili yazılar