Skip to main content

Sdilena autentizacni knihovna pro FastAPI mikrosluzby s Keycloack (OIDC/JWT).

Project description

sysnet-auth (import path: auth_lib)

Sdílená autentizační knihovna pro FastAPI mikroslužby, které ověřují identitu uživatelů přes Keycloak (OIDC / JWT). Součást SYSNET ekosystému.

Knihovna je záměrně úzká: neobsahuje business logiku, neukládá stav a nepřeposílá tokeny. Jediná zodpovědnost je validace JWT a vystavení objektu AuthenticatedUser dependency injection mechanismem FastAPI.


Proč existuje

V architektuře desítek mikroslužeb nechceme, aby každá z nich měla vlastní implementaci validace JWT. Rozkol v drobných detailech (kontrola audience, leeway, cachování JWKS) je snadný způsob, jak vyrobit bezpečnostní díru. auth_lib je jediné místo, kde se validuje identita uživatele — a všechny služby ji používají stejně.


Instalace

Balíček je publikovaný jako sysnet-auth, importuje se jako auth_lib (běžný pattern: distribuční jméno ≠ import jméno).

pip install sysnet-auth
from auth_lib import get_current_user, require_role

Python

Vyžaduje Python ≥ 3.11, plně testováno proti 3.11 – 3.14.

Runtime závislosti

Balíček Role
fastapi DI / exception handlery
pydantic v2 modely, validace
pydantic-settings konfigurace přes env
pyjwt[crypto] dekódování + validace JWT
httpx async HTTP klient pro JWKS endpoint
sysnet-pyutils sdílené SYSNET modely (UserType, ErrorModel, Log)

Volba JWT knihovny. python-jose je od 2021 neudržovaný. Používáme PyJWT — aktivně udržovaný de facto standard pro JWT v Pythonu.


Konfigurace

Všechno přes environment proměnné s prefixem AUTH_. Pydantic-settings načte settings při prvním volání get_settings() a cachuje je na proces.

