# Tasarım `10 Ortak Auth` — müşteri yarısı · kapsama matrisi

**Tarih:** 03.08.2026 · **Kaynak:** `design/10 Ortak Auth.dc.html` (298 satır, tamamı okundu)
**Kapsam:** ofis yarısı zaten yazılıydı (`Office/Auth/*`); bu matris **müşteri** ekranlarını ölçer.
**Bağlam:** uçtan uca denetim §1.5, Kuşak 0 · madde **0.4** — `GET /giris` yoktu, misafir 405 alıyordu.

⚠ Bellekten değil, dosya yeniden açılarak üretildi. Kanıt sütunundaki her satır gerçek dosya + satırdır.

---

## 1 · Giriş ekranı — desktop (880×560)

| Tasarım öğesi | | Kanıt |
|---|---|---|
| İki kolon: solda form (`flex:1`, `#EEF1F3`), sağda tanıtım (350px, beyaz, sol kenarlık) | ✓ | `templates/CustomerAuthLayout.vue:41-58` — `flex`, `main flex-1 bg-bg`, `aside w-88 border-l bg-surface` |
| Ofisin aynası: ofiste koyu kolon solda, müşteride açık tanıtım sağda | ✓ | `CustomerAuthLayout.vue:31-34` (yorum) · karşılaştırma `templates/AuthLayout.vue:13-20` |
| Logo `logo-card-light-akademi.svg`, 180px | ✓ | `CustomerAuthLayout.vue:44` — `w-45` (180px) |
| Eyebrow "MÜŞTERİ PORTALI" (10px, 700, ls 2.5px, `#78858F`) | ✓ | `CustomerAuthLayout.vue:45` — `microcaps text-ink-400` · metin `lang/tr/auth.php` `customer.eyebrow` |
| Başlık "Firma hesabınıza giriş yapın" (20px/800) | ✓ | `pages/Portal/Auth/SignIn.vue:79` — `text-h2` · `customer.title` |
| Alan 1 etiketi **"E-posta veya cep telefonu"** | ⏸ | `SignIn.vue:91` — **"E-posta"**. Telefonla giriş SMS entegrasyonuna **ertelendi** (Sets, 03.08.2026) → §6.1 |
| Alan 2 "Şifre" + sağa yaslı "Şifremi unuttum" bağlantısı | ✓ | `SignIn.vue:100-110` — `FormField` `#labelAside` slot'u |
| Şifre alanında "Göster" | ✓ | `SignIn.vue:118-119` — `PasswordInput` `show-label`/`hide-label` |
| Birincil düğme "Giriş Yap", tam genişlik, min 46px | ✓ | `SignIn.vue:124-130` — `BaseButton` `primary`, `min-h-12 md:min-h-11` (48/44px). Mockup'ın accent rengi **bilinçli sapma**, katalog kararı → §6.2 |
| "VEYA" ayracı — iki yatay çizgi arasında | ✓ | `SignIn.vue:133-137` — `h-px flex-1 bg-ink-100` + `customer.or` |
| İkincil düğme "Bana giriş bağlantısı gönder" | ✓ | `SignIn.vue:139-145` — `BaseButton variant="secondary"` |
| Ortalanmış ipucu "Şifresiz giriş: bağlantı e-postanıza gelir, **15 dk** geçerli" | ✓ | `SignIn.vue:147` — süre ayardan okunuyor, **30 dk** (Sets kararı) → §6.3 |

## 2 · Sağ tanıtım kolonu

| Tasarım öğesi | | Kanıt |
|---|---|---|
| Eyebrow "PORTALDA SİZİ BEKLEYENLER" | ✓ | `CustomerAuthLayout.vue:52` · `customer.promo_title` |
| 4 madde: nokta + başlık + açıklama | ✓ | `CustomerAuthLayout.vue:56-70` · liste `pages/Portal/Auth/promo.js` |
| Nokta renkleri: yeşil · mavi · altın · mor | ✓ | `CustomerAuthLayout.vue:22-27` — `success-dot`, `info-dot`, `caution-dot`, `viz-4` |
| Maddeler arasında alt kenarlık, sonuncuda yok | ✓ | `CustomerAuthLayout.vue:62` — `index < length - 1 ? 'border-b border-ink-50'` |
| Metinler ("Personel uyum durumu", "Teklif ve onaylar", "Sertifikalar", "İçerik paketleri") ve alt satırları | ✓ | `lang/tr/auth.php` `customer.promo_*` — tasarımcının cümleleri birebir |
| En altta (`margin-top:auto`) gri kutu: "Hesabınız yok mu? Firma yetkiliniz sizi **Organizasyon** ekranından davet edebilir." | ✓ | `CustomerAuthLayout.vue:72-74` — `mt-auto rounded-input bg-bg` · `customer.promo_note` |

## 3 · 375px

