Commit 4d7f0a51 authored by George Papathanail's avatar George Papathanail
Browse files

Merge branch 'feat/keycloak' into 'develop'

feat: validate Keycloak tokens and enforce CAMARA roles, scopes and consent per operation

See merge request !28
parents fe3c8283 cfd3146a
Loading
Loading
Loading
Loading
Loading
+177 −0

File added.

Preview size limit exceeded, changes collapsed.

+7 −0
Changes for .env.example: 7 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -25,3 +25,10 @@
# CALLBACK_SETTINGS__TIMEOUT=10.0
# QOD_SETTINGS__SERVICE_SPECIFICATION_ID="7608e902-b927-559f-b448-e7e9061dfa5c"
# LOCATION_RETRIEVAL_SETTINGS__SERVICE_SPECIFICATION_ID="0129e7ce-8e02-5dfd-bae1-0303bcc7676e"

# KEYCLOAK_SETTINGS__* default to a deliberately-unreachable `.invalid` host so a
# misconfigured deployment fails loudly instead of silently trusting the wrong
# Keycloak. Point these at your actual realm, e.g. (matches the oop-platform-chart):
# KEYCLOAK_SETTINGS__ISSUER="http://localhost:8080/realms/oop"
# KEYCLOAK_SETTINGS__JWKS_URL="http://localhost:8080/realms/oop/protocol/openid-connect/certs"
# KEYCLOAK_SETTINGS__AUDIENCE="oeg"
+1 −1
Changes for .pre-commit-config.yaml: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -23,4 +23,4 @@ repos:
    - id: mypy
      files: ^src/open_exposure_gateway/|^tests/
      args: [--strict, --ignore-missing-imports, --cache-dir, .cache/mypy]
      additional_dependencies: ["fastapi[standard]>=0.135.1", "pydantic>=2.0", "pydantic-settings>=2.0", "httpx>=0.27", "pytest>=9.0.2", "sqlalchemy>=2.0.48", "pytest-asyncio>=0.24", "testcontainers>=4.0.0", "types-PyYAML>=6.0.1"]
      additional_dependencies: ["fastapi[standard]>=0.135.1", "pydantic>=2.0", "pydantic-settings>=2.0", "httpx>=0.27", "pytest>=9.0.2", "sqlalchemy>=2.0.48", "pytest-asyncio>=0.24", "testcontainers>=4.0.0", "types-PyYAML>=6.0.1", "pyjwt[crypto]>=2.10.1"]
+1 −0
Changes for pyproject.toml: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ dependencies = [
    "asyncpg>=0.31.0",
    "fastapi[standard]>=0.135.1",
    "nats-py>=2.10.0",
    "pyjwt[crypto]>=2.10.1",
    "python-dotenv>=1.2.2",
    "sqlalchemy[asyncio]>=2.0.48",
    "structlog>=25.5.0",
+19 −0
Changes for src/open_exposure_gateway/adapters/http/keycloak_token_verifier.py: 19 added lines, 0 removed lines.
Original line number Diff line number Diff line
from open_exposure_gateway.core.auth.jwks_cache import JWKSCache
from open_exposure_gateway.core.auth.tokens import DecodedClaims, validate_token
from open_exposure_gateway.core.config import KeycloakSettings


class KeycloakTokenVerifier:
    def __init__(self, settings: KeycloakSettings) -> None:
        self._settings = settings
        self._jwks_cache = JWKSCache(settings.jwks_url)

    @property
    def is_healthy(self) -> bool:
        return self._jwks_cache.is_healthy

    async def warm(self) -> None:
        await self._jwks_cache.warm()

    async def verify(self, token: str) -> DecodedClaims:
        return await validate_token(token, self._jwks_cache, self._settings)
Loading