OAuth 2.0 Nedir: Yetkilendirme Çerçevesi Temelleri

Modern web uygulamaları geliştirirken ya da microservice mimarisi kurarken kaçınılmaz olarak karşına çıkan bir kavram var: OAuth 2.0. “Neden şifremi vermeden bu uygulama benim Google hesabıma erişebiliyor?” sorusunun cevabı tam da burada yatıyor. Sistem yöneticisi olarak sadece kullanıcı tarafında değil, servis konfigürasyonu, token yönetimi ve güvenlik açıklarını kapatma konularında da bu protokolü iyi anlamak zorundasın.

OAuth 2.0 Nedir ve Neden Var Oldu

OAuth 2.0 öncesinde uygulamalar başka servislere erişmek için kullanıcının kullanıcı adı ve şifresini doğrudan istiyordu. Twitter’dan tweet çekmek isteyen bir uygulama, senin Twitter şifreni alıp kendi sunucusunda saklıyordu. Bu hem güvensiz hem de yönetimi imkansız bir durumdu.

OAuth 2.0, RFC 6749 standardıyla tanımlanmış bir yetkilendirme çerçevesidir. Kimlik doğrulama protokolü değil, yetkilendirme protokolüdür. Bu ayrım kritik. OAuth 2.0 şunu sorar: “Kim olduğunu değil, ne yapabildiğini kanıtla.” Kullanıcı kimlik bilgilerini üçüncü taraf uygulamalarla paylaşmadan, belirli kaynaklara sınırlı erişim izni vermeni sağlar.

Pratik bir örnekle başlayalım: Slack’i Google Drive’a bağladığında şifreni vermiyorsun. Sadece “Slack’in Drive dosyalarımı okuyabilmesine izin veriyorum” diyorsun. İşte OAuth 2.0 bu izin mekanizmasını standart bir protokolle tanımlar.

Temel Kavramlar ve Roller

OAuth 2.0 ekosisteminde dört temel aktör var:

  • Resource Owner (Kaynak Sahibi): Verinin asıl sahibi olan kullanıcı. Google hesabının sahibi sensin.
  • Client (İstemci): Kaynaklara erişmek isteyen uygulama. Slack bu rolde.
  • Authorization Server (Yetkilendirme Sunucusu): Token’ları veren sunucu. Google’ın accounts.google.com’u bu rolde.
  • Resource Server (Kaynak Sunucusu): Asıl verilerin bulunduğu sunucu. drive.googleapis.com bu rolde.

Bazı sistemlerde Authorization Server ve Resource Server aynı makinede çalışabilir, ama kurumsal ortamlarda bunları ayırmak yaygın bir pratiktir.

Scope Kavramı

Scope, erişim izninin kapsamını tanımlar. “Drive’a erişim” demek yerine ne kadar erişim istediğini belirtiyorsun:

  • https://www.googleapis.com/auth/drive.readonly – Sadece okuma
  • https://www.googleapis.com/auth/drive.file – Sadece oluşturulan dosyalar
  • https://www.googleapis.com/auth/drive – Tam erişim

Sysadmin olarak kendi servislerini konfigüre ederken scope’ları olabildiğince dar tutmak en iyi pratiktir. Least privilege prensibi burada da geçerli.

Grant Type’lar: Akış Türleri

OAuth 2.0’ın farklı kullanım senaryoları için farklı akış türleri mevcut. Hangisini kullanacağını doğru seçmek hem güvenlik hem de kullanılabilirlik açısından kritik.

Authorization Code Grant

En güvenli ve en yaygın kullanılan akış. Server-side uygulamalar için ideal. Akış şöyle işler:

# 1. Adım: Authorization URL oluştur ve kullanıcıyı yönlendir
curl -v "https://accounts.google.com/o/oauth2/auth?
response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourdomain.com/callback
&scope=openid%20email%20profile
&state=random_csrf_token_here"

Kullanıcı izin verdikten sonra authorization server, redirect URI’ye bir code parametresiyle döner. Bu kod kısa ömürlüdür (genellikle 10 dakika).

# 2. Adım: Code ile token exchange (sunucu tarafında yapılır)
curl -X POST https://oauth2.googleapis.com/token 
  -H "Content-Type: application/x-www-form-urlencoded" 
  -d "code=AUTHORIZATION_CODE" 
  -d "client_id=YOUR_CLIENT_ID" 
  -d "client_secret=YOUR_CLIENT_SECRET" 
  -d "redirect_uri=https://yourdomain.com/callback" 
  -d "grant_type=authorization_code"

Bu akışta client_secret hiçbir zaman browser’a gönderilmez. Token exchange sunucu tarafında gerçekleşir.

