Skip to main content

sysnet-auth (import path: auth_lib)

GitHub PyPI - Version Python Version FastAPI Tests Coverage License

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.

Verze 0.2.4 — novinky

Offline režim (bez Keycloaku)

Nastav AUTH_VERIFY_KEYCLOAK=False pro přeskočení ověřování podpisu. Vhodné pro lokální vývoj, nikdy ne v produkci.

Podpora Keycloak audience listu

Keycloak standardně vrací aud jako seznam (["client-id", "account"]). Knihovna to od 0.2.4 zpracuje správně.

Metadata

Release files for sysnet-auth 0.2.12

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sysnet-auth 0.2.12
File Size Uploaded
sysnet_auth-0.2.12.tar.gz 38.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sysnet-auth 0.2.12
File Interpreter ABI Platform
sysnet_auth-0.2.12-py3-none-any.whl Python 3 none any Details

Total release size: 68.6 kB

Release files / sysnet_auth-0.2.12.tar.gz

Download URL sysnet_auth-0.2.12.tar.gz
Size 38.6 kB
Tags Source
SHA-256 checksum
How to use checksums
a4eac48536aed184678c3aaf681e38837093cc1d2ec254d824cd1a72fc956beb
BLAKE2b-256 checksum
How to use checksums
b9ba5a5c9042f67be89d20757669ed4c2fe50a54f5e6afc0330d66af0b44a393
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / sysnet_auth-0.2.12-py3-none-any.whl

Download URL sysnet_auth-0.2.12-py3-none-any.whl
Size 30.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a07f0822b5f65bec11f25c059e5be4a4e394f4a6a5579dcf2190eb36bca6b6fe
BLAKE2b-256 checksum
How to use checksums
0e26edf2b740db5d5e12ff9b0ada406fccb445ba20f27f2f298e60f2e0adedac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release history Release notifications | RSS feed

0.3.0

2 release files

0.2.14

2 release files

0.2.13

2 release files

This release

0.2.12 This release

2 release files

0.2.10

2 release files

0.2.9

2 release files

0.2.8

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page