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:
- User “Login with Google” butonuna basıyor
- Browser Google’a redirect: authorization_endpoint
- User Google’da login oluyor, izin veriyor
- Google browser’ı redirect_uri’ye yönlendiriyor with code parameter
- Your backend code’u Google’a POST ediyor, access_token + refresh_token alıyor
- 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_tokenGoogle redirect:
https://yourapp.com/auth/callback?code=AUTH_CODE&state=random_csrf_tokenBackend 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:
- Client code_verifier üretiyor (random 43-128 char)
- code_challenge = SHA256(code_verifier) (base64url encoded)
- Authorization URL’de code_challenge
- Token exchange’de code_verifier gönderiyor
- 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:
- Backend client_id + client_secret ile direkt token endpoint’e gidiyor
- Token alıyor
- 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.