Kimlik Doğrulama (Authentication)
JWT Token Yapısı
Access Token (30 dakika)
{"exp": 1234567890, "sub": "user-uuid", "tenant_id": "tenant-uuid", "type": "access"}
Refresh Token (7 gün)
{"exp": 1234567890, "sub": "user-uuid", "tenant_id": "tenant-uuid", "type": "refresh"}
Login Akışı
POST /api/auth/login
Body: {"email": "user@example.com", "password": "..."}
Response: {"access_token": "...", "refresh_token": "..."}
Token Yenileme
POST /api/auth/refresh
Body: {"refresh_token": "..."}
Response: {"access_token": "new-token"}
Password Hashing
- Algoritma: pbkdf2_sha256 / bcrypt
passlib[bcrypt]kütüphanesi
Dependency Injection
async def get_current_user(token: str = Depends(HTTPBearer())):
payload = decode_token(token)
user = await db.get(User, payload["sub"])
permissions = load_permissions(user)
return CurrentUser(id=user.id, tenant_id=user.tenant_id, permissions=permissions)
Token İmzalama
| Parametre | Değer |
|---|---|
| Algoritma | HS256 (HMAC with SHA-256) |
| Secret Key | SECRET_KEY environment değişkeni (minimum 32 karakter) |
| Kaynak Dosya | backend/app/core/security/auth.py |
Token imzalama için kullanılan secret key, .env dosyasında SECRET_KEY olarak tanımlanır. Bu değerin kriptografik olarak güçlü ve en az 32 karakter uzunluğunda olması zorunludur. Secret key değiştiğinde mevcut tüm token'lar geçersiz hale gelir.
Token Yenileme ve İptal
Yenileme Akışı
- Access token süresi dolduğunda (30 dakika) client HTTP 401 alır
- Client, refresh token ile
POST /api/auth/refreshendpoint'ine istek gönderir - Backend refresh token'ı doğrular ve yeni access token döner
- Refresh token'ın da süresi dolduysa (7 gün) kullanıcının yeniden giriş yapması gerekir
Stateless JWT Yapısı
- Refresh token'lar veritabanında saklanmaz (stateless JWT)
- Token iptali için blacklist mekanizması yoktur
- Bu nedenle token sürelerinin kısa tutulması kritiktir (access: 30dk, refresh: 7 gün)
Token Geçersiz Kılma
- Şifre değişikliğinde mevcut token'lar doğal süreleri dolana kadar geçerli kalır
- Acil durumda tüm token'ları geçersiz kılmak için
SECRET_KEYrotasyonu yapılmalıdır - SECRET_KEY rotasyonu tüm kullanıcıların token'larını geçersiz kılar — dikkatli kullanılmalıdır
CORS Yapılandırması
FastAPI CORSMiddleware ile cross-origin istekler kontrol altına alınmıştır.
Backend Ayarları
app.add_middleware(
CORSMiddleware,
allow_origins=["https://enerji.kepmark.com"], # Frontend domain
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Next.js Proxy (Same-Origin)
Frontend'den API'ye yapılan istekler next.config.js içindeki rewrites ile proxy'lenir. Bu sayede tarayıcı tarafında same-origin politikası sağlanır:
// next.config.js
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'http://backend:8000/api/:path*',
},
];
}
Bu yapılandırma sayesinde frontend /api/* isteklerini doğrudan kendi origin'inden yapar, CORS sorunları ortadan kalkar.
Rate Limiting
Kimlik doğrulama endpoint'leri Nginx katmanında rate limiting ile korunmaktadır.
| Endpoint | Limit | Açıklama |
|---|---|---|
POST /api/auth/login | 5 istek/dakika | Brute-force saldırı koruması |
POST /api/auth/refresh | 5 istek/dakika | Token yenileme kötüye kullanım koruması |
/api/* (genel) | 30 istek/saniye | Genel API koruması |
Aşım Davranışı
- HTTP 429 Too Many Requests status kodu döner
Retry-Afterheader'ı ile beklenmesi gereken süre belirtilir- Client tarafında exponential backoff stratejisi önerilir
Nginx Yapılandırma Örneği
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;
location /api/auth/login {
limit_req zone=login burst=3 nodelay;
proxy_pass http://backend;
}
location /api/ {
limit_req zone=api burst=50 nodelay;
proxy_pass http://backend;
}
Şifre Sıfırlama (Self-Service Password Reset)
Kullanıcılar şifrelerini unuttuklarında yönetici müdahalesi olmadan kendileri
sıfırlayabilir. Bu akış her zaman açıktır — tenant signup özelliğinin
(TENANT_SIGNUP_ENABLED) açık/kapalı olmasından bağımsız çalışır.
Ekran Akışı
- Giriş sayfası (
/login): Parola alanının yanındaki "Parolamı Unuttum" bağlantısına tıklanır. - Şifremi Unuttum (
/forgot-password): E-posta adresi girilir ve "Sıfırlama Bağlantısı Gönder" butonuna basılır. Yanıt her durumda aynıdır: "E-posta adresiniz sistemde kayıtlıysa şifre sıfırlama bağlantısı gönderildi." - E-posta: Gelen kutusuna tek kullanımlık sıfırlama bağlantısı düşer
(
/reset-password?token=...). - Yeni Şifre Belirle (
/reset-password): Yeni parola iki kez girilir ve güncellenir. Başarı ekranındaki "Yeni şifremle giriş yap" butonu giriş sayfasına yönlendirir.
Şifre Politikası
Yeni parola aşağıdaki kurallara uymak zorundadır (kayıt/signup ve
kullanıcı daveti kabulü ile aynı
politika — tek kaynak: app/core/security/password_policy.py):
- En az 12, en fazla 128 karakter
- En az bir harf (Türkçe karakterler dahil) ve bir rakam
Politikaya uymayan parola hem arayüzde anında, hem backend'de (422) reddedilir.
Güvenlik Özellikleri
| Özellik | Davranış |
|---|---|
| Tek aktif bağlantı | Yeni sıfırlama isteği, önceki aktif bağlantıları iptal eder — yalnız en son gönderilen bağlantı çalışır |
| Bağlantı ömrü (TTL) | 60 dakika (PASSWORD_RESET_TOKEN_TTL_MINUTES, varsayılan 60). Süresi dolan bağlantı için yeni istek yapılmalıdır |
| Tek kullanımlık token | Bağlantı bir kez kullanıldığında geçersizleşir |
| Enumeration koruması | İstek yanıtı e-postanın kayıtlı olup olmadığını açığa çıkarmaz — her iki durumda da aynı mesaj döner |
| Oracle koruması | Geçersiz / süresi dolmuş / kullanılmış bağlantı ayrımı yapılmadan tek bir genel hata gösterilir |
| Oturum etkisi | Başarılı sıfırlama sonrası eski refresh token'lar geçersizleşir; kullanıcı yeni parolayla tekrar giriş yapar (mevcut access token'lar exp süresine kadar geçerli kalır) |
| Rate limit | E-posta başına 3 istek/saat + IP başına 10 istek/saat (aşımda 429 + bekleme süresi) |
İlgili Endpoint'ler
| Endpoint | Açıklama |
|---|---|
POST /api/auth/password-reset/request | Sıfırlama bağlantısı iste (public, her zaman 202) |
POST /api/auth/password-reset/confirm | Token'ı tüket + yeni parolayı kaydet (public, 200) |
İlgili Sayfalar
Aynı güvenlik ilkeleri (tek kullanımlık bağlantı, bağlantı ömrü/TTL, oracle koruması, rate limit) hesap oluşturma akışlarında da geçerlidir:
- Yeni Firma Kaydı — çevrimiçi firma başvurusu (telefon kodu + e-posta doğrulama bağlantısı)
- Kullanıcı Davetleri — davet bağlantısı ile hesap kurulumu (7 gün TTL, tek aktif bağlantı)