> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flextell.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# PKCE Akışı

> SPA ve native mobil uygulamalar için Proof Key for Code Exchange (PKCE) — client_secret saklamadan güvenli OAuth 2.0 / OIDC.

**PKCE** (Proof Key for Code Exchange, RFC 7636), Authorization Code akışını `client_secret` saklayamayan istemciler için güvenli hale getiren bir uzantıdır. Flextell, PKCE'yi **tüm** OAuth istemcileri için destekler ve **SPA / native mobil** uygulamalar için **zorunlu kılmanızı** öneririz.

## Neden PKCE?

Tarayıcıda çalışan bir SPA veya native bir mobil uygulamanın kaynak kodu son kullanıcının cihazında çalışır. Bu ortama gömdüğünüz `client_secret`'ı **tersine mühendislik** ile çıkarmak önemsiz kolaydır. PKCE, secret yerine **tek seferlik dinamik bir secret** (`code_verifier`) üreterek bu riski ortadan kaldırır.

### Karşıladığı tehdit

Authorization Code **interception**: Kötü niyetli bir uygulama, hedef uygulamanın `redirect_uri` şemasını ele geçirirse (özellikle mobilde `myapp://callback` gibi custom URI'ler), kullanıcının yetkilendirme dönüşünde gelen `code`'u yakalayıp access token'a değişebilir. PKCE, sadece doğru `code_verifier`'a sahip istemcinin code'u token'a çevirebilmesini sağlar.

## Akış

```mermaid theme={null}
sequenceDiagram
    participant App
    participant Browser
    participant Flextell

    App->>App: 1. code_verifier üret (43–128 char random)
    App->>App: 2. code_challenge = BASE64URL(SHA256(code_verifier))
    App->>Browser: 3. /oauth/authorize?...&code_challenge=...&code_challenge_method=S256
    Browser->>Flextell: 4. Kullanıcı giriş yap + onay
    Flextell->>Browser: 5. redirect_uri?code=...
    Browser->>App: 6. code'u App'e aktar
    App->>Flextell: 7. POST /oauth/token (code + code_verifier)
    Flextell->>Flextell: 8. SHA256(code_verifier) == saklanan challenge mı?
    Flextell->>App: 9. access_token (+ id_token, refresh_token)
```

## Adım 1 — code\_verifier ve code\_challenge üret

`code_verifier`, **43–128 karakter** uzunluğunda kriptografik olarak rastgele üretilmiş bir string'tir. Her yetkilendirme akışı için **yeni** bir tane üretilmelidir.

`code_challenge`, `code_verifier`'ın SHA-256 özetinin **base64url** (padding'siz) kodlanmış halidir.

### JavaScript örneği

```js theme={null}
function base64UrlEncode(arrayBuffer) {
  return btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)))
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, "");
}

async function generatePkcePair() {
  const verifierBytes = crypto.getRandomValues(new Uint8Array(32));
  const verifier = base64UrlEncode(verifierBytes);

  const digest = await crypto.subtle.digest(
    "SHA-256",
    new TextEncoder().encode(verifier),
  );
  const challenge = base64UrlEncode(digest);

  return { verifier, challenge };
}
```

### Python örneği

```python theme={null}
import hashlib, secrets, base64

def generate_pkce_pair():
    verifier = secrets.token_urlsafe(64)[:64]
    digest = hashlib.sha256(verifier.encode("ascii")).digest()
    challenge = base64.urlsafe_b64encode(digest).rstrip(b"=").decode("ascii")
    return verifier, challenge
```

<Tip>
  `code_verifier`'ı **kullanıcı tarafında güvenli bir yerde** saklayın: SPA için `sessionStorage` (tek sekme), iOS için Keychain, Android için EncryptedSharedPreferences. Asla log'lamayın.
</Tip>

## Adım 2 — Yetkilendirmeye yönlendir

Standart authorize URL'ine `code_challenge` ve `code_challenge_method=S256` parametrelerini ekleyin:

```
https://dev.flextell.ai/oauth/authorize
  ?response_type=code
  &client_id=SIZIN_CLIENT_ID
  &redirect_uri=myapp%3A%2F%2Fcallback
  &scope=openid%20profile%20email%20appointments%3Aread
  &state=RASTGELE_STRING
  &code_challenge=HESAPLANAN_CHALLENGE
  &code_challenge_method=S256
```

### Ek parametreler

<ParamField query="code_challenge" type="string" required>
  `code_verifier`'ın SHA-256 özeti, base64url (padding'siz).
</ParamField>

<ParamField query="code_challenge_method" type="string" required>
  Her zaman `S256` kullanın. `plain` metodu destekleniyor olsa da zayıftır — production'da kullanmayın.
</ParamField>

## Adım 3 — Token değişimi (verifier ile)

`code`'u aldıktan sonra `/oauth/token` endpoint'ine `code_verifier` parametresi ile istek atın. **`client_secret` göndermeyin** — zaten saklamıyorsunuz:

```bash theme={null}
curl --request POST \
  --url https://dev.flextell.ai/oauth/token \
  --header "Content-Type: application/x-www-form-urlencoded" \
  --data "grant_type=authorization_code" \
  --data "code=AUTHORIZATION_CODE" \
  --data "client_id=SIZIN_CLIENT_ID" \
  --data "redirect_uri=myapp://callback" \
  --data "code_verifier=ADIM_1_DE_URETTIGIN_VERIFIER"
```

### Başarılı yanıt

```json theme={null}
{
  "token_type": "Bearer",
  "expires_in": 1296000,
  "access_token": "eyJ0eXAi...",
  "refresh_token": "def502...",
  "id_token": "eyJhbGci..."
}
```

`openid` scope'u talep ettiyseniz `id_token` da döner. Detay: [OpenID Connect](/authentication/openid-connect).

## Hata yanıtları

| HTTP | `error`           | Sebep                                                                                   |
| ---- | ----------------- | --------------------------------------------------------------------------------------- |
| 400  | `invalid_request` | `code_challenge` gönderildi ama `code_challenge_method` eksik (veya tersi).             |
| 400  | `invalid_grant`   | `code_verifier` → SHA-256 → base64url sonucu, saklanan `code_challenge` ile eşleşmiyor. |
| 400  | `invalid_grant`   | `code` başka bir istemci için üretilmiş (`client_id` uyuşmuyor).                        |

## Güvenlik kontrol listesi

* [x] **Her** akış için yeni `code_verifier` üretin. Verifier'ı yeniden kullanmayın.
* [x] `S256` kullanın; `plain` production'da kabul edilemez.
* [x] `code_verifier`'ı logging sistemine yazmayın — kısa ömürlü olsa bile.
* [x] SPA'da `state` ve `code_verifier`'ı `sessionStorage`'da, callback sayfasında tek seferlik okuyup **hemen silin**.
* [x] Native mobilde güvenli depolama (Keychain / EncryptedSharedPreferences) kullanın.
* [x] `state` parametresini her akışta doğrulayın — PKCE, state kontrolünü **ikame etmez**.

## Confidential client'larda PKCE

Sunucu tabanlı (confidential) istemciler için PKCE **zorunlu değildir** ama ek katman olarak kullanmakta sakınca yoktur. Hem `client_secret` hem `code_verifier` göndermek kabul edilir.

## Hangi kütüphaneler bu akışı hazır yapar?

Çoğu OAuth / OIDC kütüphanesi PKCE'yi otomatik yürütür:

* **Web:** [`oidc-client-ts`](https://github.com/authts/oidc-client-ts), [`@auth0/auth0-spa-js`](https://github.com/auth0/auth0-spa-js) benzeri generic OIDC client'lar.
* **Node.js:** [`openid-client`](https://github.com/panva/node-openid-client).
* **iOS:** [AppAuth-iOS](https://github.com/openid/AppAuth-iOS).
* **Android:** [AppAuth-Android](https://github.com/openid/AppAuth-Android).

Flextell'in [discovery URL](/authentication/openid-connect#discovery)'ini (`https://dev.flextell.ai/.well-known/openid-configuration`) verip `code_challenge_method=S256` ayarını açmanız çoğunlukla yeterlidir.

## Sonraki adımlar

<CardGroup cols={2}>
  <Card title="OpenID Connect" icon="id-badge" href="/authentication/openid-connect">
    id\_token doğrulama, discovery, userinfo.
  </Card>

  <Card title="Authorization Code" icon="arrow-right" href="/authentication/authorization-code">
    Akışın standart (confidential client) hali.
  </Card>
</CardGroup>
