---
title: 'Kimlik Doğrulama Akışları'
source: 'https://academia.sh/tr/kurslar/android-servisler/kimlik-dogrulama-akislari'
course: 'Platform Servisleri ve Tümleştirmeler'
language: tr
updated: '2026-10-21T05:02:41+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Biyometrik doğrulama ile hesap tabanlı girişin ne sağladığını ayırır'
    - 'Erişim belirteci ile yenileme belirtecinin görevlerini açıklar'
    - 'Eşzamanlı iki isteğin aynı yenilemeyi iki kez başlatmasını yinelenen olarak ölçer'
    - 'Tek uçuşun sonraki yenilemeyi düşürdüğünü, bunun ezilen değil kayıp olduğunu gösterir'
---

# Kimlik Doğrulama Akışları

Biyometrik doğrulama aygıttaki kapıyı açar, hesap tabanlı giriş ise sunucuya erişim ve yenileme belirteci verir. Ders eşzamanlı isteklerin aynı yenilemeyi iki kez başlatmasının sunucuya kaç kopya olarak ulaştığını ve tek uçuşun bunu nasıl değiştirdiğini ölçer.

Önceki derste istek ağa çıkabiliyordu. Sunucu ise isteği kabul etmeden önce kimin gönderdiğini sorar. Bu ders o sorunun iki yüzünü ele alır: aygıtta kullanıcıyı tanıyan biyometrik doğrulama ve sunucuda kullanıcıyı tanıyan hesap tabanlı giriş. İkisinin kesiştiği yer belirteçlerdir ve sorun orada, belirteç süresi dolduğunda çıkar. Soru kursun sorusudur: **gönderilen bu olay alıcıya kaç kopya olarak ulaştı?** Olay yenileme isteğidir, alıcı sunucudur.

```kotlin
// Arrival.kt — gonderilen bir olayin aliciya kac kopya olarak ulastigini siniflandirir
class Signal(val key: String, val seq: Int)

class Arrival(val label: String, val sent: Signal, received: List<Signal>) {
    val copies: Int = received.count { it.seq == sent.seq }
    val verdict: String = when {
        copies > 1 -> "yinelenen"
        copies == 1 -> "birebir"
        received.any { it.key == sent.key && it.seq > sent.seq } -> "ezilen"
        else -> "kayip"
    }

    fun line() {
        println("$label: anahtar=${sent.key} | kopya=$copies -> $verdict")
    }
}

fun countArrivals(arrivals: List<Arrival>) {
    for (name in listOf("birebir", "yinelenen", "ezilen", "kayip")) {
        println("$name: ${arrivals.count { it.verdict == name }}")
    }
}
```

## İki Doğrulama, İki Belirteç

Biyometrik doğrulama (`BiometricPrompt`) yereldir: parmak izi ya da yüz, kullanıcının aygıtı açmaya yetkili olduğunu gösterir ve sunucuya hiçbir şey söylemez. Uygulama bu sonucu bir kapı olarak kullanır; örneğin yenileme belirtecini yalnız doğrulamadan sonra okur.

```kotlin
// Platform kodu: BiometricPrompt
val prompt = BiometricPrompt(
    activity,
    ContextCompat.getMainExecutor(activity),
    object : BiometricPrompt.AuthenticationCallback() {
        override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
            startRefresh()
        }
    },
)
val info = BiometricPrompt.PromptInfo.Builder()
    .setTitle("Kimliginizi dogrulayin")
    .setNegativeButtonText("Vazgec")
    .build()
prompt.authenticate(info)
```

Hesap tabanlı giriş ise sunucuyla yapılır ve iki belirteç döndürür. **Erişim belirteci** kısa ömürlüdür ve her isteğe eklenir. **Yenileme belirteci** uzun ömürlüdür ve yalnız yeni bir erişim belirteci almak için kullanılır. Erişim belirtecinin süresi dolunca sunucu isteğe yetkisiz yanıt verir; istemci yenileme belirtecini sunucuya gönderir ve yeni bir erişim belirteci alır.

```kotlin
// Platform kodu: Authenticator
class TokenAuthenticator(private val store: TokenStore) : Authenticator {
    override fun authenticate(route: Route?, response: Response): Request? {
        val fresh = store.refresh() ?: return null
        return response.request.newBuilder()
            .header("Authorization", "Bearer $fresh")
            .build()
    }
}
```

