Proje: Okul Platform · Hub: Okul Platform — Architecture
Customer OTP login: çoklu kullanıcı seçimi akışı
customer/user/login.blade.php (kurumsal panel girişi) OTP akışında, aynı telefon/e-postaya bağlı birden fazla customer_user varsa hangi hesapla giriş yapılacağını kullanıcıya seçtiriyoruz.
Akış
- Kod iste:
POST /api/auth/otp(school-api proxy) → external APIOtpCodeController@generate - Kodu doğrula + adayları çek:
POST /api/auth/otp/users(yeni proxy, route nameschool-api.auth.otp.users) → external APIcustomer/otp/users(OtpUserSelectionController)- Cevap:
{ data: [{customer_user, customer, user}], user_count: N } user_count= uniqueuser_idsayısı
- Cevap:
- Branch:
user_count === 1→ direktPOST /api/auth/login(mevcut akış, opsiyoneluser_idparametresiyle)user_count > 1→ FE kart liste gösterir (firma/ad-soyad), seçimden sonraPOST /api/auth/loginçağrısınauser_ideklenir
Bu repodaki dosyalar (okulcom/school)
routes/school-api.php—auth/otp/usersproxy routeapp/Http/Controllers/SchoolApi/Auth/AuthController::resolveUsersapp/Http/Requests/SchoolApi/Auth/ResolveOtpUsersRequestapp/Http/Requests/SchoolApi/Auth/UserLoginRequest—user_idopsiyonel kuralı eklendi (sometimes|integer|min:1|prohibits:email,password)app/Service/Api/AuthService::resolveOtpUsers— external API’nincustomer/otp/usersendpoint’ini çağırırresources/views/customer/user/login.blade.php—.otp-user-stepkart listesi,resolveAndLoginJS akışı,data-resolve-urlform attribute’u
External API tarafı (zaten mevcut)
customer/otp/users(auth gerektirmez):OtpUserSelectionController→OtpCodeService::resolveUsersvalidateOtpCodeForUserSelection→ kod doğrular amamarkAsUsed: false(kod resolve’da yanmaz, login’de yanar)- Phone path: yalnızca primary contact eşleşmesi
- Email path: primary contact +
users.emaileşleşmesi (union)
auth/loginopsiyoneluser_idparametresini destekliyor (UserLoginRequest,AttemptOtpLoginDTO::userId)
Yetkilendirme uyarısı
OtpCodeService::resolveUsers ile auth/login (UserRepository::getCustomersWithAccess) filtre setleri özdeş değil:
- Resolve:
customer_users.status=true,users.is_active=true,user_type=customer, primary contact match. - Login: yukarıdakilere ek olarak
customer_user_schools.has_content_access=trueveyahas_lead_access=truezorunluluğu.
Sonuç: bir kullanıcı resolve listesinde görünüp seçildikten sonra login’de getCustomersWithAccess tarafından düşürülürse LoginFailedException::failed() → “Girilen şifre kayıtlarımızla uyuşmuyor.” hatası alınır. İlk testte bu hatayı aldım; ilgili kullanıcının customer_user_schools yetkisi yokmuş, yetki verilince akış sorunsuz çalıştı (yani BE filtre uyumsuzluğu kullanıcıyı düşürmedi, gerçekten yetki eksikti).
İleride farklı bir vakada aynı hata tekrarlarsa filtre uyumsuzluğunu API tarafında hizalamak (resolve’a has_content_access OR has_lead_access filtresi eklemek) doğrudur — login’de görünmeyecek bir kullanıcıyı seçim listesinde göstermek UX hatası.
Response shape (school-api proxy)
AuthController::resolveUsers external API cevabını JsonResponse::success($apiResponse) ile sarıyor:
{ success, message, data: { success, message, data: [...], user_count: N } }
FE: response.data.data[] ve response.data.user_count.
Related
- 2026-04-14-external-api-otp-customer-only — OTP login customer-only kuralı, school access gate’inin kökeni
- 2026-04-14-customer-vs-user-login-rules — Customer vs regular user login ayrımı