02 — Hızlı Başlangıç (15 Dakika)
Kimin için: 15 dakika içinde sıfırdan çalışan bir TALOST ID giriş akışı kurmak isteyen geliştiriciler.
Ön koşul: Node.js 18+ veya Python 3.10+, temel web sunucusu bilgisi.
Tahmini süre: 15 dakika.
Ne Öğreneceksiniz
- Geliştirici Portalından bir DEVELOPMENT istemcisi almayı,
- PKCE (Proof Key for Code Exchange) ile güvenli giriş yönlendirmesi oluşturmayı,
- Yetkilendirme kodunu (
code) token ile takas etmeyi ve kullanıcı profiline erişmeyi.
Adım Adım Kurulum
Adım 1: İstemci Bilgilerinizi Alın
- TALOST ID Geliştirici Portalı'na gidin ve uygulamanızı seçin.
- Sol menüden Ortamlar sekmesine tıklayın.
- DEVELOPMENT sütununda bir OAuth istemcisi oluşturun veya mevcut istemcinizi seçin.
- Yönlendirme adresi (Redirect URI) olarak yerel sunucu adresinizi ekleyin:
http://localhost:3000/api/auth/callback client_idveclient_secretdeğerlerini kopyalayın.
Adım 2: Ortam Değişkenlerini Tanımlayın
Uygulama projenizin kök dizininde .env.local dosyası oluşturun:
env
TALOST_ISSUER=http://127.0.0.1:3000
TALOST_CLIENT_ID=cli_dev_xxxxxxxxxxxx
TALOST_CLIENT_SECRET=sec_dev_xxxxxxxxxxxxxxxxxxxxxxxx
TALOST_REDIRECT_URI=http://localhost:3000/api/auth/callback
> Canlı ortamda `TALOST_ISSUER` değeri `https://id.talost.com` olacaktır.
Kod Örnekleri
> **Hazır Başlangıç Şablonu:** Sıfırdan kod yazmak yerine doğrudan çalıştırabileceğiniz, oturum şifreleme ve webhook altyapısı hazır Next.js 15 şablonunu kullanabilirsiniz: [templates/talost-app-starter-nextjs/](../../templates/talost-app-starter-nextjs) ve [18. SDK ve Starter Şablonları](18-sdk-ve-starter.md).
1. Next.js App Router (TypeScript)
Aşağıdaki iki dosya ile tam çalışan ve PKCE korumalı bir OIDC giriş akışı elde edersiniz:
A. Giriş Başlatma: `app/api/auth/login/route.ts`
typescript
import { NextResponse } from "next/server";
import crypto from "crypto";
import { cookies } from "next/headers";
function base64UrlEncode(str: Buffer): string {
return str.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
}
export async function GET() {
// 1. PKCE Verifier ve Challenge üret
const verifier = base64UrlEncode(crypto.randomBytes(32));
const challenge = base64UrlEncode(crypto.createHash("sha256").update(verifier).digest());
const state = base64UrlEncode(crypto.randomBytes(16));
// 2. Cookie içinde sakla (5 dakika geçerli)
const cookieStore = await cookies();
cookieStore.set("talost_pkce_verifier", verifier, { httpOnly: true, secure: process.env.NODE_ENV === "production", maxAge: 300 });
cookieStore.set("talost_oauth_state", state, { httpOnly: true, secure: process.env.NODE_ENV === "production", maxAge: 300 });
// 3. TALOST ID Authorize uç noktasına yönlendir
const authUrl = new URL(`${process.env.TALOST_ISSUER}/oauth/authorize`);
authUrl.searchParams.set("response_type", "code");
authUrl.searchParams.set("client_id", process.env.TALOST_CLIENT_ID!);
authUrl.searchParams.set("redirect_uri", process.env.TALOST_REDIRECT_URI!);
authUrl.searchParams.set("scope", "openid profile email offline_access subscription");
authUrl.searchParams.set("state", state);
authUrl.searchParams.set("code_challenge", challenge);
authUrl.searchParams.set("code_challenge_method", "S256");
return NextResponse.redirect(authUrl.toString());
}
B. Geri Dönüş ve Token Takası: `app/api/auth/callback/route.ts`
typescript
import { NextRequest, NextResponse } from "next/server";
import { cookies } from "next/headers";
export async function GET(req: NextRequest) {
const { searchParams } = new URL(req.url);
const code = searchParams.get("code");
const state = searchParams.get("state");
const error = searchParams.get("error");
if (error) {
return NextResponse.json({ error, description: searchParams.get("error_description") }, { status: 400 });
}
const cookieStore = await cookies();
const savedState = cookieStore.get("talost_oauth_state")?.value;
const verifier = cookieStore.get("talost_pkce_verifier")?.value;
// State ve Verifier kontrolü (CSRF koruması)
if (!state || !savedState || state !== savedState || !verifier || !code) {
return NextResponse.json({ error: "invalid_state", message: "Oturum veya PKCE doğrulaması başarısız." }, { status: 400 });
}
// Token Takası (POST /oauth/token)
const tokenRes = await fetch(`${process.env.TALOST_ISSUER}/oauth/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "authorization_code",
client_id: process.env.TALOST_CLIENT_ID!,
client_secret: process.env.TALOST_CLIENT_SECRET!,
code,
redirect_uri: process.env.TALOST_REDIRECT_URI!,
code_verifier: verifier,
}),
});
const tokens = await tokenRes.json();
if (!tokenRes.ok) {
return NextResponse.json(tokens, { status: tokenRes.status });
}
// Kullanıcı Bilgisini Çek (GET /oauth/userinfo)
const userRes = await fetch(`${process.env.TALOST_ISSUER}/oauth/userinfo`, {
headers: { Authorization: `Bearer ${tokens.access_token}` },
});
const user = await userRes.json();
// Oturumu tamamla (Token ve kullanıcı çerezini saklayıp ana sayfaya yönlendir)
cookieStore.delete("talost_pkce_verifier");
cookieStore.delete("talost_oauth_state");
cookieStore.set("session_user", JSON.stringify(user), { httpOnly: true, secure: true, path: "/" });
return NextResponse.redirect(new URL("/dashboard", req.url));
}
2. Python (FastAPI) Örneği
python
import hashlib
import base64
import secrets
from fastapi import FastAPI, Request, Response, HTTPException
from fastapi.responses import RedirectResponse
import httpx
import os
app = FastAPI()
def base64url(data: bytes) -> str:
return base64.urlsafe_b64encode(data).decode('utf-8').rstrip('=')
@app.get("/api/auth/login")
def login(response: Response):
verifier = base64url(secrets.token_bytes(32))
challenge = base64url(hashlib.sha256(verifier.encode('ascii')).digest())
state = secrets.token_hex(16)
response.set_cookie(key="pkce_verifier", value=verifier, httponly=True, max_age=300)
response.set_cookie(key="oauth_state", value=state, httponly=True, max_age=300)
issuer = os.getenv("TALOST_ISSUER", "http://127.0.0.1:3000")
client_id = os.getenv("TALOST_CLIENT_ID")
redirect_uri = os.getenv("TALOST_REDIRECT_URI")
auth_url = f"{issuer}/oauth/authorize?response_type=code&client_id={client_id}&redirect_uri={redirect_uri}&scope=openid profile email&state={state}&code_challenge={challenge}&code_challenge_method=S256"
return RedirectResponse(auth_url)
@app.get("/api/auth/callback")
async def callback(request: Request, code: str, state: str):
saved_state = request.cookies.get("oauth_state")
verifier = request.cookies.get("pkce_verifier")
if not saved_state or state != saved_state or not verifier:
raise HTTPException(status_code=400, detail="CSRF veya PKCE doğrulaması başarısız.")
issuer = os.getenv("TALOST_ISSUER", "http://127.0.0.1:3000")
async with httpx.AsyncClient() as client:
token_res = await client.post(
f"{issuer}/oauth/token",
data={
"grant_type": "authorization_code",
"client_id": os.getenv("TALOST_CLIENT_ID"),
"client_secret": os.getenv("TALOST_CLIENT_SECRET"),
"code": code,
"redirect_uri": os.getenv("TALOST_REDIRECT_URI"),
"code_verifier": verifier,
}
)
tokens = token_res.json()
if token_res.status_code != 200:
raise HTTPException(status_code=token_res.status_code, detail=tokens)
user_res = await client.get(
f"{issuer}/oauth/userinfo",
headers={"Authorization": f"Bearer {tokens['access_token']}"}
)
user = user_res.json()
return {"message": "Giriş başarılı", "user": user}
Sık Karşılaşılan Hatalar
invalid_grant/ PKCE mismatch:code_verifier'ın Base64URL algoritmasında+,/ve=karakterlerinin temizlendiğinden emin olun.unauthorized_client: DEV ortamındaki istemciye yalnızca uygulamanın ekip üyeleri giriş yapabilir. Ekipte olmayan bir hesapla test yapıyorsanız kullanıcıyı Ekip sekmesinden ekleyin.- Redirect URI Mismatch: Portalde kayıtlı URI ile kodda gönderilen URI (port numarası ve sondaki
/dahil) harfi harfine aynı olmalıdır.
Güvenlik Notları
- PKCE (Proof Key for Code Exchange) zorunludur.
code_challenge_method=S256olmadan yapılan istekler reddedilir. stateparametresini mutlaka rastgele üretin ve geri dönüşte doğrulayın (CSRF koruması).client_secretdeğerini asla istemci tarafına (tarayıcıya) aktarmayın.
İlgili Sayfalar
Son doğrulama: 2026-10-04 · Kaynak: _envanter.md, src/app/oauth/authorize/route.ts, src/app/oauth/token/route.ts
Son doğrulama: 2026-10-04 · Kaynak: `_envanter.md`, `src/app/oauth/authorize/route.ts`, `src/app/oauth/token/route.ts`