Python 서버 SDK
keenauth-python은 Python 3.10 이상을 위한 서버 SDK입니다. 타입이 지정된 메서드로 관리 API를 호출하며, 모든 KeenAuth 서버 SDK가 공통으로 제공하는 기능을 추가로 제공합니다. 여기에는 API 키 또는 private_key_jwt 머신 간 토큰을 사용한 인증, 재시도, 커서 페이지네이션, 타입이 지정된 오류, 웹훅 서명 검사, 지원 중단 경고가 포함됩니다. 이 패키지는 아직 PyPI에 게시되지 않았습니다.
웹 앱에서 로그인을 구현하려면 Django, Flask, FastAPI를 참조하십시오. 베어러 토큰을 받는 API를 보호하려면 API 보호를 참조하십시오.
설치
pip install keenauth-python
private_key_jwt 서명자는 cryptography 패키지가 필요합니다. m2m 추가 기능과 함께 설치하십시오.
pip install "keenauth-python[m2m]"
API 키로 관리 API 호출
import os
from keenauth import KeenAuth
client = KeenAuth("https://api.eu.keenauth.com/v1", os.environ["KEENAUTH_API_KEY"])
page = client.users.list_users(limit=20)
for user in page.data:
print(user.id)
생성된 모든 API 클래스는 속성으로 사용할 수 있습니다. 예를 들어 client.users, client.organizations, client.api_keys 등을 사용할 수 있습니다. 기본 URL은 https를 사용해야 하며, http는 localhost에서만 허용됩니다. SDK는 리디렉션을 따르지 않으므로 자격 증명이 다른 호스트로 전송되지 않습니다.
private_key_jwt 관리 토큰 사용
관리 애플리케이션은 API 키 대신 개인 키로 인증합니다. SDK는 클라이언트 어서션(RFC 7523)에 서명하고, 환경의 토큰 엔드포인트에서 관리 API 토큰을 가져오며, 만료되기 60초 전에 토큰을 갱신합니다.
import os
from keenauth import KeenAuth
from keenauth.m2m import ManagementTokenSource
source = ManagementTokenSource(
issuer="https://acme.eu.keenauth.app", # your environment's default host
client_id="app_01j9zzzzzzzzzzzzzzzzzzzz", # a management application
private_key=os.environ["KEENAUTH_PRIVATE_KEY"], # unencrypted PEM
resource="https://api.eu.keenauth.com", # the management API of your region
scopes=["users:read", "users:write"], # optional; default: the grant's default scopes
)
client = KeenAuth("https://api.eu.keenauth.com/v1", token_source=source)
- 어서션의
iss와sub은client_id로,aud는 발급자로 설정되며, 무작위jti와 60초의 수명을 가집니다.kid는 공개 키의 RFC 7638 썸프린트입니다. - 지원되는 키는 EC P-256(
ES256), EC P-384(ES384), Ed25519(EdDSA), 2048비트 이상의 RSA(기본값PS256, 또는algorithm="RS256")입니다. - 공개 키를 관리 애플리케이션에 등록하십시오.
keenauth.m2m.load_private_key(pem).public_jwk()는 이 키를 JWK로 반환합니다. - 토큰은 캐시됩니다. 여러 스레드가 동시에 새 토큰을 필요로 하면 토큰 엔드포인트로 요청이 한 번만 전송됩니다.
- 요청이 거부되면
keenauth.m2m.TokenError가 발생하며, 여기에는status,error,error_description이 포함됩니다. 오류 메시지에는 어서션이나 토큰이 포함되지 않습니다.
재시도
SDK는 429, 502, 503, 504 응답으로 실패했거나 연결 오류 또는 시간 초과가 발생한 요청을 재시도합니다. 재시도 대상은 GET, PUT, DELETE 요청과 Idempotency-Key가 포함된 POST 요청뿐입니다. If-Match가 포함된 PATCH 요청은 429에서만 재시도됩니다. 속도 제한기는 요청을 실행하기 전에 거부하며, If-Match는 변경 사항이 두 번 적용되지 않도록 합니다. 그 밖의 PATCH 요청은 재시도되지 않습니다. 총 시도 횟수는 최대 3회입니다.
- 응답에
Retry-After헤더(초 또는 HTTP 날짜)가 있으면 SDK는 해당 시간만큼 기다립니다.Retry-After가 60초를 초과하면 SDK는 기다리지 않고 응답을 반환합니다. Retry-After가 없으면 0.5초, 그다음 1초를 기다리며, 각 대기 시간에는 ±25%의 무작위 지터가 적용됩니다.- SDK는
idempotency_key=를 직접 전달하지 않는 한, 키를 받을 수 있는 모든POST작업에 무작위 UUIDv4Idempotency-Key를 추가합니다. 재시도 시에도 같은 키가 전송되므로 서버는 작업을 한 번만 실행합니다.
페이지네이션
목록 메서드는 하나의 페이지를 반환합니다. 이 페이지에는 data와 after, before 커서가 포함된 list_metadata가 있습니다. paginate는 모든 페이지를 순회하며, 다음 페이지는 해당 지점에 도달했을 때만 가져옵니다.
from keenauth import paginate
for user in paginate(client.users.list_users, limit=100, status="active"):
if user.email == "ada@example.com":
break # no further page is requested
- 필터나
order처럼 다른 인수는 모든 페이지 요청에 함께 전송됩니다. - 커서에서 역방향으로 순회하려면
before=를 전달하십시오.after와before는 함께 사용할 수 없습니다. pages(...)는 같은 인수를 받으며, 항목 대신 페이지 단위로 반환합니다.
오류
application/problem+json(RFC 9457) 형식의 오류 응답은 keenauth.ProblemError를 발생시킵니다. 이때 code, status, detail, request_id, errors 필드를 사용하십시오. 이 SDK 버전이 알지 못하는 code도 문자열로 반환되므로, 서버에 새로운 오류 코드가 추가되어도 코드가 중단되지 않습니다.
from keenauth import ProblemError
try:
client.users.get_user("usr_01j9zzzzzzzzzzzzzzzzzzzz")
except ProblemError as e:
if e.code == "not_found":
...
print(e.request_id)
웹훅 검증
KeenAuth는 Standard Webhooks 방식으로 웹훅에 서명합니다. WebhookVerifier는 webhook-id, webhook-timestamp, webhook-signature 헤더를 엔드포인트의 idp_whsec_ 시크릿과 대조하여 확인합니다.
import os
from flask import Flask, request
from keenauth.webhooks import WebhookVerificationError, WebhookVerifier
app = Flask(__name__)
verifier = WebhookVerifier(os.environ["KEENAUTH_WEBHOOK_SECRET"])
@app.post("/webhooks/keenauth")
def keenauth_webhook():
try:
verifier.verify(request.get_data(), request.headers)
except WebhookVerificationError as e:
return {"error": e.reason}, 400
event = request.get_json()
...
return "", 204
- JSON을 파싱하기 전의 원본 요청 본문을 전달하십시오. 바이트 하나라도 변경되면 검사에 실패합니다.
- 타임스탬프는 서버 시계와 앞뒤로 5분 이내여야 합니다.
- 시크릿을 교체하는 동안에는 두 값을 모두 전달하십시오. 예:
WebhookVerifier([new_secret, old_secret]). 헤더의 서명 중 하나라도 어떤 시크릿과 일치하면 요청이 수락됩니다. - 오류는 타입으로 구분됩니다.
WebhookHeadersError,WebhookTimestampError,WebhookSignatureError,WebhookSecretError(잘못된 시크릿이며, 검증기를 생성할 때 발생합니다)가 있습니다. 각 오류에는reason필드가 있습니다. - 비교는 일정한 시간에 수행되며, 오류 메시지에는 시크릿이나 서명이 포함되지 않습니다.
타입이 지정된 이벤트와 클레임
keenauth.events에는 공개 이벤트 스키마에서 생성된 모든 고객 이벤트 유형에 대한 frozen dataclass가 있습니다. parse_event는 검증된 본문을 확인하고 해당 유형의 클래스를 반환합니다. keenauth.webhooks.verify_event는 한 번의 호출로 검증과 파싱을 함께 수행합니다. match 또는 isinstance로 범위를 좁히십시오.
from keenauth.events import EmailSendingSuspendedEvent, UnknownEvent, UserCreatedEvent
from keenauth.webhooks import verify_event
event = verify_event(verifier, request.get_data(), request.headers)
match event:
case UserCreatedEvent():
print(event.data.object.id)
case EmailSendingSuspendedEvent():
print(event.data.object.reason)
case UnknownEvent():
pass # a type this SDK version does not know yet; event.type and event.raw are still available
parse_event은 bytes, 문자열, 이미 디코딩된 매핑을 받습니다. 봉투와 해당 유형의 스키마에 선언된 멤버를 확인합니다. 멤버가 없거나 타입이 잘못되면data.object.reason과 같은 경로가 포함된EventParseError가 발생합니다.- 알 수 없는 유형이거나, 이 SDK가 모델링하지 않은
schema_version을 가진 알려진 유형이면UnknownEvent가 됩니다. 이는 오류가 아닙니다. - 스키마에 선언되지 않은 멤버는
raw(이벤트와data.object모두)에 보존됩니다. - 배열은 튜플로 처리됩니다. 열거형 필드의 값은 알려진 값과 대조하여 검사하지 않습니다.
- 모델은
mypy --strict를 통과합니다.
keenauth.claims는 검증된 토큰의 클레임에 타입을 지정합니다. 제공되는 함수는 parse_id_token_claims(OpenID Connect Core)와 parse_access_token_claims(RFC 9068)입니다. 이 함수들은 클레임 타입만 확인하고 아무것도 검증하지 않으므로, 검증기가 반환한 claims에 대해 호출하십시오.
from keenauth.claims import parse_access_token_claims
at = verifier.verify_access_token(token)
claims = parse_access_token_claims(at.claims)
claims.client_id # str
claims.org_roles # tuple[str, ...] | None
claims.custom # your own claims
aud는 항상 튜플입니다. 형식이 맞지 않는 클레임이 있으면 이름과 함께 ClaimsParseError가 발생합니다.
지원 중단 경고
응답에 Deprecation(RFC 9745) 또는 Sunset(RFC 8594) 헤더가 있으면, SDK는 프로세스당 한 번씩 표준 logging 모듈을 통해 keenauth 로거에 경고를 기록합니다. 경고에는 메서드, 리소스, 헤더 값이 표시되며, ID, 쿼리 문자열, 자격 증명은 포함되지 않습니다. 요청 자체는 영향을 받지 않습니다.
import logging
logging.getLogger("keenauth").setLevel(logging.WARNING)
텔레메트리 헤더
모든 요청에는 Idp-Client: keenauth-python/<version> (python/<version>) 헤더가 포함됩니다. 이 헤더에는 개인 정보가 포함되지 않으며 끌 수 없습니다. KeenAuth는 이 헤더를 지원 및 지원 중단 안내에 사용합니다.