Skip to main content

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

Project description

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.

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.3.tar.gz (37.2 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.3-py3-none-any.whl (29.2 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for sysnet_auth-0.2.3.tar.gz
Algorithm Hash digest
SHA256 87082fbfe0ec7889aac6cab25a5ef793f89eb898b5ad971b990417d1bdcea595
MD5 547cc8fdd55294e97723e4c70e4c8d12
BLAKE2b-256 ac8f94a23dc368f6721d8f1c23fa19a6a4eda64448f97abd5d945dd198190c41

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for sysnet_auth-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 96716a630898c045a019276c953839e37f461955dab96a25133c335699cb3bdb
MD5 03bc447e1f93245279f831ac520d3361
BLAKE2b-256 1305ed8b5079457e1f693d80ff728eef3c97e1f781c127ffcc2b71595310b94b

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