Client Credentials Grant

Servis-to-servis iletişim için kullanılır. Kullanıcı etkileşimi yok, makine kimlik doğrulaması var. Microservice mimarisinde en çok bu tip görürsün:

# Microservice A, Microservice B'den veri çekiyor
curl -X POST https://auth.internal.company.com/oauth/token 
  -H "Content-Type: application/x-www-form-urlencoded" 
  -d "grant_type=client_credentials" 
  -d "client_id=microservice-a-client-id" 
  -d "client_secret=microservice-a-secret" 
  -d "scope=inventory:read orders:write"

Bu grant type’ı CI/CD pipeline’larında, cron job’larda ve internal API çağrılarında yoğun kullanırsın. Client ID ve secret’ı environment variable olarak saklamayı ihmal etme.

Refresh Token Grant

Access token’lar kısa ömürlü (genellikle 1 saat) olduğundan, kullanıcıyı her seferinde login sayfasına göndermemek için refresh token mekanizması var:

# Access token süresi dolduğunda refresh token ile yenile
curl -X POST https://auth.example.com/oauth/token 
  -H "Content-Type: application/x-www-form-urlencoded" 
  -d "grant_type=refresh_token" 
  -d "refresh_token=REFRESH_TOKEN_HERE" 
  -d "client_id=YOUR_CLIENT_ID" 
  -d "client_secret=YOUR_CLIENT_SECRET"

Refresh token’lar uzun ömürlüdür (günler, haftalar, hatta aylar). Bunları güvenli saklamak kritik. Database’e şifrelenmiş sakla, plain text asla.

Token Tipleri ve Yapısı

OAuth 2.0 token formatını belirtmez, sadece konsepti tanımlar. Ama pratikte iki tip sıklıkla görürsün:

Opaque Token

Anlamsız bir string. Sadece veren sunucu anlayabilir. Her request’te resource server bu token’ı authorization server’a doğrulatmak zorunda:

# Token introspection endpoint (RFC 7662)
curl -X POST https://auth.example.com/oauth/introspect 
  -H "Authorization: Basic BASE64_CLIENT_CREDENTIALS" 
  -d "token=opaque_token_string_here"

# Cevap
# {
#   "active": true,
#   "scope": "read write",
#   "client_id": "app123",
#   "exp": 1640000000
# }

JWT (JSON Web Token)

Self-contained token. Resource server, authorization server’a sormadan token’ı doğrulayabilir. Yoğun trafik ortamlarında tercih edilir ama revocation zorlaşır:

# JWT token'ı decode et (doğrulama yapmadan sadece içeriği görme)
echo "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIiwic2NvcGUiOiJyZWFkIHdyaXRlIiwiZXhwIjoxNjQwMDAwMDAwfQ.signature" | 
  cut -d. -f2 | 
  base64 -d 2>/dev/null | 
  python3 -m json.tool

# JWT imzasını openssl ile doğrula
# Önce public key al
curl https://auth.example.com/.well-known/jwks.json -o jwks.json

# Token header + payload imzasını doğrula
openssl dgst -sha256 -verify public_key.pem 
  -signature signature.bin 
  header_payload.txt

Gerçek Dünya Senaryosu: Internal API Gateway

Şirket içinde bir API gateway kuruyorsun ve tüm microservice’lerin OAuth 2.0 ile korunmasını istiyorsun. Keycloak kullanarak kurulum yapalım:

# Docker ile Keycloak başlat
docker run -d 
  --name keycloak 
  -p 8080:8080 
  -e KEYCLOAK_ADMIN=admin 
  -e KEYCLOAK_ADMIN_PASSWORD=changeme 
  -e KC_DB=postgres 
  -e KC_DB_URL=jdbc:postgresql://postgres:5432/keycloak 
  -e KC_DB_USERNAME=keycloak 
  -e KC_DB_PASSWORD=keycloak_pass 
  quay.io/keycloak/keycloak:latest start-dev

# Keycloak CLI ile realm oluştur
/opt/keycloak/bin/kcadm.sh config credentials 
  --server http://localhost:8080 
  --realm master 
  --user admin 
  --password changeme

# Yeni realm oluştur
/opt/keycloak/bin/kcadm.sh create realms 
  -s realm=company-internal 
  -s enabled=true

# Client oluştur (microservice-a için)
/opt/keycloak/bin/kcadm.sh create clients 
  -r company-internal 
  -s clientId=microservice-a 
  -s secret=microservice-a-secret-2024 
  -s "serviceAccountsEnabled=true" 
  -s "standardFlowEnabled=false" 
  -s protocol=openid-connect

