Merkezi Authentication & Authorization altyapisi. Kimlik dogrulama uygulamalarin icinde degil, Keycloak IDP uzerinde yapilir. Uc uygulama (web arayuzu, korumali API, servis istemcisi) ayni kimlik altyapisina baglanir.
| Arac | Indirme |
|---|---|
| Docker Desktop | https://www.docker.com/products/docker-desktop/ |
| .NET 8 SDK | https://dotnet.microsoft.com/download/dotnet/8.0 |
git clone https://github.com/SeeyfullahG/KeycloakAuthProject.git
cd KeycloakAuthProject
.\start.ps1start.ps1 sirasiyla sunlari yapar: Docker Desktop kapaliysa acar, Keycloak +
PostgreSQL konteynerlerini baslatir, realm'i otomatik yukler, projeleri derler,
Backend ve Frontend'i ayri pencerelerde calistirir ve tarayiciyi acar.
Ilk calistirmada Docker imajlari indirilecegi icin birkac dakika surebilir.
| Ne | Adres |
|---|---|
| Frontend (buradan basla) | http://localhost:5002 |
| Backend API / Swagger | http://localhost:5000/swagger |
| Keycloak admin konsolu | http://localhost:8080 (admin / admin) |
| Kullanici | Sifre | Roller | Ne gorur |
|---|---|---|---|
sefo_admin |
Admin123! |
admin, user | Admin Paneli gorunur |
sefo_user |
User123! |
user | Admin Paneli gizli, /admin adresi 403 |
Ikisiyle de girip farki gormek, projenin ozunu en hizli anlatan denemedir. API Testi sayfasindaki dort buton 200 / 401 / 403 farkini canli gosterir.
dotnet run --project src\Authtake.ThirdPartyClientKonsol uygulamasi kendi kimligiyle (client_id + client_secret) token alir ve korumali API'yi cagirir. Ortada tarayici, giris ekrani ya da sifre giren bir insan yoktur.
.\test-all.ps1135 otomatik dogrulama testi (6 suite). PART 5 testi token omrunu gecici olarak
kisalttigi icin toplam ~2 dakika surer. Tek tek de calistirilabilir:
.\scripts\verify-part1.ps1 ... .\scripts\verify-security.ps1
Backend ve Frontend pencerelerini kapat, ardindan:
docker compose down # veriler korunur
docker compose down -v # veritabanini da siler (realm dosyadan yeniden yuklenir)| # | Bilesen | Teknoloji | Durum |
|---|---|---|---|
| 1 | Keycloak IDP + PostgreSQL | Docker Compose | ✅ PART 1 |
| 2 | Backend API (Resource Server) | .NET 8 Web API | ✅ PART 2 |
| 3 | Frontend (Web App) | ASP.NET Blazor (statik SSR) | ✅ PART 3 |
| 4 | 3rd Party API (Service Account) | .NET 8 Console | ✅ PART 4 |
| 5 | Token yonetimi (otomatik yenileme) | Frontend + Keycloak | ✅ PART 5 |
docker-compose.yml Keycloak 26 + PostgreSQL 15, kalici volume
.env Kimlik bilgileri / portlar
keycloak/import/authtake-realm.json Realm, 3 client, 2 rol, 2 user (otomatik import)
scripts/verify-part1.ps1 12 realm/rol/akis testi
| Arac | Surum | Konum |
|---|---|---|
| Docker Desktop | 4.85.0 / Engine 29.6.2 | C:\Program Files\Docker\Docker (WSL2 backend) |
| .NET SDK | 8.0.423 | %LOCALAPPDATA%\Microsoft\dotnet (user PATH'e eklendi) |
Yeni bir terminalde dogrula:
docker compose version
dotnet --version
dotnetbulunamazsa terminali kapatip yeniden ac — PATH degisikligi yalnizca yeni acilan sureclerde gorunur.
cd "C:\Users\user\OneDrive\Masaüstü\KeycloakAuthProject"
docker compose up -d
docker compose logs -f keycloak # "Running the server" gorunce hazirAdmin Console: http://localhost:8080 → admin / admin
.\scripts\verify-part1.ps1Script sunlari kontrol eder: realm discovery endpoint, 3rd party client
credentials flow, token icindeki aud + realm_access.roles, iki test
kullanicisinin Authorization Code + PKCE ile girisi ve rol atamalari, ve
password akisinin gercekten kapali oldugu.
Veri kalicligi dogrulandi: docker compose down (volume silmeden) ve
tekrar up sonrasinda realm, kullanicilar ve sertlestirme ayarlari
korunuyor. Elle olusturulan bir iz kullanicisiyla test edildi.
src/Authtake.BackendApi/
Auth/KeycloakAuthenticationExtensions.cs JWT dogrulama, rol mapping, hata yanitlari
Models/ApiModels.cs UserInfo / ApiResponse / ErrorResponse
Extensions/ClaimsPrincipalExtensions.cs Claim -> UserInfo cikarimi
Controllers/{Public,Hello,Admin}Controller.cs
appsettings.json Keycloak authority/realm/audience + CORS
scripts/verify-part2.ps1 29 endpoint + RBAC testi
dotnet run --project src/Authtake.BackendApi --launch-profile httpAPI: http://localhost:5000 — Swagger UI: http://localhost:5000/swagger (Swagger'daki Authorize butonuna Keycloak access token'ini yapistirip endpoint'leri tarayicidan deneyebilirsin.)
| Endpoint | Yetki | 200 | 401 | 403 |
|---|---|---|---|---|
GET /api/public/hello |
Yok | herkes | — | — |
GET /api/hello/secure |
Gecerli token | admin + user | token yok/gecersiz/suresi dolmus | — |
GET /api/admin/data |
admin veya service-api rolu |
sefo_admin, 3rd party servis | token yok | sefo_user |
Basarili (200) — claim'lerden cikarilmis kullanici bilgisiyle:
{
"message": "Admin data retrieved successfully.",
"user": {
"id": "7e5cfdf4-...", "username": "sefo_admin",
"email": "sefo_admin@authtake.local", "name": "Sefo Admin",
"roles": ["admin", "offline_access", "user"],
"clientId": "authtake-frontend", "isServiceAccount": false
},
"data": { "endpoint": "/api/admin/data", "accessedVia": "authorization_code", "records": [...] },
"timestamp": "2026-08-10T18:40:12.3456789+00:00"
}Hata (401 / 403) — standart zarf:
{
"error": "Forbidden",
"message": "You are authenticated but do not have the required role to access this resource.",
"status": 403,
"path": "/api/admin/data",
"timestamp": "2026-08-10T18:40:12.3456789+00:00"
}.\scripts\verify-part2.ps1 # Keycloak + API ayakta olmali- Rol mapping: Keycloak rolleri
realm_access.rolesic ice JSON'unda gelir; ASP.NET Core bunu tanimaz.OnTokenValidatedicinde duzClaimTypes.Roleclaim'lerine aciliyor, boyleceRequireRole("admin")calisiyor. MapInboundClaims = false: JwtBearer varsayilan olarakemail/sub/nameclaim'lerini eski SOAP sema URI'lerine cevirir. Kapatilmazsa OIDC claim adlari okunamaz veuser.emailbos doner.ClockSkew = TimeSpan.Zero: Varsayilan 5 dk tolerans, PART 5'teki token expiry senaryosunu test edilemez hale getiriyordu.auddogrulamasi: Sadeceauthtake-backendaudience'ini tasiyan token'lar kabul edilir (realm'deki audience mapper'lar bunu saglar).- 401 vs 403 ayrimi:
OnChallenge(kimlik yok/gecersiz) veOnForbidden(kimlik var, rol yetersiz) olaylari ayri ayri ele alinip JSON yaziyor.
src/Authtake.Frontend/
Auth/KeycloakOidcExtensions.cs OIDC (Authorization Code + PKCE), rol mapping
Auth/HttpContextAuthenticationStateProvider.cs
Services/BackendApiClient.cs Backend API'ye Bearer token ile istek
Components/Layout/MainLayout.razor Role-based menu, giris/cikis
Components/Pages/Home.razor Acilis, giris butonu
Components/Pages/Profile.razor Token claim'leri + cozulmus payload
Components/Pages/AdminPanel.razor Sadece admin
Components/Pages/ApiTest.razor 200/401/403 canli deneme
Components/Shared/{RedirectToLogin,AccessDenied}.razor
scripts/verify-part3.ps1 40 uctan uca test
Uc bilesenin de ayakta olmasi gerekiyor:
docker compose up -d # Keycloak
dotnet run --project src/Authtake.BackendApi --launch-profile http # :5000
dotnet run --project src/Authtake.Frontend --launch-profile http # :5002Tarayicidan http://localhost:5002 → Giris Yap.
sefo_admin / Admin123! ile sefo_user / User123! arasindaki farki gor.
| Adres | Kim gorebilir | Ne yapar |
|---|---|---|
/ |
Herkes | Giris yapmamissa giris butonu, yapmissa karsilama + kartlar |
/profile |
Giris yapmis | Token claim'leri, roller, gecerlilik suresi, cozulmus payload |
/api-test |
Giris yapmis | Uc endpoint'i canli dene, 200/401/403 farkini gor |
/admin |
admin rolu |
Backend'den korumali veriyi ceker |
/access-denied |
— | Rol yetersizliginde gelinen 403 sayfasi |
.\scripts\verify-part3.ps1Script gercek bir tarayici gibi davranir: login endpoint'ine gider, Keycloak'a yonlendirilir, giris formunu doldurur, donen oturum cookie'siyle korumali sayfalari ister. Iki kullaniciyla da tum akisi ve cikisi test eder.
- Statik SSR secildi (interaktif circuit degil): bilesenler
HttpContext'e erisebildigi icin auth cookie'sindeki access token'a dogrudan ulasiliyor. Bunun bedeli,AuthenticationStateProvider'i kendimizin kaydetmesi (HttpContextAuthenticationStateProvider) — interaktif modda framework bunu kendisi yapar. - Roller access token'dan okunuyor: Keycloak
realm_access.rolesbilgisini varsayilan olarak yalnizca access token'a koyar, id_token'da yoktur. OIDC handler kimligi id_token'dan urettigi icin roller bos kaliyordu; artik access token'in govdesi ayrica ayristiriliyor. AccessDeniedPath: Varsayilani/Account/AccessDenied. Bizde boyle bir sayfa olmadigi icin rol yetersizliginde kullanici 404 goruyordu.- HTTP gelistirme icin cookie ayarlari: OIDC varsayilani, kodu siteler arasi
bir POST ile geri gonderir (
response_mode=form_post) ve correlation/nonce cookie'leriniSameSite=Noneyazar — bu daSecure(HTTPS) zorunlu kilar. HTTP'de cookie dustugu icin donuste 400 aliniyordu.response_mode=query+SameSite=Lax+SecurePolicy=SameAsRequestile cozuldu. Uretimde HTTPS altinda varsayilanlara donulmelidir. - Iki katmanli koruma: Menuden linki gizlemek yalnizca gorsel kolaylik.
/adminadresi elle yazilsa bile once frontend 403 sayfasina duser, ayrica Backend API rolu bagimsiz olarak kendisi dogrular.
Test script'i notu:
verify-part3.ps1HTTP istekleriniInvoke-WebRequestyerineHttpWebRequestile atar ve cookie'leri elle toplar. Iki sebep: (1) Windows PowerShell 5.1, yonlendirme yanitlarindaki cookie'leri oturuma aktarmaz; (2) .NET'inCookieContainer'i, yolu istek yolunun altinda olmayan cookie'yi reddeder ve Secure cookie'yi HTTP'de gondermez — tarayicilarlocalhosticin bu istisnalari tanir. Bunlar test istemcisinin sinirlari, uygulamanin degil.
Kullanici etkilesimi olmadan calisan bir istemci. Tarayici, giris ekrani veya
sifre giren bir insan yoktur: uygulama kendi kimligiyle (client_id +
client_secret) Keycloak'tan token alir ve Backend API'ye o token'la gider.
Gercek hayattaki karsiligi, baska bir sirketin sisteminin bizim API'mize
baglanmasi ya da gece calisan bir entegrasyon isidir.
src/Authtake.ThirdPartyClient/
Services/KeycloakTokenService.cs Client Credentials akisi + token onbellegi
Services/BackendApiClient.cs Bearer token ile Backend cagrisi
Models/TokenModels.cs TokenResponse / TokenClaims / ApiCallResult
Reporting/ConsoleReport.cs Konsol ciktisi ve kontrol sayaci
Program.cs 5 adimlik senaryo
appsettings.json Keycloak + Backend yapilandirmasi
scripts/verify-part4.ps1 19 test
Keycloak ve Backend API ayakta olmali:
docker compose up -d
dotnet run --project src/Authtake.BackendApi --launch-profile http
dotnet run --project src/Authtake.ThirdPartyClient| Adim | Ne yapar |
|---|---|
| 1 | Keycloak'tan grant_type=client_credentials ile token alir, icindeki azp / aud / roller / gecerlilik suresini gosterir |
| 2 | Uc Backend endpoint'ini token ile cagirir, donen durum kodlarini yazar |
| 3 | Admin yanitini ayristirir: isServiceAccount, accessedVia alanlarini dogrular |
| 4 | Negatif kontrol: token gondermeden ayni adrese gider, 401 bekler |
| 5 | Token onbellegini gosterir: ikinci istek Keycloak'a gitmez |
Uygulama kendi kontrollerini sayar ve cikis kodu dondurur (0 = hepsi gecti), boylece otomatik testlerden veya bir CI adimindan calistirilabilir.
| Frontend (PART 3) | 3rd Party (PART 4) | |
|---|---|---|
| Akis | Authorization Code + PKCE | Client Credentials |
| Kullanici | Var, sifresini Keycloak'a girer | Yok |
| Kimlik | Gercek kisi (sefo_admin) |
Service account (service-account-authtake-3rdparty) |
| Client tipi | Public (secret saklayamaz) | Confidential (client_secret sunucuda) |
| Refresh token | Var | Yok — token dolunca ayni sekilde yenisi istenir |
| Backend'in gordugu | accessedVia: authorization_code |
accessedVia: client_credentials |
Dikkat: Backend API tarafinda hicbir degisiklik yapilmadi. Ayni endpoint,
ayni rol kontrolu, ayni Authorization: Bearer basligi. Backend'i istegin bir
insandan mi yoksa bir servisten mi geldigi ilgilendirmiyor — yalnizca token'in
gecerli olup olmadigi ve icindeki roller. Merkezi kimlik dogrulamanin en somut
faydasi bu.
.\scripts\verify-part4.ps1Script uygulamayi calistirip cikis kodunu ve ciktisini kontrol eder, ardindan
iki bagimsiz test yapar: yanlis client_secret ile uygulamanin temiz bir
hatayla durdugunu (ortam degiskeni ile ezerek), ve uygulamadan bagimsiz olarak
akisin kendisinin de calistigini.
- Token onbellegi: Her istek icin yeni token almak gereksiz. Token, suresi dolmadan 30 saniye oncesine kadar yeniden kullanilir; bu pay, istek yoldayken token'in gecersizlesmesini onler.
ContentRootPath = AppContext.BaseDirectory:Host.CreateApplicationBuilderyapilandirmayi varsayilan olarak calisma dizininde arar.dotnet run --project ...cozum kokunden calistirildigindaappsettings.jsonbulunamaz; icerik koku uygulamanin kendi klasorune sabitlendi.audclaim'i tek metin de olabilir, dizi de: JWT standardi ikisine de izin verir, ayristirici iki durumu da karsilar.- Cikis kodu: Uygulama basarisiz kontrol varsa 1 doner. Bir demo programini test edilebilir hale getiren en ucuz yontem.
Access token 15 dakika yasar. PART 5'ten once bunun sonucu su idi: 15 dakika sonra kullanici arayuzde hala "giris yapmis" gorunuyor (oturum cookie'si 7 gun gecerli) ama Backend cagrilari 401 donmeye basliyordu.
Artik uygulama, token'in omru dolmadan 1 dakika once refresh token ile sessizce yenisini aliyor. Kullanici bunu hic fark etmiyor.
src/Authtake.Frontend/
Auth/TokenRefreshService.cs Yenileme cagrisi + paralel istek koordinasyonu
Auth/KeycloakOidcExtensions.cs OnValidatePrincipal icinde yenileme tetigi
scripts/verify-part5.ps1 16 test
Yenileme, cookie her dogrulandiginda (yani her istekte) kontrol edilir:
Istek gelir
-> Cookie cozulur, icindeki 'expires_at' okunur
-> Suresi dolmaya 1 dk'dan az mi kaldi?
Hayir -> devam
Evet -> refresh token ile Keycloak'tan yeni token al
Basarili -> yeni token'lari cookie'ye yaz, devam
Basarisiz -> oturumu kapat, giris ekranina gonder
Bu, CookieAuthenticationEvents.OnValidatePrincipal uzerine kurulu; ayri bir
zamanlayici ya da arka plan isi yok. Yenilenen token'lar StoreTokens ile
cookie'ye yazilir ve ShouldRenew = true ile cookie yeniden gonderilir.
Realm'de refresh token rotasyonu acik: her yenilemede yeni bir refresh token verilir ve eskisi aninda gecersizlesir. Bir sayfa ayni anda birkac istek atarsa hepsi ellerindeki ayni eski refresh token ile yenilemeye kalkar; ilki basarili olur, digerleri reddedilir ve kullanici bosuna disari atilir.
Iki onlem birlikte kullaniliyor:
| Onlem | Ne yapar |
|---|---|
Kullanici basina kilit (SemaphoreSlim) |
Ayni anda yalnizca bir yenileme calisir |
| 30 saniyelik onbellek | Yarisi kaybeden istek, kazananin aldigi token'lari onbellekten okur; tekrar denemez |
Onbellek anahtari eski refresh token'dir: ayni eski token'la gelen her istek ayni yeni token setine ulasir.
.\scripts\verify-part5.ps1Test 15 dakika beklemek yerine realm'in accessTokenLifespan degerini admin API
ile gecici olarak 40 saniyeye dusurur, token'in gercekten eskimesini bekler,
sonra ayari geri alir. Dogruladiklari:
| Kontrol | Beklenen |
|---|---|
| Bekleme sonrasi profil sayfasi | Gecerlilik sonu ILERI tasinmis |
| Kullanici durumu | Hala giris yapmis (disari atilmamis) |
/api/hello/secure ve /api/admin/data |
200 OK — yenilenen token Backend'de gecerli |
| Keycloak'ta oturum sonlandirilirsa | Yenileme reddedilir, kullanici giris ekranina yonlendirilir |
| Test sonrasi realm | accessTokenLifespan eski degerine doner |
Son madde onemli: ayar geri alinmazsa hem uygulama hem diger testler
kisaltilmis token omruyle calismaya devam ederdi. Temizlik finally blogunda
ve admin token'i orada tazeleniyor — master realm'de admin token'inin
varsayilan omru 60 saniye, test ise iki kez ~50 saniye bekliyor.
scripts/verify-security.ps1 (19 test) su saldiri senaryolarini calistirir ve
hepsinin reddedildigini dogrular:
| Senaryo | Beklenen |
|---|---|
| Imzasi bozulmus token | 401 |
Rolleri degistirilmis token (admin, superuser, root eklenmis) |
401 |
alg=none — "imza kontrolu yapma" saldirisi |
401 |
| Baska realm'den alinmis, kendi icinde gecerli token | 401 |
id_token'i access token yerine kullanmak |
401 |
| Rolu yetersiz kullanici | 403 (401 degil) |
| Public client'ta password akisi | reddedilir |
| Public client'ta client_credentials | reddedilir |
| Eski refresh token'i ikinci kez kullanmak | reddedilir (rotasyon) |
| Ard arda yanlis sifre | hesap gecici kilitlenir |
Test, kilitledigi hesabi sonunda admin API ile tekrar acar; aksi halde diger script'ler 15 dakika calisamazdi.
| Karar | Gerekce |
|---|---|
| Public client'ta password (Direct Access Grants) akisi kapali | Acik kalirsa PKCE'nin sagladigi korumayi atlayan bir yan kapi olusur. Testler de artik gercek Authorization Code + PKCE akisini kullanir (scripts/lib/AuthFlow.ps1) |
| Brute force korumasi acik | failureFactor=5, kademeli bekleme, en fazla 15 dk kilit. Password akisiyla birlikte kapali olmasi ciddi bir riskti |
Refresh token rotasyonu (revokeRefreshToken=true) |
Her yenilemede eski token gecersizlesir; calinan bir refresh token'in omru tek kullanimla sinirlanir |
Servis hesabina admin yerine service-api rolu |
En az yetki ilkesi: admin rolune ileride eklenecek yetkiler 3rd party servise otomatik olarak gecmez. Insan ve makine yetkileri ayri kalir |
| PostgreSQL portu disariya acilmadi | Veritabanina yalnizca Keycloak konteyneri erisir |
Cikis yapildiginda refresh token gercekten iptal edilir, ancak elde bulunan access token suresi dolana kadar (en fazla 15 dk) gecerli kalmaya devam eder.
Bu bir acik degil, JWT'nin dogasidir: Backend her istekte Keycloak'a "bu token
hala gecerli mi" diye sormaz, imzaya ve exp alanina bakar. Aninda iptal
gerekiyorsa token introspection (her istekte IDP'ye sorma) gerekir — bunun
bedeli her cagrida ek bir ag turudur. Kisa omur bu riski sinirlar.
Davranis sessizce degisirse fark edelim diye bu da teste baglandi.
Giris endpoint'i, giris sonrasi donulecek adresi kullanicidan aliyordu
(/authentication/login?returnUrl=...). Kontrol yalnizca "goreli adres mi"
diye bakiyordu:
Uri.IsWellFormedUriString(returnUrl, UriKind.Relative) // yetersiz//kotusite.example RFC 3986'ya gore gecerli bir goreli adrestir
(network-path reference), ama tarayici onu http://kotusite.example olarak
yorumlar. Test edildi ve dogrulandi: kullanici gercek Keycloak sayfasinda giris
yaptiktan sonra baska bir siteye dusuruluyordu.
Sömürü senaryosu: saldirgan kurbanina .../authentication/login?returnUrl=//saldirgan.example
linkini gonderir. Kurban gercek giris ekranini gorur, dogru sifresini girer,
ve saldirganin sayfasinda son bulur; orada "oturumunuz dustu, tekrar girin"
diyen sahte bir ekran sifre toplayabilir. Acik yonlendirmenin klasik kullanimi
budur: kimlik avini gercek akisin guvenilirligiyle guclendirmek.
Cozum, ASP.NET Core'un kendi Url.IsLocalUrl mantigi: adres tek bir / ile
baslamali ve ardindan / veya \ gelmemeli. verify-part3.ps1 bes varyanti
(mutlak adres, //, /\, \\, bosluklu) kalici olarak test eder.
authtake-frontend icin kayitli adresler joker karakterliydi
(http://localhost:5002/*) ve kullanilmayan girdiler iceriyordu (React icin
:3000, :5001). Joker redirect URI, ayni origin'de bir acik yonlendirme
bulunmasi halinde yetki kodunun calinmasina zemin hazirlar — yukaridaki
acik tam da bu origin'deydi.
| Once | Sonra | |
|---|---|---|
| redirectUris | 4 adet, joker karakterli | http://localhost:5002/signin-oidc |
| webOrigins | 4 adet | http://localhost:5002 |
| post logout | 4 adet, joker karakterli | http://localhost:5002/signout-callback-oidc |
Daraltirken bir hata yapildi ve testler yakaladi: post-logout adresi olarak
http://localhost:5002/ kaydedilmisti, oysa OIDC handler cikista
SignedOutCallbackPath degerini gonderiyor. Keycloak adresi tanimayinca
400 donuyor, Keycloak oturumu kapanmiyor ve kullanici bir sonraki istekte
sessizce yeniden iceri aliniyordu. Dogru adres kaydedildi.
RP-initiated logout, Keycloak'a kimin cikis yaptigini soyleyen id_token'i
id_token_hint parametresiyle gonderir. Keycloak 19+ bu parametreyi zorunlu
tutar: post_logout_redirect_uri varken id_token_hint yoksa istegi
400 "Missing parameters: id_token_hint" ile reddeder.
/authentication/logout her cagrildiginda kosulsuz olarak Keycloak'a cikis
istegi gonderiyordu. Ortada oturum yoksa gonderilecek bir id_token da olmadigi
icin kullanici bu hata sayfasini goruyordu.
Sanildigindan kolay olusan bir durumdur:
- Cikis yaptiktan sonra geri tusuna basmak (ayni adres yeniden cagrilir)
- Cikis adresini yer imine eklemek
- Oturum dusmusken "Cikis Yap"a tiklamak
Cozum: cikis endpoint'i once saklanan id_token'a bakar. Yoksa Keycloak'a hic
gitmez — geride kalmis yerel cookie'yi temizleyip ana sayfaya doner.
Oturum varken davranis degismedi: tek cikis (single logout) calismaya devam
ediyor. Iki senaryo da verify-part3.ps1 icinde kalici teste baglandi.
/authentication/logout bir GET endpoint'idir; saldirganin sayfasindaki bir
<img src="..."> etiketi kullaniciyi oturumdan dusurebilir (CSRF). Etkisi
yalnizca rahatsizlik verir — veri sizmaz, kullanici tekrar giris yapabilir.
OIDC'nin RP-initiated logout akisi da zaten tarayici yonlendirmesi uzerine
kuruludur. Uretimde POST + antiforgery token tercih edilmelidir.
/profile sayfasi access token'in kendisini ve cozulmus govdesini ekrana basar.
Bu, projenin ogretici amaci icin bilincli bir tercihtir — token'in ne
oldugunu gormek konuyu anlamanin en hizli yolu.
Gercek bir uygulamada yapilmamalidir: oturum cookie'si HttpOnly isaretlidir,
yani JavaScript'in token'a erismesini engelleriz. Token'i HTML'e basmak bu
korumayi anlamsiz kilar — sayfada bir XSS acigi olsa token dogrudan
okunabilirdi. Uretimde bu kart kaldirilmalidir.
- 500 yanitlari da 401/403 ile ayni hata zarfini kullanir
(
error/message/status/path/timestamp). Ic detay sizdirilmez; ayrinti yalnizca loglara yazilir. - Keycloak erisilemezse Backend API imza anahtarlarini alamaz ve korumali endpoint'ler 401 doner — yani sistem acik kalmaz, kapanir (fail-closed). Dogrulanmis davranistir.
Not: 500 zarfi kod icinde yerinde, ancak otomatik testlerde dogrulanmiyor. Gercek bir 500 tetiklemek icin uygulamaya kalici bir "hata uret" endpoint'i eklemek gerekirdi; bu, uretim koduna test amacli bir acik eklemek anlamina geldigi icin tercih edilmedi.
Asagidakiler bilerek gelistirme ayarinda birakildi. Yerel calistirmayi kolaylastirirlar; uretimde kabul edilemezler.
| # | Konu | Su anki durum | Uretimde |
|---|---|---|---|
| 1 | TLS | Her sey HTTP: sslRequired: none, RequireHttpsMetadata: false, cookie'ler SameAsRequest |
HTTPS zorunlu; sslRequired: external, cookie'ler Always, OIDC varsayilanlarina (form_post + SameSite=None) donulur |
| 2 | Gizli bilgiler | Client secret'lar, admin/admin ve test sifreleri repoda acikta |
Secret yoneticisi (Vault, Key Vault, Docker secrets); realm import'tan cikarilir |
| 3 | Keycloak modu | start-dev, bellek ici cache, admin konsolu varsayilan sifreyle |
start + uretim veritabani + guclu admin sifresi + admin konsoluna ag kisitlamasi |
| 4 | Rate limiting | Backend API'de yok | Reverse proxy veya AddRateLimiter ile istek sinirlama |
| 5 | Token omru | Access 15 dk, SSO oturumu 7 gun | Ihtiyaca gore kisaltilir; 7 gun cogu senaryo icin uzun |
Realm: authtake — Access token 15 dk (900s), SSO session 7 gun (604800s)
| Client ID | Tip | Flow | Secret |
|---|---|---|---|
authtake-frontend |
Public | Authorization Code + PKCE (S256) | — |
authtake-backend |
Confidential | Yok — sadece JWT dogrular | backend-secret-change-me-2024 |
authtake-3rdparty |
Confidential | Client Credentials (service account) | thirdparty-secret-change-me-2024 |
authtake-frontend redirect URI'lari hem React (:3000) hem .NET
(:5001 / :5002) icin onceden tanimli — PART 3'te frontend secimi
degistiginde realm'i tekrar duzenlemeye gerek yok.
authtake-frontend ve authtake-3rdparty uzerinde audience mapper var:
uretilen access token'larin aud claim'i authtake-backend icerir, boylece
Backend API audience dogrulamasi yapabilir.
authtake-backend icin tum flow'lar kapali (standard/implicit/direct/service
account) — bu, Keycloak 26'da "bearer-only" resource server'in karsiligidir.
| Kullanici | Sifre | Roller |
|---|---|---|
sefo_admin |
Admin123! |
admin, user |
sefo_user |
User123! |
user |
service-account-authtake-3rdparty |
— (client secret) | service-api |
$r = Invoke-RestMethod -Method Post `
-Uri "http://localhost:8080/realms/authtake/protocol/openid-connect/token" `
-ContentType 'application/x-www-form-urlencoded' `
-Body @{
grant_type = 'client_credentials'
client_id = 'authtake-3rdparty'
client_secret = 'thirdparty-secret-change-me-2024'
}
$r.access_tokenToken'i jwt.io'ya yapistirarak realm_access.roles ve aud claim'lerini gorebilirsin.
- Veriler
postgres_datavekeycloak_datanamed volume'larinda kalicidir.docker compose downveriyi silmez;docker compose down -vsiler. - Realm import sadece realm yoksa calisir. Realm JSON'i degistirip yeniden
yuklemek icin:
docker compose down -v; docker compose up -d - Realm JSON'da gecersiz bir alan varsa Keycloak acilista crash eder ama
container
runninggorunur (restart loop).docker compose psyaniltici olabilir; sorun cikarsadocker compose logs keycloak | Select-String ERROR. Keycloak 26 su alanlari kabul etmez: clientpostLogoutRedirectUris(yerineattributes["post.logout.redirect.uris"]), realmdefaultRoles. start-devmodu vesslRequired: noneyalnizca gelistirme icindir. Production'dastart+ HTTPS + gercek secret'lar kullanilmalidir.