İki parça platform kodudur ve burada çıktı, süre ya da sayı iddia etmiyoruz. Model, platformun bu kuralını kurar; model platformun kendisi değildir. Modelde bir çağrı, elindeki erişim belirtecini ve yetkisiz yanıt alırsa göndereceği yenileme olayını taşır. Geçerli belirteci olmayan her çağrı yenileme gönderir.

```kotlin
// Flow.kt — belirteci gecersiz her cagri kendi yenileme olayini gonderir
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

class Call(val name: String, val token: String, val refresh: Signal)

fun refreshAll(valid: String, calls: List<Call>): List<Signal> =
    calls.filter { it.token != valid }.map { it.refresh }

fun singleFlight(valid: String, calls: List<Call>): List<Signal> = runBlocking {
    val mutex = Mutex()
    val inFlight = mutableSetOf<String>()
    val started = mutableListOf<Signal>()
    calls.filter { it.token != valid }.map { call ->
        async {
            mutex.withLock {
                if (inFlight.add(call.refresh.key)) started.add(call.refresh)
            }
        }
    }.awaitAll()
    started
}
```

`refreshAll` korumasızdır: her yetkisiz çağrı yenileme gönderir. `singleFlight` ise bir yenileme sürerken aynı anahtarın ikinci yenilemesini beklemeden düşürür; kontrol ve kayıt bir `Mutex` altında yapılır, böylece iki çağrı aynı anda "henüz yok" görmez.

```kotlin
// Probe.kt — gonderilen yenilemeler icin sunucuya ulasan listeyi alici sayar
fun probe(sent: List<Signal>, received: List<Signal>): List<Arrival> =
    sent.mapIndexed { i, s -> Arrival("olay-${i + 1}", s, received) }
```

## Yarış Neden Doğar

Yenileme yarışı, üç adımın aralarında boşluk bırakmasından doğar: çağrı yetkisiz yanıt alır, yenileme belirtecini okur, yenileme isteğini gönderir. Bu adımlar sıralı görünür, ama bir ekranın birden çok isteği aynı anda yola çıktığında her isteğin kendi sırası vardır. İlk istek yetkisiz yanıtı aldığında yenileme başlar; ikinci istek henüz yeni belirteç gelmeden yetkisiz yanıtı alırsa o da yenileme başlatır. Kimse ötekinin başladığını bilmez.

Korumasız akışın modeli `refreshAll` işlevidir ve bu yarışı tek satırda kurar: geçerli belirteci olmayan her çağrı kendi yenilemesini gönderir. Çağrılar birbirini görmez.

Tek uçuşun (single flight) işi çağrılara birbirini göstermektir. Bir ortak kayıt tutar: bir yenileme sürüyorsa kaydına anahtarı yazılır, sonra gelen çağrı kayıtta anahtarı görürse kendi yenilemesini göndermez ve sonucu bekler. Burada kritik olan, "kayıtta var mı" sorusu ile "kayda yaz" işleminin bölünmemesidir. İkisi ayrı adımlar olursa iki çağrı arada aynı anda "yok" görür ve ikisi de gönderir. Modeldeki `Mutex` bu bölünmeyi önler: bir çağrı kilidi alınca kontrol ve kayıt birlikte yapılır, ötekiler sırada bekler.

Kilidin bir bedeli de vardır: sıradaki çağrılar kilit açılana kadar bekler. Modelde bekleme çok kısadır ve ölçümü etkilemez; gerçek bir uygulamada ağ isteği kilit içinde yapılırsa bekleme uzar, bu yüzden kilit kontrol ve kayıtla sınırlı tutulur, ağ isteği kilit dışında yapılır.

## Her Çağrının Kendi Belirteci Varsa

Üç çağrıdan ikisinin erişim belirteci eskidir ve iki ayrı oturuma aittir. Üçüncünün belirteci geçerlidir ve yenileme göndermez.

```kotlin
// Main.kt — ayri oturumlarin yenilemeleri birer kopya ulasir
fun main() {
    val a = Signal("yenileme.a", 1)
    val b = Signal("yenileme.b", 2)
    val c = Signal("yenileme.c", 3)
    val calls = listOf(Call("ekran-1", "eski", a), Call("ekran-2", "eski", b), Call("ekran-3", "yeni", c))
    val received = refreshAll("yeni", calls)
    val arrivals = probe(listOf(a, b), received)
    arrivals.forEach { it.line() }
    countArrivals(arrivals)
}
```