| Tasarım öğesi | | Kanıt |
|---|---|---|
| Sağ tanıtım kolonu 768px altında **gizlenir** (yığılmaz) | ✓ | `CustomerAuthLayout.vue:50` — `hidden … md:flex` |
| Form tek kolon | ✓ | `CustomerAuthLayout.vue:42-47` — `main flex-1` tek kolon, `max-w-85` |
| 48px dokunma hedefleri | ✓ | `atoms/BaseButton.vue:45` — `min-h-12 md:min-h-11`. Arch testi: `FrontendTest` *"grows every form control to 48px on a phone"* → §6.4 |

## 4 · Kart halleri

| Tasarım öğesi | | Kanıt |
|---|---|---|
| "MÜŞTERİ · MAGIC LINK / Bağlantı gönderildi" — adres + süre + yeşil şerit | ✓ | `SignIn.vue:58-76` · `auth.customer_magic_link.sent_*` · `Banner tone="success"` |
| "Bağlantının süresi doldu" — turuncu nokta + başlık + "Yeni Bağlantı İste" | ✓ | `pages/Portal/Auth/MagicLinkExpired.vue:29-42` — `StatusDot tone="warning"` |
| Süresi dolan bağlantı **hata sayfası değil bu kartı** çizer | ✓ | `Auth/MagicLinkController.php:44-56` (`catch (AuthenticationRefused)`) · test `CustomerAuthScreenTest.php` "draws the expired card" |
| "MÜŞTERİ · PASSCODE" — SMS kodu kartı | ❌ | **Kapsam dışı, kayıtlı karar.** `AuthMethod` (27.07.2026, Sets): dördüncü yöntem passkey'dir, gönderilen kod magic link'in kendisidir. SMS yöntemi yok |
| Şifre sıfırlama 1/2 — "Şifreni sıfırla" + e-posta + "Bağlantı Gönder" + "← Girişe dön" | ✓ | `pages/Portal/Auth/ForgotPassword.vue:33-68` |
| Şifre sıfırlama 2/2 — yeni şifre + güç çubuğu + tekrar + "Şifreyi Güncelle" + oturum notu | ✓ | `pages/Portal/Auth/ResetPassword.vue:43-107` — `PasswordStrength` dahil |

## 5 · Tasarımcının çizmediği, akış için zorunlu ekran

| Ekran | | Kanıt |
|---|---|---|
| Magic link kimlik teyidi (TC son 4 hane) | ✓ (tasarımsız) | `pages/Portal/Auth/Challenge.vue` — mevcut görsel dille, `OtpInput length=4`. Tasarımcının yerine koyduğu SMS kartı kapsam dışı olduğu için karşılığı yok |
| Müşteri ikinci faktörü (2/2) | ✓ (tasarımsız) | `pages/Portal/Auth/SecondFactor.vue` — ofis kartının (`10`, "OFİS · ADIM 2/2") müşteri karşılığı |

---

## 6 · ⚠/❌ satırların açıklaması — **karara bağlandı (Sets, 03.08.2026)**

### 6.1 · "E-posta veya cep telefonu" → "E-posta"

**Ölçüm:** `CustomerAuthenticator::attempt()` yalnız adres hash'iyle arar (`app/Support/Auth/CustomerAuthenticator.php`); `SignInRequest` kuralı `['required','string','email','max:255']`. **Telefonla giriş arka uçta yok.** Veritabanı geçidi (`app.customer_user_lookup`) telefon hash'i döndürmüyor.

Etiketi olduğu gibi yazmak, ekranın çalışmayan bir yolu vaat etmesi olurdu. Doğru olan yazıldı.

✅ **KARAR (Sets, 03.08.2026): şimdilik e-posta kalsın.** *"SMS entegrasyonu yaptıktan sonra bakacağız, daha SMS entegrasyonu ile ilgili dönüş gelmedi. Provider ekleyeceğiz sonuçta çok dert olmaz."*

Yani telefonla giriş **ertelendi, reddedilmedi**. Açıldığı gün gereken üç şey: geçit fonksiyonuna (`app.customer_user_lookup`) telefon hash'i, `phone_hash` kolonu + migration, ve etiketin tasarımdaki tam metne (`E-posta veya cep telefonu`) dönmesi. SMS sağlayıcı sözleşmesi geldiğinde bu satır yeniden açılır.

### 6.2 · Birincil düğme rengi

Mockup müşteri "Giriş Yap"ını **accent** (`oklch(0.5 0.1 243)`), ofis "Devam Et"ini ink çiziyor. `BaseButton`'ın kendi gerekçesi bunun tersini söylüyor: *"Ink for the primary action, not accent: the accent carries links and focus, and a page where the button and every link share one colour stops telling them apart (00 Tokenlar)."*

Yani bu, **daha önce alınmış ve `00 Tokenlar`'a dayanan** bir karar. Yeni bir sapma üretmemek için katalog kararına uyuldu; ekran ofisle aynı düğmeyi kullanıyor.