Bu kurulumla microservice-a artık client credentials grant kullanarak token alabilir ve diğer servislere güvenli çağrı yapabilir.

PKCE: Public Client’lar için Güvenlik Katmanı

Mobil uygulamalar ve SPA’lar (Single Page Application) client_secret saklayamaz. Kod kaynak kodu içinde görünür. Bu senaryolar için PKCE (Proof Key for Code Exchange) devreye girer:

# PKCE code_verifier oluştur (client tarafında)
CODE_VERIFIER=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-43)
echo "Code Verifier: $CODE_VERIFIER"

# code_challenge hesapla (SHA256 hash, base64url encoded)
CODE_CHALLENGE=$(echo -n $CODE_VERIFIER | 
  openssl dgst -binary -sha256 | 
  base64 | 
  tr '+/' '-_' | 
  tr -d '=')
echo "Code Challenge: $CODE_CHALLENGE"

# Authorization URL'e ekle
curl -v "https://auth.example.com/oauth/authorize?
response_type=code
&client_id=mobile-app
&redirect_uri=myapp://callback
&code_challenge=${CODE_CHALLENGE}
&code_challenge_method=S256
&scope=openid profile"

# Token exchange sırasında code_verifier gönder
curl -X POST https://auth.example.com/oauth/token 
  -d "grant_type=authorization_code" 
  -d "code=AUTH_CODE" 
  -d "client_id=mobile-app" 
  -d "code_verifier=${CODE_VERIFIER}" 
  -d "redirect_uri=myapp://callback"

Authorization server, code_challenge ile code_verifier’ı karşılaştırır. Araya giren bir saldırgan code’u çalsa bile code_verifier’ı bilmediğinden token alamaz.

Güvenlik Açıkları ve Önlemler

Sysadmin olarak bilmen gereken yaygın OAuth 2.0 güvenlik açıkları var:

State Parameter ile CSRF Koruması

State parametresi olmadan OAuth akışı CSRF saldırılarına açık:

# State parametresi oluştur (her request için unique olmalı)
STATE=$(openssl rand -hex 16)
echo $STATE > /tmp/oauth_state_$SESSION_ID

# Authorization URL'e ekle
AUTH_URL="https://auth.example.com/oauth/authorize?
response_type=code
&client_id=YOUR_CLIENT_ID
&state=${STATE}
&scope=read"

# Callback'te doğrula
# Gelen state değeri session'daki ile eşleşmeli
SAVED_STATE=$(cat /tmp/oauth_state_$SESSION_ID)
if [ "$RETURNED_STATE" != "$SAVED_STATE" ]; then
  echo "CSRF saldırısı tespit edildi! İstek reddedildi."
  exit 1
fi

Token Sızıntısı Tespiti

Log’lardan token sızıntısını önlemek için nginx konfigürasyonu:

# nginx.conf - Authorization header'ı loglamayı engelle
log_format main '$remote_addr - $remote_user [$time_local] '
                '"$request_method $uri $server_protocol" '
                '$status $body_bytes_sent '
                '"$http_referer"';
                # $http_authorization LOGLAMA!

# Access log'da token maskeleme için
# access_log /var/log/nginx/access.log main;

# Ayrıca token'ları query string'de ASLA gönderme
# Kötü: GET /api/data?access_token=TOKEN123
# İyi: GET /api/data (Authorization: Bearer TOKEN123 header'ı ile)

Token Revocation Endpoint

Kullanıcı logout olduğunda veya güvenlik ihlali durumunda token’ları hemen iptal etme:

# RFC 7009 - Token Revocation
curl -X POST https://auth.example.com/oauth/revoke 
  -H "Content-Type: application/x-www-form-urlencoded" 
  -H "Authorization: Basic $(echo -n 'client_id:client_secret' | base64)" 
  -d "token=ACCESS_TOKEN_TO_REVOKE" 
  -d "token_type_hint=access_token"

# Refresh token iptal et
curl -X POST https://auth.example.com/oauth/revoke 
  -H "Content-Type: application/x-www-form-urlencoded" 
  -H "Authorization: Basic $(echo -n 'client_id:client_secret' | base64)" 
  -d "token=REFRESH_TOKEN_TO_REVOKE" 
  -d "token_type_hint=refresh_token"

Well-Known Endpoint ile Discovery

Modern OAuth 2.0 sunucuları .well-known/openid-configuration endpoint’ini expose eder. Bu endpoint’i kullanarak sunucu hakkında her şeyi otomatik keşfedebilirsin:

# OpenID Connect Discovery
curl https://accounts.google.com/.well-known/openid-configuration | python3 -m json.tool