Proměnná Default Popis
AUTH_KEYCLOAK_URL Základní URL Keycloaku (https://kc.example.cz)
AUTH_REALM Název Keycloak realm
AUTH_AUDIENCE Očekávaný aud claim (typicky client_id API)
AUTH_ALGORITHMS ["RS256"] Povolené podpisové algoritmy (CSV nebo JSON)
AUTH_JWKS_CACHE_SECONDS 300 TTL JWKS cache
AUTH_JWKS_HTTP_TIMEOUT_SECONDS 5.0 HTTP timeout pro fetch JWKS
AUTH_LEEWAY_SECONDS 0 Tolerance hodinového rozdílu (clock skew)
AUTH_RESOURCE_CLIENT None Pokud nastaveno, mergne i resource_access.<klient>.roles
AUTH_KID_MISS_REFRESH_COOLDOWN_SECONDS 0 Opt-in: refresh při neznámém kid 1× za N sekund

Odvozené hodnoty

  • Issuer: {AUTH_KEYCLOAK_URL}/realms/{AUTH_REALM}
  • JWKS URL: {issuer}/protocol/openid-connect/certs

Použití ve FastAPI

Ověřený uživatel

from fastapi import Depends, FastAPI
from auth_lib import AuthenticatedUser, get_current_user, install_exception_handlers

app = FastAPI()
install_exception_handlers(app)

@app.get("/me")
async def me(user: AuthenticatedUser = Depends(get_current_user)) -> AuthenticatedUser:
    return user

Role guard

from auth_lib import require_role, require_any_role, require_all_roles

@app.delete("/users/{id}")
async def delete_user(
    id: str,
    user: AuthenticatedUser = Depends(require_role("admin")),
):
    ...

@app.get("/content")
async def list_content(
    user: AuthenticatedUser = Depends(require_any_role(["editor", "viewer"])),
):
    ...

Konverze do SYSNET UserType

from auth_lib import get_current_user

@app.get("/user-profile")
async def profile(user = Depends(get_current_user)):
    sysnet_user = user.to_user_type()   # sysnet_pyutils.UserType
    return sysnet_user

Mapping: sub → identifier, preferred_username → name, email → email, given_name → name_first, family_name → name_last, name → name_full.


Observability

Knihovna neví, co je Prometheus / OpenTelemetry / strukturovaný log. Místo toho exponuje pub-sub hooky, které si konzument zaregistruje:

from auth_lib import on_token_validated, on_token_rejected, on_jwks_refresh

@on_token_validated
def _ok(user):
    metrics.incr("auth.ok", tags={"sub": user.sub})

@on_token_rejected
def _err(exc):
    metrics.incr("auth.err", tags={"type": type(exc).__name__})

@on_jwks_refresh
def _jwks(n):
    metrics.gauge("auth.jwks.keys", n)

Výjimka v hooku nikdy neshodí validaci (hooky jsou best-effort).

Rychlé zapnutí přes SYSNET logger

from auth_lib import install_sysnet_logging
install_sysnet_logging()  # zapíše INFO/WARNING přes sysnet_pyutils.Log

Idempotentní — druhé volání už hooky znovu neregistruje.


Výjimky

Výjimka HTTP Kdy
InvalidTokenError 401 špatný podpis, expirace, iss, aud, neznámý kid, …
MissingRoleError 403 uživatel je ověřen, ale chybí mu role
AuthConfigurationError 500 nedostupný JWKS, špatné URL, malformed response

Všechny dědí z AuthError.

install_exception_handlers(app) registruje handler, který vrací sysnet_pyutils.ErrorModel:

{"code": 401, "message": "Token has expired"}

Jednotný formát chyb napříč SYSNET službami.


JWKS caching

  • In-memory TTL cache (default 300 s).
  • Lazy fetch při prvním volání.
  • Single-flight: souběh N requestů při cold/expired cache spustí právě jeden fetch.
  • Fresh cache je autoritativní — kid miss = InvalidTokenError. Brání DoS přes náhodné kidy.
  • Opt-in cooldown refresh (AUTH_KID_MISS_REFRESH_COOLDOWN_SECONDS > 0): při kid miss ve fresh cache povolí 1 refresh za N sekund — responzivnější reakce na rotaci klíčů.
  • Lazy init asyncio.Lock — kompatibilní s Pythonem 3.14 (neváže lock na event loop z doby importu).
  • Žádný background task, žádný disk.

Bezpečnostní poznámky

  • Ověřujeme vždy podpis, issuer i audience.
  • Výchozí leeway=0. Zapnout jen při doloženém clock skew.
  • Pouze RS256 výchozí, none ani HS256 nepovolujeme bez dobrého důvodu.
  • Čteme pouze Authorization: Bearer (žádné cookies, žádné X-* hlavičky).
  • Tokeny se nelogují. Diagnostika přes sub / jti.
  • JWKS fetch má timeout → nedostupný Keycloak vrátí 500, ne čekání donekonečna.

Struktura projektu

auth_lib/
├── auth_lib/
│   ├── __init__.py          # veřejné re-exporty (__all__)
│   ├── config.py            # AuthSettings
│   ├── exceptions.py        # AuthError + to_error_model()
│   ├── models.py            # AuthenticatedUser + to_user_type()
│   ├── jwks.py              # async JWKS cache + cooldown
│   ├── dependencies.py      # get_current_user, install_exception_handlers
│   ├── roles.py             # require_role / require_any_role / require_all_roles
│   ├── observability.py     # hook registry + install_sysnet_logging()
│   └── py.typed             # PEP 561 marker
├── tests/
├── pyproject.toml           # sysnet-auth, Python 3.11-3.14
├── CHANGELOG.md
├── README.md
└── .gitignore

Testování

pip install -e ".[dev]"
pytest --cov=auth_lib --cov-report=term-missing

Testy nevolají reálný Keycloak — používají vlastní RSA keypair a JWKS cache předvyplněnou odpovídajícím JWK. 58 testů, 98 % pokrytí.


Architektonický princip

Tato knihovna je jediným místem, kde se řeší validace identity uživatele.

Všechny FastAPI mikroslužby ji používají jednotným způsobem. Pokud narazíš na potřebu obejít auth_lib (vlastní dekódování, custom validace), je to signál k úpravě auth_lib — ne k duplikaci logiky.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sysnet_auth-0.2.0.tar.gz (24.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sysnet_auth-0.2.0-py3-none-any.whl (16.9 kB view details)

Uploaded Python 3

File details

Details for the file sysnet_auth-0.2.0.tar.gz.

File metadata

  • Download URL: sysnet_auth-0.2.0.tar.gz
  • Upload date:
  • Size: 24.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for sysnet_auth-0.2.0.tar.gz
Algorithm Hash digest
SHA256 27ac53c545032910f26f8d0ea8c41f761ec9ca32966e91685a1254d1f6410914
MD5 e9f76f0494527e7ba5ed763a7285feb0
BLAKE2b-256 1ef7bfd1f5418faf887ed18b20aa0d041724d9d96ee4441f8f8eef224a8b01e7

See more details on using hashes here.

File details

Details for the file sysnet_auth-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: sysnet_auth-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 16.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for sysnet_auth-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7b610e8818792fd2005fbf2e63fd617013a78331c1013709dca137675addebc6
MD5 09c1c0d1698bfbb941888c94f41e31b8
BLAKE2b-256 057d58f069f51784164719b73f0827446682b9d15d5405210ffcf3afcc76f2a1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page