Ara katman (middleware) ile API'nizi koruma

Her sunucu SDK'sı, KeenAuth erişim belirteçleriyle API'nizi koruyan hafif bir ara katman içerir: Go'da net/http, Node.js'te Express ve Fastify, Python'da ise Django, Flask ve FastAPI. Bu katman belirteç doğrulayıcının üzerinde çalışır ve kendi belirteç mantığını içermez.

Ara katman neler yapar

Go (net/http)

protect paketi yalnızca standart kütüphaneyi kullanır ve http.Handler kabul eden her yönlendiriciyle çalışır.

import (
	"github.com/keenauth/keenauth-go/protect"
	"github.com/keenauth/keenauth-go/verify"
)

v, err := verify.New(verify.Config{
	Issuer:   "https://acme.eu.keenauth.app",
	Audience: "https://api.acme.example",
})
if err != nil {
	return err
}
m, err := protect.New(v, protect.Options{
	PublicBaseURL:       "https://api.acme.example",
	ResourceMetadataURL: "https://api.acme.example/.well-known/oauth-protected-resource",
})
if err != nil {
	return err
}

mux := http.NewServeMux()
mux.Handle("GET /orders", m.Require(protect.Requirement{Scopes: []string{"orders:read"}})(http.HandlerFunc(
	func(w http.ResponseWriter, r *http.Request) {
		claims, _ := protect.ClaimsFrom(r.Context())
		fmt.Fprintln(w, claims.Subject)
	})))

age := 10 * time.Minute
mux.Handle("POST /payouts", m.Require(protect.Requirement{
	Scopes:        []string{"payouts:write"},
	AcceptableACR: []string{"urn:idp:acr:mfa"},
	MaxAuthAge:    &age,
})(payoutsHandler))

m.Handler(next), geçerli bir belirteç dışında ek gereksinim olmadan bir işleyiciyi korur. Require, bir kapsam geçerli bir kapsam belirteci değilse başlangıçta panic oluşturur; tıpkı http.ServeMux'un geçersiz bir desen için yaptığı gibi.

Node.js (Express ve Fastify)

Ara katman @keenauth/node/middleware alt yolundadır. Express ve Fastify, SDK'nın bağımlılıkları değildir.

import { createVerifier } from "@keenauth/node";
import { createProtector, expressAuth, fastifyAuth } from "@keenauth/node/middleware";

const verifier = createVerifier({ issuer: "https://acme.eu.keenauth.app", audience: "https://api.acme.example" });
const protector = createProtector(verifier, { publicBaseUrl: "https://api.acme.example" });

// Express
app.get("/orders", expressAuth(protector, { scopes: ["orders:read"] }), (req, res) => {
  res.json({ sub: req.auth.subject });
});

// Fastify
fastify.decorateRequest("auth", null);
fastify.get("/orders", { preHandler: fastifyAuth(protector, { scopes: ["orders:read"] }) }, async (request) => {
  return { sub: request.auth.subject };
});

Adım yukarı doğrulama için { acceptableAcr: ["urn:idp:acr:mfa"], maxAuthAgeSeconds: 600 } kullanın. TypeScript'te req.auth değerini okumak için isteği alt yoldan dışa aktarılan AuthenticatedRequest türüyle tanımlayın.

Python (Django, Flask, FastAPI)

Kullandığınız çatı için ek paketi kurun: pip install "keenauth-python[django]", "keenauth-python[flask]" veya "keenauth-python[fastapi]". Çatı yalnızca kendi uyarlayıcı modülü tarafından içe aktarılır.

from keenauth.middleware import Protector
from keenauth.verify import Verifier, VerifierConfig

verifier = Verifier(VerifierConfig(issuer="https://acme.eu.keenauth.app", audience="https://api.acme.example"))
protector = Protector(verifier, public_base_url="https://api.acme.example")

Django:

from django.http import JsonResponse
from keenauth.middleware.django import protect

@protect(protector, scopes=["orders:read"])
def orders(request):
    return JsonResponse({"sub": request.auth.subject})

Flask:

from flask import g
from keenauth.middleware.flask import protect

@app.get("/orders")
@protect(protector, scopes=["orders:read"])
def orders():
    return {"sub": g.auth.subject}

FastAPI:

from fastapi import Depends, FastAPI
from keenauth.middleware.fastapi import RequireAuth, install
from keenauth.verify import AccessClaims

app = FastAPI()
install(app)

@app.get("/orders")
def orders(claims: AccessClaims = Depends(RequireAuth(protector, scopes=["orders:read"]))):
    return {"sub": claims.subject}

install(app), hata gövdesini yazan işleyiciyi ve her bir sınama için bir WWW-Authenticate başlığını kaydeder. Adım yukarı doğrulama için acceptable_acr=["urn:idp:acr:mfa"], max_auth_age=timedelta(minutes=10) kullanın.

Genel temel URL (zorunlu)