# Keycloak için
curl http://localhost:8080/realms/company-internal/.well-known/openid-configuration | 
  python3 -c "
import json, sys
config = json.load(sys.stdin)
print('Token Endpoint:', config['token_endpoint'])
print('Authorization Endpoint:', config['authorization_endpoint'])
print('JWKS URI:', config['jwks_uri'])
print('Revocation Endpoint:', config.get('revocation_endpoint', 'Desteklenmiyor'))
print('Introspection Endpoint:', config.get('introspection_endpoint', 'Desteklenmiyor'))
"

Bu endpoint sayesinde client konfigürasyonunu hardcode etmeden dinamik olarak alabilirsin. Authorization server’ın URL’i değişse bile client’lar otomatik adapte olur.

Token Saklama Stratejileri

Farklı ortamlar için farklı saklama stratejileri geçerli:

Server-side uygulamalar için:

  • Token’ları Redis veya encrypted database’de sakla
  • Environment variable olarak client secret’ları ilet
  • Hiçbir zaman application log’larına yazma

Konteyner ortamları için:

  • Kubernetes Secret kullan, ConfigMap değil
  • Vault entegrasyonu ile dinamik secret’lar
  • Service Account token’larını sınırlı scope ile oluştur

CI/CD için:

  • GitLab CI’da CI_JOB_TOKEN veya masked variable kullan
  • GitHub Actions’da Secrets kullan
  • Pipeline log’larında token maskelemeyi aktif et
# Kubernetes secret olarak client secret sakla
kubectl create secret generic oauth-client-secret 
  --from-literal=client_id=myapp-client 
  --from-literal=client_secret=supersecretvalue 
  -n production

# Pod'da environment variable olarak referans ver
# env:
#   - name: OAUTH_CLIENT_SECRET
#     valueFrom:
#       secretKeyRef:
#         name: oauth-client-secret
#         key: client_secret

# HashiCorp Vault'tan dinamik secret al
vault kv get -field=client_secret secret/oauth/microservice-a

Monitoring ve Troubleshooting

OAuth akışlarında hata ayıklamak zor olabilir. Bazı pratik ipuçları:

Token endpoint’ine giden istekleri izle, hata kodlarını anla:

  • invalid_client: Client ID veya secret yanlış
  • invalid_grant: Authorization code vekatesi dolmuş ya da kullanılmış
  • invalid_scope: İstenen scope tanımlanmamış
  • unauthorized_client: Bu grant type için yetki yok
# Token'ın süresi dolmuş mu kontrol et
TOKEN="eyJhbGciOiJSUzI1NiJ9..."
EXP=$(echo $TOKEN | cut -d. -f2 | base64 -d 2>/dev/null | python3 -c "
import json, sys
from datetime import datetime
payload = json.load(sys.stdin)
exp = payload.get('exp', 0)
exp_date = datetime.fromtimestamp(exp)
now = datetime.now()
if exp_date > now:
    print(f'Geçerli - {(exp_date - now).seconds} saniye kaldı')
else:
    print(f'SÜRESİ DOLMUŞ - {(now - exp_date).seconds} saniye önce doldu')
")
echo $EXP

# Authorization server loglarını izle (Keycloak için)
docker logs keycloak --follow 2>&1 | grep -E "(ERROR|WARN|token|auth)"

Sonuç

OAuth 2.0 ilk bakışta karmaşık görünebilir, ama temel kavramları yerleştirdiğinde sistem entegrasyonlarının ne kadar güvenli ve yönetilebilir hale geldiğini görürsün. Yetkilendirme akışı seçiminde işin boyutuna göre karar ver: server-side uygulamalarda Authorization Code Grant, servis-to-servis iletişimde Client Credentials, mobil ve SPA’da PKCE destekli Authorization Code Grant kullan.

Sysadmin perspektifinden en kritik noktalar şunlar: Client secret’ları Kubernetes Secret veya Vault gibi güvenli sistemlerde sakla, token’ları asla log’lara düşürme, scope’ları olabildiğince dar tut, state parametresiyle CSRF korumasını unutma ve token revocation endpoint’ini uygulamaya dahil et.

OAuth 2.0 bir protokol olduğundan implementasyon detayları Keycloak, Auth0, Okta, Azure AD gibi provider’lara göre farklılık gösterir. Ama RFC’yi bir kez iyi anladığında herhangi bir provider’ı hızlıca konfigüre edebilir, güvenlik açıklarını tanıyabilir ve sorunları hızla debug edebilirsin. JWT token yönetimi ve OpenID Connect konularına geçmeden önce bu temellerin oturmuş olması kritik öneme sahip.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir