Firebase Authentication, kullanıcı doğrulama işlemlerini güvenli ve kolay bir şekilde gerçekleştirmenizi sağlar. Bu yazıda, SMS tabanlı OTP (One-Time Password) doğrulama sisteminin nasıl implemente edileceğini detaylıca inceleyeceğiz.
Firebase OTP Doğrulama Nedir?
Firebase OTP doğrulama, kullanıcının telefon numarasını doğrulamak için tek kullanımlık şifre gönderen bir sistemdir. Bu sistem:
- Kullanıcı kimliğini güvenli bir şekilde doğrular
- SMS üzerinden tek kullanımlık şifre gönderir
- Otomatik SMS algılama özelliği sunar (Android ve iOS 17+)
- Çoklu platform desteği sağlar (iOS, Android, Web)
- Güvenlik ve dolandırıcılık koruması sunar
- App Check entegrasyonu ile güvenliği artırır
- Multi-factor authentication (MFA) desteği sunar
Firebase Projesini Hazırlama
1. Firebase Console Ayarları
Öncelikle Firebase Console'da gerekli ayarları yapmamız gerekiyor:
- Firebase Console'a gidin
- Yeni bir proje oluşturun (veya mevcut projenizi seçin)
- Authentication > Sign-in method bölümüne gidin
- "Phone" sağlayıcısını etkinleştirin
- App Check'i etkinleştirin (önerilen)
- Test telefon numaralarını ekleyin (geliştirme aşaması için)
2. Firebase SDK Kurulumu
# NPM ile kurulum npm install firebase @firebase/auth # Yarn ile kurulum yarn add firebase @firebase/auth
3. Firebase Konfigürasyonu
// firebase/config.ts import { initializeApp } from 'firebase/app'; import { getAuth, initializeAppCheck, ReCaptchaV3Provider } from 'firebase/auth'; const firebaseConfig = { apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID }; // Firebase'i başlat const app = initializeApp(firebaseConfig); // App Check'i etkinleştir (önerilen) if (process.env.NODE_ENV === 'production') { initializeAppCheck(app, { provider: new ReCaptchaV3Provider(process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY!), isTokenAutoRefreshEnabled: true }); } // Auth nesnesini oluştur export const auth = getAuth(app);
OTP Doğrulama Implementasyonu
1. Telefon Numarası Girişi ve Doğrulama Başlatma
// components/PhoneAuth.tsx import { useState } from 'react'; import { RecaptchaVerifier, signInWithPhoneNumber } from 'firebase/auth'; import { auth } from '../firebase/config'; export function PhoneAuth() { const [phoneNumber, setPhoneNumber] = useState(''); const [verificationId, setVerificationId] = useState(''); const [error, setError] = useState<string | null>(null); // reCAPTCHA doğrulayıcısını oluştur const setupRecaptcha = () => { const recaptchaVerifier = new RecaptchaVerifier(auth, 'recaptcha-container', { size: 'normal', callback: () => { // reCAPTCHA başarılı startPhoneVerification(); }, 'expired-callback': () => { // reCAPTCHA süresi doldu setError('reCAPTCHA süresi doldu. Lütfen tekrar deneyin.'); } }); return recaptchaVerifier; }; // Telefon doğrulamasını başlat const startPhoneVerification = async () => { try { const recaptchaVerifier = setupRecaptcha(); const confirmationResult = await signInWithPhoneNumber( auth, phoneNumber, recaptchaVerifier ); setVerificationId(confirmationResult.verificationId); setError(null); } catch (error) { setError('Doğrulama kodu gönderilemedi. Lütfen tekrar deneyin.'); console.error('Phone verification error:', error); } }; return ( <div className="max-w-md mx-auto p-6"> <h2 className="text-2xl font-bold mb-4"> Telefon Numarası Doğrulama </h2> <div className="space-y-4"> <div> <label className="block text-sm font-medium mb-1"> Telefon Numarası </label> <input type="tel" value={phoneNumber} onChange={(e) => setPhoneNumber(e.target.value)} placeholder="+90 5XX XXX XX XX" className="w-full px-4 py-2 border rounded-md" /> </div> {/* reCAPTCHA container */} <div id="recaptcha-container"></div> <button onClick={startPhoneVerification} className="w-full px-4 py-2 bg-blue-600 text-white rounded-md" > Doğrulama Kodu Gönder </button> {error && ( <p className="text-red-500 text-sm">{error}</p> )} </div> </div> ); }
2. OTP Kodu Doğrulama
// components/OTPVerification.tsx import { useState } from 'react'; import { PhoneAuthProvider, signInWithCredential } from 'firebase/auth'; import { auth } from '../firebase/config'; interface OTPVerificationProps { verificationId: string; onSuccess: () => void; } export function OTPVerification({ verificationId, onSuccess }: OTPVerificationProps) { const [otp, setOtp] = useState(''); const [error, setError] = useState<string | null>(null); const [isVerifying, setIsVerifying] = useState(false); const verifyOTP = async () => { if (otp.length !== 6) { setError('Lütfen 6 haneli doğrulama kodunu girin.'); return; } setIsVerifying(true); setError(null); try { // Credential oluştur const credential = PhoneAuthProvider.credential( verificationId, otp ); // Credential ile giriş yap await signInWithCredential(auth, credential); onSuccess(); } catch (error) { setError('Geçersiz doğrulama kodu. Lütfen tekrar deneyin.'); console.error('OTP verification error:', error); } finally { setIsVerifying(false); } }; return ( <div className="max-w-md mx-auto p-6"> <h2 className="text-2xl font-bold mb-4"> Doğrulama Kodu </h2> <div className="space-y-4"> <div> <label className="block text-sm font-medium mb-1"> 6 Haneli Kod </label> <input type="text" maxLength={6} value={otp} onChange={(e) => setOtp(e.target.value.replace(/\D/g, ''))} placeholder="123456" className="w-full px-4 py-2 border rounded-md text-center text-2xl tracking-widest" /> </div> <button onClick={verifyOTP} disabled={isVerifying} className="w-full px-4 py-2 bg-blue-600 text-white rounded-md disabled:opacity-50" > {isVerifying ? 'Doğrulanıyor...' : 'Doğrula'} </button> {error && ( <p className="text-red-500 text-sm">{error}</p> )} </div> </div> ); }
3. Ana Komponent ve Durum Yönetimi
// pages/auth/phone.tsx import { useState } from 'react'; import { PhoneAuth } from '../../components/PhoneAuth'; import { OTPVerification } from '../../components/OTPVerification'; export default function PhoneAuthPage() { const [verificationId, setVerificationId] = useState<string | null>(null); const [isAuthenticated, setIsAuthenticated] = useState(false); const handleVerificationSuccess = () => { setIsAuthenticated(true); // Kullanıcıyı yönlendir veya state'i güncelle }; if (isAuthenticated) { return ( <div className="text-center p-6"> <h2 className="text-2xl font-bold text-green-600"> Başarıyla doğrulandı! </h2> <p className="mt-2"> Telefon numaranız başarıyla doğrulandı. </p> </div> ); } return ( <div> {!verificationId ? ( <PhoneAuth onVerificationSent={(id) => setVerificationId(id)} /> ) : ( <OTPVerification verificationId={verificationId} onSuccess={handleVerificationSuccess} /> )} </div> ); }
Güvenlik Önlemleri
1. App Check Entegrasyonu
// utils/appCheck.ts import { initializeAppCheck, ReCaptchaV3Provider } from 'firebase/app-check'; export function setupAppCheck(app: FirebaseApp) { if (process.env.NODE_ENV === 'production') { initializeAppCheck(app, { provider: new ReCaptchaV3Provider(process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY!), isTokenAutoRefreshEnabled: true }); } }
2. Rate Limiting ve Güvenlik Kuralları
// Firebase Security Rules { "rules": { "phoneVerification": { "$uid": { ".read": "$uid === auth.uid", ".write": "$uid === auth.uid", "attempts": { ".validate": "newData.val() <= 5 && newData.val() >= 0 && (!data.exists() || newData.val() > data.val()) && now - data.child('lastAttempt').val() > 300000" // 5 dakika } } } } }
3. Multi-Factor Authentication (MFA)
// utils/mfa.ts import { multiFactor, PhoneAuthProvider, PhoneMultiFactorGenerator } from 'firebase/auth'; export async function enrollPhoneMFA(auth: Auth, phoneNumber: string) { const user = auth.currentUser; if (!user) throw new Error('Kullanıcı oturum açmamış'); const multiFactorSession = await multiFactor(user).getSession(); const phoneAuthProvider = new PhoneAuthProvider(auth); const verificationId = await phoneAuthProvider.verifyPhoneNumber({ phoneNumber, session: multiFactorSession }); return verificationId; } export async function verifyPhoneMFA(auth: Auth, verificationId: string, verificationCode: string) { const user = auth.currentUser; if (!user) throw new Error('Kullanıcı oturum açmamış'); const cred = PhoneAuthProvider.credential(verificationId, verificationCode); const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred); await multiFactor(user).enroll(multiFactorAssertion, 'Phone Number'); }
En İyi Pratikler (2024)
Güvenlik
- App Check kullanın
- MFA desteği ekleyin
- Rate limiting uygulayın
- IP tabanlı kısıtlamalar ekleyin
- Şüpheli aktiviteleri izleyin
Kullanıcı Deneyimi
- Progressive Web App (PWA) desteği
- Otomatik SMS algılama
- Offline destek
- Hata durumlarında retry mekanizması
- Accessibility standartlarına uyum
Performans
- Lazy loading
- Code splitting
- Service worker optimizasyonu
- Firebase SDK modüllerini ayrı ayrı import edin
Test ve İzleme
- Firebase Analytics entegrasyonu
- Error tracking
- Performance monitoring
- A/B testing
- User feedback collection
Sonuç
Firebase OTP doğrulama sistemi, güvenli ve kullanıcı dostu bir kimlik doğrulama çözümü sunar. Bu yazıda öğrendiklerimizi özetleyelim:
- Firebase Authentication kurulumu ve konfigürasyonu
- Telefon numarası doğrulama akışı
- OTP kodu doğrulama implementasyonu
- Güvenlik önlemleri ve en iyi pratikler
Bu sistemi kendi projenize entegre ederek, güvenli bir telefon doğrulama sistemi oluşturabilirsiniz.