Bir DPoP kanıtı, istemcinin çağırdığı URL'yi (htu) belirtir. Ara katman bu URL'yi, yapılandırdığınız genel temel URL'nin (PublicBaseURL, publicBaseUrl, public_base_url) ve istek yolunun birleşimiyle karşılaştırır. Bunu istemcilerin kullandığı adrese ayarlayın; örneğin https://api.acme.example veya https://acme.example/api. Bu alan zorunludur: istemcileriniz yalnızca Bearer belirteçleri gönderse bile, bu olmadan ara katmanı oluşturmak başlangıçta bir yapılandırma hatasıyla başarısız olur.

Ara katman bu URL'yi asla isteğin Host başlığından veya X-Forwarded-* başlıklarından oluşturmaz. Python'da ise çatının kendi istek URL'sini kullanmaz; çatı veya sunucu iletilen başlıklara güvenecek şekilde yapılandırılmış olsa bile. İstemci bu değerleri seçer: API'niz iki ana makine adı altında sunuluyorsa, bunlardan biri için yapılmış bir kanıt diğerine tekrar oynatılabilir. Bir yük dengeleyicinin veya ters vekilin arkasında, genel temel URL aynı zamanda ara katmana istemcilerin gördüğü şemayı, ana makineyi ve yol önekini de bildirir. Genel temel URL https kullanmalıdır; localhost, 127.0.0.1 ve [::1] istisnadır (RFC 6750 §5.3). Şema ve ana makine büyük/küçük harf duyarsız karşılaştırılır ve varsayılan bağlantı noktası (:443, :80) yok sayılır; bu hem temel URL'de hem de kanıtın htu değerinde geçerlidir.

Yanıtlar

Durum Durum kodu WWW-Authenticate code
Belirteç yok veya desteklenmeyen şema 401 Bearer ve DPoP algs="…", hata olmadan missing_token
Sorgudaki belirteç, tekrarlanan veya hatalı Authorization 400 Bearer error="invalid_request" invalid_request
Geçersiz, süresi dolmuş veya iptal edilmiş belirteç 401 error="invalid_token" invalid_token
Eksik, tekrarlanan veya geçersiz DPoP kanıtı 401 DPoP error="invalid_dpop_proof" invalid_dpop_proof
Gerekli bir kapsam eksik 403 error="insufficient_scope", scope="…" insufficient_scope
Adım yukarı doğrulama gerekli 401 error="insufficient_user_authentication", acr_values="…", max_age="…" insufficient_user_authentication
İmza anahtarları veya iptal durumu kullanılamıyor 503 yok jwks_unavailable, revocation_unavailable

Sınama, istemcinin kullandığı şemayı kullanır. Kaynak meta veri URL'sini ayarladığınızda, her 401 sınaması resource_metadata (RFC 9728) içerir. Gövde type, title, status, code ve sabit bir detail alanı içerir; bir belirtecin neden geçersiz olduğunu hiçbir zaman belirtmez. Kendi günlükleriniz için bir hata geri çağırma işlevi (OnError, onError, on_error) iletin: bu işlev doğrulayıcının hatasını alır ve bu hata belirteç veya talep değeri içermez. Authorization ya da DPoP başlıklarını kendiniz günlüğe yazmayın.

İptal önbelleği (token.revoked)

JWT erişim belirteci, süresi dolana kadar geçerli kalır. İptal edilmiş belirteçleri daha erken reddetmek için token.revoked webhook'unu işleyin ve seçicisini, ara katmanın sorguladığı bir iptal önbelleğine kaydedin. SDK'lar bellek içi bir önbellek içerir: Go için protect.NewMemoryRevocationCache, Node.js için createMemoryRevocationCache ve Python için MemoryRevocationCache.

Bir seçici bir belirteç kimliğini (jti), bir oturumu (sid), bir kullanıcıyı (sub), bir kuruluşu (org_id), bir uygulamayı (client_id) veya kuruluş + kullanıcı ve uygulama + kullanıcı çiftlerini belirtir. jti seçicisi o belirteci iptal eder. Diğer seçiciler, olayın not_before zamanından önce verilmiş tüm belirteçleri iptal eder; sonradan verilen belirteçler kabul edilir. İşaretçiler varsayılan olarak 24 saat ve 1 dakika tutulur; bu, mümkün olan en uzun erişim belirteci ömrü ile saat kayması toplamıdır.

cache := protect.NewMemoryRevocationCache(0, nil)
m, _ := protect.New(v, protect.Options{PublicBaseURL: "https://api.acme.example", Revocation: cache})

// In your verified token.revoked webhook handler:
err := cache.Revoke(protect.Selector{SessionID: "ses_…"}, notBefore)

Bellek içi önbellek tek bir sürece aittir. API'niz birden fazla örnekte çalışıyorsa, iptal denetleyici arayüzünü paylaşılan bir depo üzerinde uygulayın. Önbellek yanıt veremezse, ara katman isteği 503 ile reddeder.

Test etme

Ara katman testleriniz için belirteç üretmek üzere çevrimdışı test kitini kullanın. Kitte DPoP kanıtı yardımcısı yoktur; testinizde kanıtı dilinizin kriptografi kütüphanesiyle imzalayın.