Proje: Okul B2B App · Hub: Okul B2B App — Incidents

/customer/otp/users 403 — postPublic x-consumer-key göndermiyor

Semptom

OTP çoklu kullanıcı seçimi akışında, kod doğrulama adımı olan POST https://api.okul.work/customer/otp/users çağrısı 403 döndü:

[ERROR] API Error (Public): POST .../customer/otp/users [Error: 403]
[ERROR] Resolve OTP users error [ServerError: Server error: 403]

/auth/otp (send) aynı apiClient.postPublic() ile sorunsuz çalışıyordu — sadece resolve çağrısı patladı.

Root cause

okulcom-api route gruplarında middleware farklı:

  • routes/api/auth.php['api'] (sadece base middleware)
  • routes/api/customer.php['api', 'auth:sanctum', 'platform', 'user-group-type:...']
    • customer/otp/users route’u withoutMiddleware(['auth:sanctum', 'user-group-type:...']) ile auth’u kaldırmış ama platform (VerifyConsumerToken) middleware’ini bırakmış.
    • Bu middleware x-consumer-key header’ını şart koşar; yoksa 403.

apiClient.postPublic() ise defaultHeaders’ı bilinçli olarak göndermiyor (utils/ApiClient.ts:148):

// Use ONLY provided headers (no defaultHeaders for public endpoints)
const requestHeaders = headers;

Sonuç: x-consumer-key gitmiyor → 403.

Çözüm

AuthService.resolveOtpUsers()’da postPublic yerine post kullan. apiClient.post() defaultHeaders’ı (x-consumer-key dahil) merge eder ama Authorization sadece setAuthToken() çağrılmışsa eklenir — yani auth gerektirmeyen ama platform doğrulaması isteyen endpoint’ler için doğru seçim:

// /customer/* group requires `x-consumer-key` (platform middleware) but NOT auth.
// post() (vs postPublic) merges defaultHeaders which includes x-consumer-key.
const response = await apiClient.post<any>('/customer/otp/users', { value, code });

Commit: f90a9d6 (feat(auth): OTP login çoklu kullanıcı seçimi).

Çıkarım

postPublic = “hiçbir default header gitmesin” demek. Yani Authorization + x-cid + x-consumer-key + Accept-Language + X-App-Version hepsi düşer. Sadece tamamen public, hiçbir platform doğrulaması istemeyen endpoint’lerde (örn. /auth/otp, /auth/login) kullanılır.

Auth gerekmiyor ama platform doğrulaması/locale/version header’ı gerekiyorsa → post() kullan. Auth token zaten yoksa eklenmiyor.