04.1 — Web Sunucu Taraflı Giriş (Confidential Client + PKCE)
Kimin için: Next.js, Node.js, Python, .NET, Java veya PHP ile geliştirilen sunucu taraflı web uygulamaları.
Ön koşul: Temel Kavramlar ve bir Confidential OAuth İstemcisi.
Tahmini süre: 20 dakika.
Ne Öğreneceksiniz
- Confidential istemciler için standart Authorization Code + PKCE akışını,
- Sunucu tarafında
statevecode_verifieryönetimini, - Token takasını ve şifreli HttpOnly çerez ile güvenli oturum açmayı.
Giriş Akış Mimarisi
Sunucu taraflı web uygulamalarında istemci gizli anahtarı (client_secret) doğrudan sunucuda saklandığı için en güvenli akış türüdür. PKCE eklenmesiyle yetkilendirme kodunun çalınması (code interception) riski tamamen ortadan kaldırılır:
Diyagram (Mermaid)
sequenceDiagram
autonumber
actor User as Kullanıcı
participant Browser as Tarayıcı
participant App as Uygulama Sunucusu (Backend)
participant Talost as TALOST ID (/oauth)
User->>Browser: "Giriş Yap" butonuna tıklar
Browser->>App: GET /api/auth/login
App->>App: state & PKCE (verifier + challenge) üretir
App-->>Browser: Set-Cookie (state, verifier) & 302 Redirect (/oauth/authorize)
Browser->>Talost: GET /oauth/authorize?...
Talost->>User: Giriş & Onay Ekranını sunar
User->>Talost: Kullanıcı adı, şifre ve 2FA girer
Talost-->>Browser: 302 Redirect (redirect_uri?code=XYZ&state=ABC)
Browser->>App: GET /api/auth/callback?code=XYZ&state=ABC
App->>App: Cookie'deki state ile gelen state'i karşılaştırır
App->>Talost: POST /oauth/token (code + verifier + client_secret)
Talost-->>App: 200 OK (access_token, id_token, refresh_token)
App->>Talost: GET /oauth/userinfo (Bearer access_token)
Talost-->>App: 200 OK (sub, email, name, subscription)
App-->>Browser: Set-Cookie (Oturum Çerezi) & 302 /dashboard
Adım Adım Entegrasyon
1. Authorize URL Parametreleri
Kullanıcı giriş butonuna bastığında tarayıcı aşağıdaki adrese yönlendirilmelidir:
- Uç Nokta:
GET {{ISSUER}}/oauth/authorize - Parametreler:
response_type: Zorunlu, sabit değer:codeclient_id: Geliştirici portalından aldığınız istemci ID'si (cli_...)redirect_uri: Kayıtlı geri dönüş adresiniz (https://app.com/api/auth/callback)scope:openid profile email offline_access subscriptionstate: En az 16 baytlık rastgele dize (CSRF koruması)code_challenge:code_verifierdeğerinin SHA-256 özeti (Base64URL formatında)code_challenge_method: Zorunlu, sabit değer:S256
2. Token Takası (`POST /oauth/token`)
Geri dönüş adresine gelen code parametresi, arka planda sunucudan sunucuya takas edilir:
- Uç Nokta:
POST {{ISSUER}}/oauth/token - Headers:
Content-Type: application/x-www-form-urlencoded - Gövde Parametreleri:
grant_type:authorization_codeclient_id:{{CLIENT_ID}}client_secret:{{CLIENT_SECRET}}code: Authorize adımından dönen tek kullanımlık kodredirect_uri: İlk adımda gönderilen adresle birebir aynı olmalıdırcode_verifier: İlk adımda üretilen ham PKCE dizesi
Kod Örnekleri
1. C# (.NET 8 Web API / MVC) Örneği
csharp
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
private readonly IConfiguration _config;
private readonly IHttpClientFactory _httpClientFactory;
public AuthController(IConfiguration config, IHttpClientFactory httpClientFactory)
{
_config = config;
_httpClientFactory = httpClientFactory;
}
[HttpGet("login")]
public IActionResult Login()
{
// 1. PKCE ve State üret
var verifier = GenerateRandomBase64Url(32);
var challenge = ComputeSha256Base64Url(verifier);
var state = GenerateRandomBase64Url(16);
// 2. Cookie'ye yaz
var cookieOpts = new CookieOptions { HttpOnly = true, Secure = true, MaxAge = TimeSpan.FromMinutes(5) };
Response.Cookies.Append("pkce_verifier", verifier, cookieOpts);
Response.Cookies.Append("oauth_state", state, cookieOpts);
// 3. Yönlendir
var issuer = _config["Talost:Issuer"];
var clientId = _config["Talost:ClientId"];
var redirectUri = Uri.EscapeDataString(_config["Talost:RedirectUri"]!);
var authUrl = $"{issuer}/oauth/authorize?response_type=code&client_id={clientId}&redirect_uri={redirectUri}&scope=openid%20profile%20email%20offline_access&state={state}&code_challenge={challenge}&code_challenge_method=S256";
return Redirect(authUrl);
}
[HttpGet("callback")]
public async Task<IActionResult> Callback([FromQuery] string code, [FromQuery] string state)
{
var savedState = Request.Cookies["oauth_state"];
var verifier = Request.Cookies["pkce_verifier"];
if (string.IsNullOrEmpty(state) || state != savedState || string.IsNullOrEmpty(verifier))
{
return BadRequest(new { error = "invalid_state", message = "Doğrulama başarısız." });
}
var client = _httpClientFactory.CreateClient();
var issuer = _config["Talost:Issuer"];
var tokenParams = new Dictionary<string, string>
{
{ "grant_type", "authorization_code" },
{ "client_id", _config["Talost:ClientId"]! },
{ "client_secret", _config["Talost:ClientSecret"]! },
{ "code", code },
{ "redirect_uri", _config["Talost:RedirectUri"]! },
{ "code_verifier", verifier }
};
var response = await client.PostAsync($"{issuer}/oauth/token", new FormUrlEncodedContent(tokenParams));
var content = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
return StatusCode((int)response.StatusCode, content);
}
// Tokenları güvenli oturum çerezi olarak saklayıp panele yönlendir
Response.Cookies.Delete("oauth_state");
Response.Cookies.Delete("pkce_verifier");
Response.Cookies.Append("app_session", content, new CookieOptions { HttpOnly = true, Secure = true });
return Redirect("/dashboard");
}
private static string GenerateRandomBase64Url(int length)
{
var bytes = RandomNumberGenerator.GetBytes(length);
return Convert.ToBase64String(bytes).Replace("+", "-").Replace("/", "_").TrimEnd('=');
}
private static string ComputeSha256Base64Url(string input)
{
var hash = SHA256.HashData(Encoding.ASCII.GetBytes(input));
return Convert.ToBase64String(hash).Replace("+", "-").Replace("/", "_").TrimEnd('=');
}
}
Sık Yapılan Hatalar
invalid_granthatası: Kodun geçerlilik süresi 5 dakikadır ve tek kullanımlıktır. Sayfa iki kez yenilendiğinde aynı kodla ikinci kez token takası denenirse bu hata alınır.redirect_uriuyumsuzluğu: Authorize adımında gönderilenredirect_uriile token takasında gönderilenredirect_uritam olarak aynı dize olmalıdır.
Güvenlik Notları
client_secretanahtarını kesinlikle istemci tarafı tarayıcı kodlarına göndermeyin.- Çerezlerde
HttpOnly,SecureveSameSite=Lax(veyaStrict) bayraklarını mutlaka etkinleştirin.
İlgili Sayfalar
Son doğrulama: 2026-10-04 · Kaynak: _envanter.md, src/app/oauth/authorize/route.ts
Son doğrulama: 2026-10-04 · Kaynak: `_envanter.md`, `src/app/oauth/authorize/route.ts`