Ana Sayfa / Blog / OAuth 2.0 flow’ları pratikte: authorization code, PKCE, client credentials

OAuth 2.0 flow’ları pratikte: authorization code, PKCE, client credentials

OAuth 2.0 en yaygın authorization protocol. 4 farklı flow, her biri farklı senaryoya. Pratik kullanım rehberi.

OAuth 2.0 authentication değil, authorization protocol. “User X’in izni var bu resource’a erişim için” söylüyor. Modern API ekosisteminin temel taşı. Google Login, Facebook Login, GitHub OAuth, Stripe Connect – hepsi OAuth 2.0.

Çoğu developer OAuth’un sadece bir flow’unu biliyor. Aslında 4 farklı flow var, her biri farklı client type için. Bu yazıda pratik kullanımları anlatacağım.

Core concepts

Actors:
Resource Owner: Kullanıcı
Client: Third-party app (your app)
Authorization Server: Identity provider (Google, Facebook, own)
Resource Server: Protected API

Tokens:
Access token: API call’larda kullanılan, kısa ömür (1 saat)
Refresh token: Yeni access token almak için, uzun ömür (günler/aylar)

Flow 1: Authorization Code (en yaygın)

Web app’ler için. Server-to-server token exchange.

Process:

  1. User “Login with Google” butonuna basıyor
  2. Browser Google’a redirect: authorization_endpoint
  3. User Google’da login oluyor, izin veriyor
  4. Google browser’ı redirect_uri’ye yönlendiriyor with code parameter
  5. Your backend code’u Google’a POST ediyor, access_token + refresh_token alıyor
  6. Token’lar user session’a bağlanıyor

URL example:

https://accounts.google.com/o/oauth2/v2/auth?
    client_id=YOUR_CLIENT_ID&
    redirect_uri=https://yourapp.com/auth/callback&
    response_type=code&
    scope=email profile&
    state=random_csrf_token

Google redirect:

https://yourapp.com/auth/callback?code=AUTH_CODE&state=random_csrf_token

Backend code → token exchange:

response = requests.post('https://oauth2.googleapis.com/token', data={
    'code': code,
    'client_id': CLIENT_ID,
    'client_secret': CLIENT_SECRET,
    'redirect_uri': 'https://yourapp.com/auth/callback',
    'grant_type': 'authorization_code'
})
tokens = response.json()
# {access_token, refresh_token, expires_in}

Security: Client secret backend’de saklıyor. Browser’a sızmıyor.

Flow 2: Authorization Code + PKCE

Mobile/native app için. “Public client” secret saklayamıyor.

PKCE (Proof Key for Code Exchange): Client app random bir secret üretiyor (code_verifier), hash’i (code_challenge) authorization request’te gönderiyor. Token exchange’de code_verifier proof ediyor.

Process:

  1. Client code_verifier üretiyor (random 43-128 char)
  2. code_challenge = SHA256(code_verifier) (base64url encoded)
  3. Authorization URL’de code_challenge
  4. Token exchange’de code_verifier gönderiyor
  5. Server hash’i match ediyor, token veriyor

iOS Swift example:

let verifier = generateRandomString(length: 64)
let challenge = sha256(verifier).base64UrlEncoded

// Authorization URL'ye code_challenge ekle
let authURL = "https://oauth.provider.com/authorize?code_challenge=\(challenge)&code_challenge_method=S256&..."

// Token exchange
let tokenRequest = URLRequest(url: tokenEndpoint)
tokenRequest.httpBody = "code=\(code)&code_verifier=\(verifier)&...".data(using: .utf8)

PKCE mobile OAuth standart. 2019’dan beri RFC’de recommended.

Flow 3: Client Credentials

Server-to-server. User context yok, sadece app kendi adına authorize.

Use case: Backend job user data’sına değil, API service’ine erişmek istiyor. “Your app reports to admin API”.

Process:

  1. Backend client_id + client_secret ile direkt token endpoint’e gidiyor
  2. Token alıyor
  3. API call’larda token kullanıyor
response = requests.post(token_endpoint, data={
    'grant_type': 'client_credentials',
    'client_id': CLIENT_ID,
    'client_secret': CLIENT_SECRET,
    'scope': 'api.read api.write'
})

User involvement yok. Backend script, cron job, admin task’lar için.

Flow 4: Resource Owner Password Credentials

Deprecated. User’ın username/password’ünü app’e verip, app Google’dan token alıyor.

Problem: User’ın password’u app’e dokunuyor. Security anti-pattern.

Sadece: Legacy system migration, first-party app (your company app + your OAuth server) gibi edge case’lerde.

Yeni sistemlerde kesinlikle kullanma.

Token refresh

Access token expire oldu (1 saat). User’ı tekrar authorize etmeden yeni token almak:

response = requests.post(token_endpoint, data={
    'grant_type': 'refresh_token',
    'refresh_token': stored_refresh_token,
    'client_id': CLIENT_ID,
    'client_secret': CLIENT_SECRET  # confidential client
})

New access_token + opsiyonel new refresh_token.

Refresh token rotation: Security için her refresh’te yeni refresh_token ver, eskisini invalidate et. Leaked token kullanım penceresi kısalıyor.

Scopes

Scope = izin scope’u. User neye izin veriyor.

Example Google scopes:
email: email address
profile: name, photo
https://www.googleapis.com/auth/drive.readonly: Drive read
https://www.googleapis.com/auth/drive.file: Drive file write

Principle of least privilege: Sadece ihtiyaç olan scope’u iste. Her ek scope user’ın trust’ını zorluyor.

Dynamic scope request: User akışına göre gerektiği zaman ek scope iste. Google’ın incremental authorization pattern.

Common pitfalls

1. State parameter unutmak.

Authorization URL’de state parameter CSRF koruma. Random token gönderiyorsun, callback’te aynı token geliyor mu check.

state = generate_random_token()
session['oauth_state'] = state
# authorization URL'ye state ekle
# callback'te: assert request.args['state'] == session['oauth_state']

2. Access token’ı storage’da plain text saklamak.

LocalStorage’da, cookie’de (non-httpOnly), kişisel data klasöründe. Encrypted storage kullan.

iOS: Keychain. Android: EncryptedSharedPreferences. Web: HttpOnly secure cookie.

3. Redirect URI validation eksik.

redirect_uri authorization server’da registered list’te olmalı. Wildcard kullanma. Open redirect attack riski.

4. Token expiration handling eksik.

Access token expire olduğunda API call 401 dönüyor. Client’ın refresh token ile yeni token alması, request’i retry etmesi lazım. Middleware’de.

5. Scope elevation silent.

Refresh token ile daha fazla scope isteme. Refresh sadece mevcut scope’u yeniliyor.

OAuth 2.0 vs OpenID Connect

OAuth 2.0 sadece authorization. “Bu app’in resource’a erişimi var.” Kullanıcı kim olduğunu söylemiyor.

OpenID Connect (OIDC) OAuth 2.0 üstüne kurulu. Authentication ekliyor. id_token (JWT) kullanıcı bilgilerini içeriyor.

Modern “Login with X” implementation’ları OIDC kullanıyor.

ID Token example:

{
  "sub": "user_12345",
  "email": "user@example.com",
  "name": "Ali Çınaroğlu",
  "iss": "https://accounts.google.com",
  "aud": "your_client_id",
  "exp": 1700000000
}

Signature verify et, user identity verified.

Implementation tools

OAuth client kendi yazmak uğraştırıcı. Battle-tested libraries kullan:

Backend:
– Python: authlib, requests-oauthlib
– Node.js: passport, openid-client
– Go: golang.org/x/oauth2
– PHP: league/oauth2-client

Frontend:
– JS: oidc-client-ts, react-oidc-context
– iOS: AppAuth-iOS, Apple ASWebAuthenticationSession
– Android: AppAuth-Android

Rolling-your-own OAuth subtle bug’lar. Library kullan.

Security checklist

OAuth implementation’ın production-ready olması için:

  • [ ] HTTPS everywhere (HTTP asla)
  • [ ] State parameter CSRF için
  • [ ] PKCE mobile/native client’larda
  • [ ] Token encrypted storage
  • [ ] Refresh token rotation
  • [ ] Short access token TTL (1 saat max)
  • [ ] Token revocation endpoint’i support
  • [ ] Redirect URI strict matching
  • [ ] Scope minimum
  • [ ] ID token signature verify (OIDC)

Bu checklist’in eksikleri security breach’e açık.

Sonuç

OAuth 2.0 modern API ekosisteminin çimentosu. 4 flow, her biri farklı client type için. Authorization code + PKCE mobile’de, Authorization code web’de, Client credentials server-to-server, Password deprecated.

PKCE, state parameter, refresh rotation, token storage discipline. Implementation tool’ları kullan, kendi yazma. Security checklist’i göz önünde tut.

OAuth karmaşık ama mature. İyi library + spec knowledge ile güvenli auth mümkün.

Bu konuda bir projeniz mi var?

Kısa bir özet bırakın, 24 saat içinde size dönüş yapayım.

İletişime Geç