Django API'sini koruma
Bu hızlı başlangıç, Django API'nizi KeenAuth erişim belirteçleriyle korur. Python SDK'nın @protect görünüm süsleyicisini ekler, bir görünümde bir kapsam zorunlu kılar ve API'yi SDK test kiti ile çevrimdışı test edersiniz. Örnek Django 5.2 kullanır ve veritabanı gerektirmez.
API'nin üç uç noktası vardır:
| Uç nokta | Gereksinim |
|---|---|
GET /api/public |
yok |
GET /api/private |
API'niz için geçerli bir erişim belirteci |
GET /api/messages |
messages:read kapsamına sahip geçerli bir erişim belirteci |
Başlamadan önce
- Bir KeenAuth ortamı. Bu ortamın yayıncısı
https://your-environment.eu.keenauth.appbiçimindedir. - Ortamda kayıtlı bir API (API oluşturun). Bu API'nin
identifierdeğeri, örneğinhttps://api.example.com, API'nizin kabul ettiği belirteçlerin hedef kitlesidir. - Bu API üzerinde, onu çağıran uygulamaya verilmiş
messages:readkapsamı. - Python 3.10 veya üzeri.
1. SDK'yı kurun
pip install "keenauth-python[django]"
SDK henüz yayımlanmadı. Yayımlanana kadar örnek, SDK'yı kaynak kodundan kurar. Örnek, her paketi requirements.txt içinde tam bir sürüme sabitler.
2. Yapılandırın
project/settings.py her değeri ortam değişkenlerinden okur:
| Değişken | Zorunlu | Anlamı |
|---|---|---|
DJANGO_SECRET_KEY |
evet | Django'nun gizli anahtarı; bir tane üretin ve asla commit etmeyin |
DJANGO_ALLOWED_HOSTS |
hayır | Virgülle ayrılmış sunucu adları, varsayılan localhost,127.0.0.1 |
KEENAUTH_ISSUER |
evet | Ortamınızın yayıncısı |
KEENAUTH_AUDIENCE |
evet | API'nizin tanımlayıcısı |
PUBLIC_BASE_URL |
evet | İstemcilerin çağırdığı URL (bkz. Proxy arkasında) |
KEENAUTH_WEBHOOK_SECRET |
önerilir | token.revoked teslim eden web kancası uç noktasının idp_whsec_ imza sırrı (döndürme sırasında virgülle ayrılmış iki değer). Bu olmadan API iptal edilmiş belirteçlerden haberdar olmaz |
3. Görünümlerinizi koruyun
@protect(protector) geçerli bir belirteç ister ve doğrulanmış talepleri request.auth içine koyar; scopes=[...] ayrıca kapsamları da ister. Başarısız bir denetim, görünüm çalışmadan önce bir application/problem+json yanıtı döndürür.
@require_GET
def public(request: HttpRequest) -> JsonResponse:
# No token needed.
return JsonResponse({"message": "Hello from a public endpoint."})
@require_GET
@protect(protector)
def private(request: HttpRequest) -> JsonResponse:
# Any valid access token for this API; the verified claims are in request.auth.
return JsonResponse({"message": "Hello from a private endpoint.", "sub": request.auth.subject})
@require_GET
@protect(protector, scopes=["messages:read"])
def messages(request: HttpRequest) -> JsonResponse:
# A valid access token that also carries the messages:read scope.
return JsonResponse({"messages": ["Hello from a scoped endpoint."]})
api/auth.py süsleyiciyi başlangıçta ayarlardan (api/apps.py içindeki ApiConfig.ready) oluşturur; böylece eksik bir ayar, sunucu ilk isteği almadan önce durur. Doğrulayıcı, bir belirteci ilk gördüğünde imza anahtarlarını ortamınızdan alır.
@cache
def get_protector() -> Protector:
if not settings.KEENAUTH_ISSUER or not settings.KEENAUTH_AUDIENCE or not settings.KEENAUTH_PUBLIC_BASE_URL:
raise ImproperlyConfigured("KEENAUTH_ISSUER, KEENAUTH_AUDIENCE and PUBLIC_BASE_URL are required")
# The verifier refuses an issuer under the reserved .invalid domain, so a test token can never pass here.
verifier = Verifier(VerifierConfig(issuer=settings.KEENAUTH_ISSUER, audience=settings.KEENAUTH_AUDIENCE))
return Protector(
verifier,
public_base_url=settings.KEENAUTH_PUBLIC_BASE_URL,
revocation=revocation,
# The error never contains the token or claim values; never log the Authorization or DPoP header.
on_error=lambda err: log.warning("rejected: %s", err),
)
@cache
def get_webhook() -> Optional[RevocationWebhook]:
"""The token.revoked webhook, or None when KEENAUTH_WEBHOOK_SECRET is not set. A malformed secret raises
WebhookSecretError (without echoing it), so ApiConfig.ready stops the server at startup."""
if not settings.KEENAUTH_WEBHOOK_SECRETS:
return None
return RevocationWebhook(settings.KEENAUTH_WEBHOOK_SECRETS, revocation)
class ProtectorProxy:
"""Hands each request to `get_protector()`, so the `@protect` decorators can be applied at import time."""
def authenticate(self, req: RequestInfo, requirement: Optional[Requirement] = None) -> AccessClaims:
return get_protector().authenticate(req, requirement)
protector = ProtectorProxy()
4. Çalıştırın ve API'yi çağırın
export DJANGO_SECRET_KEY="$(python3 -c 'import secrets; print(secrets.token_urlsafe(50))')"
export KEENAUTH_ISSUER=https://your-environment.eu.keenauth.app
export KEENAUTH_AUDIENCE=https://api.example.com
export PUBLIC_BASE_URL=https://api.example.com
python manage.py runserver 8080
runserver yalnızca geliştirme içindir; üretimde project.wsgi komutunu bir WSGI sunucusu altında çalıştırın.
İlk çağrı için, API'nizde messages:read kapsamına sahip bir makineden makineye uygulamasının istemci kimlik bilgileri akışıyla bir belirteç alın:
ACCESS_TOKEN=$(curl -s -u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=client_credentials \
-d resource=https://api.example.com \
-d scope=messages:read \
https://your-environment.eu.keenauth.app/oauth2/token | jq -r .access_token)
curl -i http://localhost:8080/api/messages -H "Authorization: Bearer $ACCESS_TOKEN"
5. Ağ olmadan test edin
keenauth.testing, genel bir test anahtarıyla imzalanmış belirteçler üretir ve testing.verifier yalnızca bu belirteçleri kabul eder. Testler get_protector yerine bu doğrulayıcıyı kullanan bir süsleyici koyar; bu yüzden ağa, KeenAuth hesabına veya ayarlarda bir test anahtarına ihtiyaç duymazlar. Bkz. Çevrimdışı test kiti.
class ProtectedAPITest(SimpleTestCase):
def setUp(self) -> None:
self.cache = MemoryRevocationCache()
test_protector = Protector(testing.verifier(audience=AUDIENCE), public_base_url=BASE_URL, revocation=self.cache)
patcher = mock.patch.object(auth, "get_protector", return_value=test_protector)
patcher.start()
self.addCleanup(patcher.stop)
def test_scope(self) -> None:
self.assert_problem(self.get("/api/messages", testing.mint_access_token(AUDIENCE)), 403, "insufficient_scope",
'scope="messages:read"')
tok = testing.mint_access_token(AUDIENCE, claims={"scope": "profile messages:read"})
self.assertEqual(self.get("/api/messages", tok).status_code, 200)
set -a; . ./.env.example; set +a
DJANGO_SECRET_KEY=insecure-test-only python manage.py test
Örneğin testleri, geçerli bir belirtecin kabul edildiğini ve eksik, süresi dolmuş veya yanlış hedef kitleli bir belirtecin, eksik bir kapsamın, URL sorgusundaki bir belirtecin ve iptal edilmiş bir belirtecin aşağıdaki durum kodlarıyla reddedildiğini denetler. Ayrıca PUBLIC_BASE_URL için bir kanıtla DPoP bağlı bir belirteç gönderirler ve üretim doğrulayıcısının test sağlayıcısını reddettiğini kontrol ederler. Testler ayrıca imzalı token.revoked ttesliminatlarını da gönderir: geçerli bir teslimat belirteci iptal eder; sahte, değiştirilmiş veya eski bir teslimat 400 alır ve hiçbir şeyi iptal etmez; tekrarlanan bir teslimatın tek bir etkisi olur.
Yanıtlar
| İstek | Durum | Gövdede code |
|---|---|---|
Authorization başlığı yok |
401 |
missing_token |
| Süresi dolmuş belirteç, başka bir API için belirteç, hatalı imza, iptal edilmiş belirteç | 401 |
invalid_token |
| Gerekli kapsamı olmayan belirteç | 403 |
insufficient_scope |
URL sorgusunda belirteç (?access_token=) |
400 |
invalid_request |
Gövde application/problem+json ve WWW-Authenticate başlığı standart sınamayı taşır; Django birden fazla sınamayı tek bir başlıkta birleştirir. Tam tablo Ara katmanla API'nizi koruma sayfasındadır.
Proxy arkasında
İstemciler DPoP kullanıyorsa, ara katman her kanıtın istemcinin çağırdığı URL için üretildiğini denetler. PUBLIC_BASE_URL değerini, örneğin https://api.example.com, istemcilerin çağırdığı dış URL olarak ayarlayın; bu, bir proxy veya yük dengeleyici arkasında da geçerlidir. Bu ayar zorunludur ve https kullanmalıdır (http yalnızca localhost, 127.0.0.1 veya [::1] için); bu ayar olmadan uygulama başlamayı reddeder. Ara katman X-Forwarded-* başlıklarını kendisi asla okumaz; Django bunları yalnızca SECURE_PROXY_SSL_HEADER veya USE_X_FORWARDED_HOST ayarladığınızda uygular ve PUBLIC_BASE_URL ayarlıyken denetlenen URL bunlara bağlı değildir.
İptal edilmiş belirteçler
Bir erişim belirteci süresi dolana kadar geçerli kalır; bu yüzden KeenAuth, belirteçlerin iptal edildiğini API'nize bildirir. Örnek, süsleyiciye bellek içi bir iptal önbelleği verir ve bunu besleyen bir token.revoked web kancasını POST /webhooks/keenauth (api/webhook.py) adresinde sunar.
- Panelde,
https://<your host>/webhooks/keenauthURL'siyle,token.revokedolaylarına abone olan bir web kancası uç noktası oluşturun ve imza sırrınıKEENAUTH_WEBHOOK_SECRETolarak dışa aktarın. - İşleyici, imzayı SDK ile ham gövde üzerinde doğrular ve geçersizse ya da zaman damgası beş dakikadan fazla kaymışsa
400yanıtı verir. Olayın seçicisini (access_token,session,user,client,org,org_user,client_user) bir iptal işaretçisine eşler ve204yanıtı verir; ara katman bunun ardındannot_beforeöncesinde verilmiş eşleşen her belirteci reddeder. - KeenAuth en az bir kez teslim eder; bu yüzden işleyici her
webhook-iddeğerini bir kez uygular. İşaretçi saklanamazsa503yanıtı verir ve KeenAuth olayı yeniden teslim eder. Diğer olay türlerini ve hiçbir erişim belirtecinin eşleşemeyeceği seçicileri (refresh_family,api) yok sayar.
İşleyici olay kimliğini, seçici türünü ve nedeni günlüğe yazar; gövdeyi, imzayı veya sırrı asla yazmaz. Her çalışan süreç kendi önbelleğine sahiptir; birden fazla çalışan veya örnek varsa bunun yerine paylaşılan bir depo kullanın. Bkz. İptal önbelleği.
Sonraki adımlar
- Ara katmanla API'nizi koruma: Flask, adım artırma gereksinimleri, DPoP ve tüm seçenekler.
- Belirteçleri doğrulama: ara katmanın altındaki doğrulayıcı.
- Çevrimdışı test kiti.