Commit 3db701ef authored by Dimitrios Gogos's avatar Dimitrios Gogos
Browse files

Merge branch 'adr-0037-ewbi-oauth2' into 'develop'

docs: ADR-0038 — EWBI auth drift from mTLS to OAuth2 Client-Credentials (Keycloak federation realm); mTLS deferred

See merge request !2
parents 2ec1168c 5bc7fce7
Loading
Loading
Loading
Loading
+6 −7
Changes for decisions/0016-fm-python-fastapi-stack.md: 6 added lines, 7 removed lines.
Original line number Diff line number Diff line
@@ -27,15 +27,14 @@ exception; (b) FM follows the platform stack — a single Python/FastAPI service

Option (b). FM is one Python/FastAPI service like every other OOP component, following the same
hexagonal `python-template` layout and importing oop-commons. Nginx (already in front of every
service) terminates TLS/mTLS and forwards the client certificate to FM in a header; FM middleware
validates the certificate thumbprint. Outbound EWBI mTLS uses `httpx` with per-partner client
certificates. There is no Envoy sidecar and no Go control plane.
service) terminates HTTPS and routes the canonical EWBI path to FM. OAuth2 Client-Credentials is
the current EWBI authorization mechanism (ADR-0042); `httpx` handles outbound token acquisition
and Bearer-authenticated partner calls. There is no Envoy sidecar and no Go control plane.

## Consequences

Single-language stack: shared Pydantic models, shared auth/logging/health middleware, shared
developer knowledge; one deployable, one scaling model (stateless horizontal pods). Cost: FM does
not get Envoy's battle-tested mTLS/filter machinery — certificate handling and header stripping
are FM application code and must be covered by the topology-leakage and certificate-validation
test suites. Revisit trigger: EWBI traffic or mTLS policy complexity (revocation, SPIFFE-style
rotation) outgrowing what Nginx + FastAPI middleware can express.
not get Envoy's policy/filter machinery, so Bearer-token validation and header stripping are FM
application code and require security tests. If mTLS sender-binding is added later (ADR-0042),
revisit whether its revocation and rotation policy still fits Nginx + FastAPI.
+48 −0
Changes for decisions/0042-ewbi-oauth2-client-credentials-mtls-deferred.md: 48 added lines, 0 removed lines.
Original line number Diff line number Diff line
---
type: Decision Record
title: "ADR-0042: EWBI authorization is OAuth2 Client-Credentials; mTLS is deferred defense-in-depth"
description: "FM authorizes E/WBI requests with OAuth2 Client-Credentials + signed JWT (OPG.04 Sec. 9); our Keycloak issues inbound tokens, each partner's Authorization Server issues outbound tokens, and mTLS is deferred"
tags: [adr, fm, federation, security]
timestamp: 2026-08-06T00:00:00Z
status: "accepted"
---

# ADR-0042: EWBI authorization is OAuth2 Client-Credentials; mTLS is deferred defense-in-depth

**Status:** accepted
**Date:** 2026-08-06
**Affects:** [fm/requirements-and-design.md](/fm/requirements-and-design.md), [fm/overview-and-architecture.md](/fm/overview-and-architecture.md), [fm/implementation-and-delivery.md](/fm/implementation-and-delivery.md), [fm/persistence-model.md](/fm/persistence-model.md), [platform/architecture.md](/platform/architecture.md), [platform/future-architecture.md](/platform/future-architecture.md), [ADR-0016](0016-fm-python-fastapi-stack.md)

## Context

FM's specs (REQ-FED-01, ADR-0016) currently describe mTLS with pre-exchanged client certificates as *the* E/WBI authorization mechanism: Nginx terminates mTLS and forwards the client-certificate thumbprint, FM looks it up in `partner_ops`.

Checked against GSMA OPG.04 v6.0's actual normative artifact — the OpenAPI 3.0.3 contract GSMA publishes alongside the PRD (Annex A; distributed separately as `OPG.04_EWBI_Federation_API_v1.4.0.yaml`), not just the PDF prose — the E/WBI Federation Management Service declares one root-level `security` requirement covering all 74 operations, with zero per-operation overrides and no mTLS securityScheme anywhere in the document:

```yaml
security:
  - oAuth2ClientCredentials: [fed-mgmt]
  - notifClientCredentials: [fed-mgmt-notif]
```

Sec. 9.2 assigns roles (Partner OP = Authorization + Resource Server, Originating OP = Client); Sec. 9.3 mandates signed JWT (JWS, RFC 7515); Sec. 9.5 requires the same grant for callbacks. Sec. 9's opening paragraph has one internally-contradictory hedge ("if OAuth2 is used") against otherwise-unconditional language throughout the rest of Sec. 9 and the contract itself — read as a drafting slip, not real optionality.

