PRD "AyrisLegal Google Drive Backup & Recovery" kapsamında sadece backend altyapısı: OAuth connect/callback/status/disconnect/refresh uç noktaları, AES-256-GCM token şifreleme (tokenCrypto.ts), tek kullanımlık CSRF state (oauthState.ts), drive.file scope ile AyrisLegal/Davalar klasör oluşturma (googleDriveClient.ts). Electron tarafı (dosya tarama/kuyruk/UI) ayrı bir aşamada. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
37 lines
1.4 KiB
TypeScript
37 lines
1.4 KiB
TypeScript
import crypto from 'crypto';
|
||
|
||
// Google OAuth "state" parametresi: CSRF'e karşı kriptografik olarak rastgele,
|
||
// kısa ömürlü, tek kullanımlık (bkz. PRD §20). Bellek-içi Map yeterli — OAuth
|
||
// akışı saniyeler/dakikalar içinde tamamlanıyor, kalıcı depolamaya gerek yok.
|
||
// NOT: backend birden fazla instance'la (load balancer arkasında) çalışırsa bu
|
||
// state farklı bir instance'a düşebilir — o zaman paylaşımlı bir store (Redis
|
||
// vb.) gerekir. Şu an tek instance varsayımıyla yazıldı.
|
||
interface StateEntry { userId: string; expiresAt: number; }
|
||
|
||
const STATE_TTL_MS = 10 * 60 * 1000; // 10 dakika
|
||
const stateStore = new Map<string, StateEntry>();
|
||
|
||
function cleanupExpired() {
|
||
const now = Date.now();
|
||
for (const [key, entry] of stateStore.entries()) {
|
||
if (entry.expiresAt < now) stateStore.delete(key);
|
||
}
|
||
}
|
||
|
||
export function createState(userId: string): string {
|
||
cleanupExpired();
|
||
const state = crypto.randomBytes(32).toString('hex');
|
||
stateStore.set(state, { userId, expiresAt: Date.now() + STATE_TTL_MS });
|
||
return state;
|
||
}
|
||
|
||
// Tek kullanımlık — çağrıldığı anda store'dan siliniyor, ikinci kullanım (replay)
|
||
// her zaman null döner.
|
||
export function consumeState(state: string): string | null {
|
||
const entry = stateStore.get(state);
|
||
if (!entry) return null;
|
||
stateStore.delete(state);
|
||
if (entry.expiresAt < Date.now()) return null;
|
||
return entry.userId;
|
||
}
|