Commit 8b791159 authored by Sergio Gimenez's avatar Sergio Gimenez
Browse files

Merge branch 'fix/keycloak-jwks-url' into 'develop'

fix(fm): fetch Keycloak signing keys from FM_KEYCLOAK_JWKS_URL when set

See merge request !32
parents 916f35d8 4ba1b1e6
Loading
Loading
Loading
Loading
Loading
+1 −0
Changes for README.md: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -91,6 +91,7 @@ Settings are environment variables with the `FM_` prefix, read by `federation_ma
| `FM_POSTGRES_URL` | `postgresql+asyncpg://fm:fm@localhost:5433/fm_db` | `fm_db` connection |
| `FM_NATS_URL` | `nats://localhost:4222` | DataBus |
| `FM_KEYCLOAK_ISSUER` | `http://localhost:8090/realms/federation` | Inbound token issuer |
| `FM_KEYCLOAK_JWKS_URL` | issuer + `/protocol/openid-connect/certs` | Where FM fetches the signing keys, when it can't reach Keycloak on the issuer URL |
| `FM_FEDERATION_ID`, `FM_COUNTRY_CODE`, `FM_MCC`, `FM_MNCS` | — | Our OPG.04 identity, sent on outbound `CreateFederation` |
| `FM_PARTNER_STATUS_LINK` | — | Callback URL advertised to partners |
| `FM_PLATFORM_CAPS` | `["serviceAPIs"]` | Capabilities advertised on federation setup |
+2 −2
Changes for src/federation_manager/adapters/security/keycloak_validator.py: 2 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -5,9 +5,9 @@ from federation_manager.domain.models import ValidatedClaims


class KeycloakJwtValidator:
    def __init__(self, issuer: str) -> None:
    def __init__(self, issuer: str, jwks_url: str | None = None) -> None:
        self._issuer = issuer
        self._jwks_client = jwt.PyJWKClient(f"{issuer}/protocol/openid-connect/certs")
        self._jwks_client = jwt.PyJWKClient(jwks_url or f"{issuer}/protocol/openid-connect/certs")

    async def validate(self, token: str) -> ValidatedClaims:
        try:
+3 −0
Changes for src/federation_manager/core/config.py: 3 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -22,6 +22,9 @@ class Settings(BaseSettings):
    srm_internal_url: str = "http://localhost:8081"
    event_consumer_durable: str = "fm-event-worker"
    keycloak_issuer: str = "http://localhost:8090/realms/federation"
    # The issuer is the public URL partners' tokens carry; FM may only reach Keycloak on an
    # internal address. Unset, the keys are fetched from the issuer.
    keycloak_jwks_url: str | None = None

    # Our own OPG.04 identity, sent on outbound CreateFederation.
    federation_id: str = "oop-local"
+3 −1
Changes for src/federation_manager/main.py: 3 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -75,7 +75,9 @@ async def default_lifespan(app: FastAPI) -> AsyncIterator[None]:
    engine = build_engine(settings.postgres_url, echo=settings.postgres_echo)
    await run_migrations(engine)
    app.state.session_maker = build_session_maker(engine)
    app.state.jwt_validator = KeycloakJwtValidator(settings.keycloak_issuer)
    app.state.jwt_validator = KeycloakJwtValidator(
        settings.keycloak_issuer, jwks_url=settings.keycloak_jwks_url
    )
    key = settings.partner_secret_key_bytes
    cipher = None if key is None else AesGcmSecretCipher(key)
    app.state.partner_secret_cipher = cipher
+1 −0
Changes for tests/integration/test_nats_handoff.py: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -93,6 +93,7 @@ async def test_fm_result_reaches_a_core_subscriber() -> None:
    await publisher.connect()
    srm = await nats.connect(URL)
    subscription = await srm.subscribe(SUBJECT_FM_COMPLETED)
    await srm.flush()  # the server must know the subscription before FM publishes
    marker = str(uuid4())
    try:
        await publisher.publish(SUBJECT_FM_COMPLETED, {"operation_id": marker})
Loading