GSMA OPG.02 v10.0 Sec. 4.2.3 / Sec. 3.3.4.5 separately and explicitly sanctions certificate-based mutual authentication ("commonly used API authentication mechanisms, e.g. Public Key Certificates") for OP-to-OP *connection* identity — legitimate, but a different layer than Sec. 9's per-request *authorization*, and not what OPG.04's contract checks.

FM does not currently implement OAuth2/JWT for E/WBI at all. Real Nginx mTLS termination also does not exist yet — today's cert-thumbprint header is not derived from any actual TLS client-certificate handshake, so it proves no identity cryptographically; it is a partner lookup, not authentication.

The platform already runs a centralized Keycloak as core shared infrastructure (`core` Compose profile: PostgreSQL, Redis, NATS, Keycloak, Nginx; `platform/architecture.md` Security Architecture table), currently serving OEG's NBI auth and FM's own internal admin routes (both realm `oop`). This ADR extends that existing pattern rather than introducing a new one.

## Decision

1. **E/WBI authorization is OAuth2 Client-Credentials + signed JWT (OPG.04 Sec. 9).** For inbound calls, the partner is a client of our platform's centralized Keycloak, using the dedicated `federation` realm; FM validates the resulting token. For outbound calls, FM is a client of the partner OP's Authorization Server and obtains the token from that partner's token endpoint. The local `federation` realm stays separate from the `oop` realm used for internal admin and app-provider authentication. Each partner gets its own local Keycloak client with the `fed-mgmt` scope (`fed-mgmt-notif` for callbacks).
2. **mTLS is not built and is reclassified as deferred defense-in-depth.** No certificate thumbprint or client-certificate path is retained in today's `partner_ops` schema. When sender-constrained tokens are designed (for example RFC 8705), the required trust and certificate fields will be introduced with that design. Today's caller-supplied thumbprint header proves no identity and is removed completely.
3. Signing is JWS (RFC 7515), matching Keycloak's default and OPG.04 Sec. 9.3's primary recommendation — not the Sec. 9.3 pre-shared-symmetric-key (MAC) alternative, which only earns its keep when there is no real Authorization Server to lean on. There is one.

## Consequences

FM gains a real dependency on the centralized Keycloak for inbound EWBI authorization (already a platform dependency for OEG and FM's own admin routes, so no new infrastructure). FM must implement inbound Bearer-JWT validation (signature via Keycloak's JWKS, issuer, expiry, `fed-mgmt` scope) and outbound Client-Credentials token acquisition and caching before calling a partner. `partner_ops` stores the partner's inbound client identity plus our outbound client ID, token endpoint, and a reference to the mounted secret; it stores no OAuth client secret inline.

Cost: FM does not get to skip Authorization Server work by leaning on Nginx alone — Nginx's role here stays limited to HTTPS termination and routing, not the authorization decision. OPG.04 Sec. 9 requires TLS and OAuth2 but defines no mTLS OpenAPI security scheme. If the project later adds RFC 8705 sender-binding, that is a separate project decision grounded in OPG.02 Sec. 4.2.3/3.3.4.5, not a claim that OPG.04 currently mandates mTLS.

This ADR does not change FM's stack choice (ADR-0016 stands); it corrects only the E/WBI authorization mechanism ADR-0016 assumed in passing.
+1 −0
Changes for decisions/index.md: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -44,3 +44,4 @@ the reasoning lives here. Never renumber; supersede instead (see the record-deci
* [ADR-0039](0039-source-spec-stated-by-publisher.md) - source_spec is stated by the publishing component (OEG from CAMARA, FM from OPG.04) on network capability commands; SRM never derives it from the envelope source and never synthesizes a family, API, or version
* [ADR-0040](0040-target-serveability-is-a-tri-state.md) - can_serve_target answers can_serve | unknown_subscriber | unsupported_identifier; a boolean cannot carry the 404/422 split §E.2 requires, and 404 needs positive evidence (supersedes ADR-0038's boolean)
* [ADR-0041](0041-tfsdk-owns-adapter-construction.md) - TF-SDK builds adapters behind a factory keyed by adapter kind; SRM resolves control_path_ref to an instance but never enumerates adapter kinds
* [ADR-0042](0042-ewbi-oauth2-client-credentials-mtls-deferred.md) - E/WBI authorization is OAuth2 Client-Credentials: local Keycloak for inbound, partner Authorization Server for outbound; mTLS deferred
+147 −80

File changed.

Preview size limit exceeded, changes collapsed.

+98 −40

File changed.

Preview size limit exceeded, changes collapsed.

Loading