```text
olay-1: anahtar=yenileme.a | kopya=1 -> birebir
olay-2: anahtar=yenileme.b | kopya=1 -> birebir
birebir: 2
yinelenen: 0
ezilen: 0
kayip: 0
```

Gönderilen iki yenilemenin ikisi birer kopya ulaştı. Geçerli belirteci olan çağrı yenileme göndermediği için ölçümde bir olay değildir.

## Eşzamanlı İki İstek

Bir ekran açılırken birden çok istek aynı anda çıkar. Erişim belirteci eskiyse bu isteklerin hepsi yetkisiz yanıt alır ve hepsi yenileme belirtecini sunucuya gönderir. Aynı oturumun yenileme belirteci aynı olduğu için, sunucuya ulaşan olay aynı olaydır.

```kotlin
// Main.kt — ayni oturumun eszamanli cagrilari yenilemeyi birden cok kez gonderir
fun main() {
    val a = Signal("yenileme.a", 1)
    val b = Signal("yenileme.b", 2)
    val c = Signal("yenileme.c", 3)
    val calls = listOf(
        Call("ekran-1", "eski", a),
        Call("ekran-2", "eski", a),
        Call("ekran-3", "eski", b),
        Call("ekran-4", "eski", c),
        Call("ekran-5", "eski", c),
        Call("ekran-6", "eski", c),
    )
    val received = refreshAll("yeni", calls)
    val arrivals = probe(listOf(a, b, c), received)
    arrivals.forEach { it.line() }
    countArrivals(arrivals)
}
```

```text
olay-1: anahtar=yenileme.a | kopya=2 -> yinelenen
olay-2: anahtar=yenileme.b | kopya=1 -> birebir
olay-3: anahtar=yenileme.c | kopya=3 -> yinelenen
birebir: 1
yinelenen: 2
ezilen: 0
kayip: 0
```

Bu dersin teslimi: yinelenen. Tek bir oturum, aynı yenilemeyi iki ve üç kez göndermiş olabilir; kural kopya sayısını okur. Sunucu yenileme belirtecini her kullanımda yeniliyorsa bu fazlalık sorun doğurabilir, ama bunu ölçüm göstermez; kural yalnız kaç kopya ulaştığını söyler.

Aynı altı çağrı tek uçuşla çalışırsa ne olur? Altı çağrının üçü farklı anahtarı, üçü tekrarı taşır; tek uçuş her anahtardan yalnız birini bırakır.

```kotlin
// Main.kt — tek ucus ayni olayin kopyalarini tek kopyaya indirir
fun main() {
    val a = Signal("yenileme.a", 1)
    val b = Signal("yenileme.b", 2)
    val c = Signal("yenileme.c", 3)
    val calls = listOf(
        Call("ekran-1", "eski", a),
        Call("ekran-2", "eski", a),
        Call("ekran-3", "eski", b),
        Call("ekran-4", "eski", c),
        Call("ekran-5", "eski", c),
        Call("ekran-6", "eski", c),
    )
    val received = singleFlight("yeni", calls)
    val arrivals = probe(listOf(a, b, c), received)
    arrivals.forEach { it.line() }
    countArrivals(arrivals)
}
```

```text
olay-1: anahtar=yenileme.a | kopya=1 -> birebir
olay-2: anahtar=yenileme.b | kopya=1 -> birebir
olay-3: anahtar=yenileme.c | kopya=1 -> birebir
birebir: 3
yinelenen: 0
ezilen: 0
kayip: 0
```

Korumasız akışta yinelenen 2 olan sonuç, tek uçuşla birebir 3 oldu. Bu, tek uçuşun amacıdır: aynı olayın kopyalarını tek kopyaya indirmek. Farklı olayları düşürmesi ise bir sonraki bölümde görülen yan etkisidir.

**GD1.** Model, aynı oturumun çağrılarının aynı yenileme olayını taşıdığını varsayar. Platformda yenileme belirtecinin her kullanımda yenilenmesi, ikinci kopyayı sunucuda başka bir sonuca götürebilir; model bunu kurmaz.

**GD2.** Model, biyometrik doğrulamayı ölçüme katmaz. Doğrulama kapıyı açar; kapının ardındaki yenileme akışı ölçülür.

## Sınırlayıcı Ölçüm

Dört çağrı vardır. İlk ikisi aynı anahtarlı yenilemeyi taşır ama farklı olaylardır: ikincisi daha sonra, daha büyük bir `seq` ile gönderilmiştir. Son ikisi aynı olayı paylaşır. Önce korumasız akışa bakalım.

```kotlin
// Main.kt — korumasiz akista ayni oturumun cagrilari ayri olaylar gonderir
fun main() {
    val a1 = Signal("yenileme.a", 1)
    val a2 = Signal("yenileme.a", 2)
    val b = Signal("yenileme.b", 3)
    val calls = listOf(
        Call("ekran-1", "eski", a1),
        Call("ekran-2", "eski", a2),
        Call("ekran-3", "eski", b),
        Call("ekran-4", "eski", b),
    )
    val received = refreshAll("yeni", calls)
    val arrivals = probe(listOf(a1, a2, b), received)
    arrivals.forEach { it.line() }
    countArrivals(arrivals)
}
```

```text
olay-1: anahtar=yenileme.a | kopya=1 -> birebir
olay-2: anahtar=yenileme.a | kopya=1 -> birebir
olay-3: anahtar=yenileme.b | kopya=2 -> yinelenen
birebir: 2
yinelenen: 1
ezilen: 0
kayip: 0
```

Şimdi aynı dört çağrı tek uçuşla çalışır. İlk yenileme sürerken aynı anahtarın ikincisi düşürülür.

```kotlin
// Main.kt — tek ucus ayni anahtarin sonraki yenilemesini dusurur
fun main() {
    val a1 = Signal("yenileme.a", 1)
    val a2 = Signal("yenileme.a", 2)
    val b = Signal("yenileme.b", 3)
    val calls = listOf(
        Call("ekran-1", "eski", a1),
        Call("ekran-2", "eski", a2),
        Call("ekran-3", "eski", b),
        Call("ekran-4", "eski", b),
    )
    val received = singleFlight("yeni", calls)
    val arrivals = probe(listOf(a1, a2, b), received)
    arrivals.forEach { it.line() }
    countArrivals(arrivals)
}
```

```text
olay-1: anahtar=yenileme.a | kopya=1 -> birebir
olay-2: anahtar=yenileme.a | kopya=0 -> kayip
olay-3: anahtar=yenileme.b | kopya=1 -> birebir
birebir: 2
yinelenen: 0
ezilen: 0
kayip: 1
```

Tek uçuş, aynı olayı paylaşan iki çağrıyı tek kopyaya indirdi: `yenileme.b` yinelenenden birebire geçti. Farklı olay olan ikinci `yenileme.a` ise sunucuya hiç ulaşmadı ve kayıp oldu. Bu olay ezilen değildir. Ezilmek için aynı anahtarla daha büyük bir `seq`in ulaşması gerekir; ulaşan tek kopya `seq` 1'dir ve düşürülen olaydan küçüktür. Kural yalnız sonra gelen olayın yerini almasını görür, önce gelen olayın sonrakinin yerini almasını görmez.

Bunun bedeli uygulama için gerçektir. Düşürülen yenileme, ikinci çağrının kendi belirtecini sunucuya hiç göndermediği anlamına gelir; çağrı ilk yenilemenin sonucunu beklemelidir. Ölçüm bu beklemeyi değil, yalnız hangi olayın ulaştığını gösterir.

**GD3.** Model, tek uçuşu anahtar bazında tutar ve yenilemenin bittiğini hiç işaretlemez; bir yenileme sürerken aynı anahtarın ikincisi her zaman düşer. Platformda yenileme biter ve sonraki yenileme yeniden başlayabilir.

## Alıcı Tarafında Yapılacaklar

Alıcı sunucudur, ama kopyaları azaltmanın büyük kısmı istemcide yapılır. Üç iş vardır.

- **Yenilemeyi tek noktadan başlatmak.** Çağrıların her biri yenileme göndermez; yetkisiz yanıtı alan çağrı ortak bir yenileme işlevine gider ve işlev tek uçuş kuralını uygular. Böylece kural tek yerde yazılır ve ekranlar onu bilmek zorunda kalmaz.
- **Düşürülen çağrıyı beklemeye almak.** Yenilemeyi göndermeyen çağrı vazgeçmez; sürmekte olan yenilemenin sonucunu bekler ve yeni belirteçle kendi isteğini yeniden gönderir. Bu adım modelde yoktur, ama düşürülen olayın kayıp kalması bunu zorunlu kılar.
- **Sunucunun belirteci yenileme davranışını bilmek.** Sunucu yenileme belirtecini her kullanımda değiştiriyorsa ikinci kopya geçersiz belirteçle gelir ve reddedilir; bunun kullanıcıyı çıkış yaptırıp yaptırmayacağı sunucunun kuralıdır ve burada ölçülmez. İstemci açısından tek uçuş, bu soruyu sormamanın en ucuz yoludur.

Yenileme akışının tek bir yerde durması, hata ayıklamayı da kolaylaştırır: kopya sayısı yükseldiğinde bakılacak yer tektir. Kullanıcıya gösterilecek ekran da bu işlevde karar verilir: yenileme başarılıysa kullanıcı hiçbir şey görmez, başarısızsa oturum açma ekranına yönlendirilir. Düşürülen çağrının sonucu yenilemenin sonucuna bağlandığı için çağrı ayrı bir hata ekranı göstermemelidir.

Sunucu tarafında ise yenileme isteğine bir istek kimliği eklemek, kopyaları tekilleştirmeyi mümkün kılar. Bu, teslimi değiştirmez: kopya yine ulaşmıştır, ama sonuç yinelenmez.

## Yaygın Yanılgılar

**Biyometrik doğrulama sunucuyu doğrular.** Doğrulama yereldir; sunucuya kullanıcının kim olduğunu söylemez. Sunucu kimliği belirteçten öğrenir. Bu yüzden modelde doğrulama ölçülmedi, yalnız kapıdan sonraki yenileme ölçüldü.

**Tek uçuş hiçbir olayı kaybettirmez.** Kaybettirir. Aynı anahtarın farklı olayını düşürdü ve kural onu kayıp saydı. Tek uçuşun kazancı yinelenenin birebire inmesi, bedeli düşen olaydır.

**Düşürülen olay ezilendir.** Ezilmek için sonradan aynı anahtarla daha büyük bir `seq` ulaşmalıdır. Düşürülen olay ikincidir ve ondan önceki kopya ulaşmıştır; kural bunu ezilen değil kayıp sayar.

**Aynı belirteçle gelen kopya zararsızdır.** Sunucu belirteci kullanımda yeniliyorsa değildir. Zararsız olması sunucunun kuralına bağlıdır, istemcinin varsayımına değil.

**Her çağrı kendi belirtecini yenilerse sorun yoktur.** Ölçümde her çağrının kendi belirteci olduğunda iki yenileme birebir ulaştı, üçüncü çağrı yenileme göndermedi. Ama bu sonuç, her çağrının ayrı bir yenileme olayı taşıdığı bir akışa aittir. Çağrılar aynı oturumun aynı olayını taşıdığında, eşzamanlı ölçümdeki gibi kopya oluşur.

**Yinelenen sayısı çağrı sayısına eşittir.** Eşit değildir. Kopya sayısı, kaç çağrının aynı olayı taşıdığına bağlıdır. Altı çağrılı ölçümde bir olayın kopyası iki, bir başkasının üç oldu; çağrı sayısı altıydı, kopya sayısı bunlardan hiçbirine eşit değildi.

**Yenilemeyi kullanıcı görmez.** Görmez, ama sonucunu görür. Yenileme başarısız olursa kullanıcı oturumdan atılır; bu ders başarısızlığı ölçmez, yalnız kaç yenilemenin sunucuya ulaştığını sayar.

## Özet

- Bu dersin teslimi: yinelenen. Aynı oturumun eşzamanlı çağrıları aynı yenileme olayını sunucuya birden çok kez gönderir.
- Biyometrik doğrulama aygıttaki kapıdır; hesap tabanlı giriş sunucuya erişim ve yenileme belirteci verir.
- Ayrı oturumların yenilemeleri birer kopya ulaşır: birebir 2. Aynı oturumun iki ve üç çağrısı iki ve üç kopya üretir: yinelenen 2, birebir 1.
- Tek uçuş aynı olayın kopyalarını tek kopyaya indirir: korumasız akışta yinelenen 2 olan altı çağrı, tek uçuşla birebir 3 olur. Ama aynı anahtarın sonraki, farklı olayını düşürür: birebir 2, kayıp 1.
- Düşürülen olay ezilen değildir, çünkü ulaşan kopya ondan küçük `seq` taşır.

## Sonraki Adım

Belirteç yenilendi ve istekler sunucuya ulaşıyor. Bir sonraki soru, uygulamanın kendisinin ne olduğudur: aygıta kurulan paketin kimden geldiği. Sonraki derste yayımlanan sürümün aygıttaki kurulu uygulamayı aynı sertifikayla imzalandığı sürece güncelleyip güncellemediğine bakıyoruz.