✅ **KARAR (Sets, 03.08.2026): katalog kararı ile devam.** *"Doğrusunu yapmışsın."* Müşteri birincil düğmesi ofisle aynı ink düğmedir; `BaseButton`'a accent varyantı yazılmaz. Mockup'ın accent düğmesi **uygulanmayacak bilinçli sapmadır** ve gerekçesi `00 Tokenlar`'dır: accent bağlantıyı ve odağı taşır, düğmeyle paylaşırsa ikisi ayırt edilemez.

### 6.3 · "15 dk" → gerçek süre

`config/security.php:60` → `magic_link_ttl = 30 * 60`, yani **30 dakika**. Ekran ayarı okuyor (`CustomerSessionController::linkMinutes()`), çizimi değil. Mockup'ın sayısını yazmak, kullanıcıya bağlantısının 15 dakika erken öldüğünü söylemek olurdu.

✅ **KARAR (Sets, 03.08.2026): 30 dakika.** *"Mockup burada örnek, bizim planımız ne ise ona uyacağız."* `config/security.php` değişmez; ekran zaten ayarı okuyor, çizimi değil. Kod değişikliği gerekmedi.

### 6.4 · 44px vs 48px dokunma hedefi

`BaseButton` bütün panellerde `min-h-11` (44px) kullanıyor; `01 Form Elemanlari` kataloğunun ölçüsü bu. Tasarımın 375px bloğu müşteri girişi için 48px yazıyor. Tek ekran için atomu ayırmak, kataloğu ikiye bölmek olurdu.

✅ **KARAR (Sets, 03.08.2026): düzeltildi, tasarıma uyuldu.** *"O tasarımcının sorumluluğu."*

Ölçüm, kuralın **yarım** uygulandığını gösterdi — ve asıl bulgu bu: `HANDOFF §11` zaten *"dokunma hedefi ≥44px (mobil form 48px)"* diyor, **bütün girdiler** ilk yazıldıkları günden beri `min-h-12 md:min-h-11` taşıyor, ama `BaseButton` düz `min-h-11`di. Yani bir formun **alanları** başparmak için büyüyor, **gönder düğmesi** büyümüyordu — başparmağın gerçekten ısabet etmesi gereken tek kontrol.

Düzeltilenler: `BaseButton`, `PasswordInput`'un göster düğmesi, `DateRangePicker` tetikleyicisi, `FileDrop`'un gözat düğmesi, anket ölçek düğmeleri, giriş ekranının metin düğmesi.

⛔ Bir daha kayamasın diye arch testi yazıldı: `tests/Arch/FrontendTest.php` → *"grows every form control to 48px on a phone"*. Breakpoint'siz `min-h-11` taşıyan form kontrolü testi kırar. ⚠ Kapsam form kontrolleridir; gezinme rayı, tablo sayfalayıcı ve kenar çubuğu satırı *"mobil form"* değildir — onların tabanı 44px ve zaten sağlıyorlar.

---

## 7 · Bu turda kapatılan üç bulgu

| # | Neydi | Kanıt |
|---|---|---|
| 0.4 | `GET /giris` yoktu; misafir 405 alıyordu | `routes/panel.php` — `customer.login.show`; test *"sends a guest asking for the panel to the sign in screen rather than a 405"* |
| yeni | **Müşteri ikinci faktörü girişte hiç sorulmuyordu.** `hasCompletedMfa()` yalnız `two_factor_confirmed_at`'e bakıyor, yani KAYIT yeterliydi; `customer.approve_quote` MFA kapısı böyle geçiliyordu | `CustomerSessionController::store()` + `CustomerSecondFactorController` + `AnswerCustomerSecondFactorChallenge`; test *"asks for the second factor when the account carries one"* |
| yeni | `CustomerUser::two_factor_recovery_codes` **cast'siz**di — yazarken JSON'a dönüyor, okurken ham string geliyordu. Sütunu hiç kimse okumadığı için hiç patlamamıştı | `app/Models/CustomerUser.php:108-122` |

## 8 · Bu turda açılan bulgu

| Ne | Ölçüm |
|---|---|
| Yedek kod saklama ofis ile müşteri arasında **ayrışıyor** | Ofis: `encrypted:array`. Müşteri: `array` (düz JSON). İkisi de zaten `Hash::make` edilmiş kod tutuyor, yani okunamaz; fark savunma derinliğinde. Müşteri tarafını `encrypted:array` yapmak diskteki satırları çözülemez hale getirir → migration ister |
| Katılımcı olmayan biri magic link takip ederse **404** görür | `ParticipantAreaController` katılımcı olmayana 404 döner (varlığı sır). Bağlantıyı takip eden panel kullanıcısı — kimlik teyidini geçtikten sonra — hiçbir yere düşemiyor: panel `RequireStrongAuth` yüzünden 403, katılımcı alanı 404. Ofiste bunun karşılığı `StrengthenSession` akışı; müşteri tarafında yok |
| Müşteri girişinde **hız sınırı yok** | `SignInCustomerUser` `ThrottlesSignIn` kullanmıyor; ofis tarafı kullanıyor. İkinci faktör adımı da sayaçsız. Denetimde ayrıca kayıtlı değildi |
