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.
