Facade that exposes the Prometheus-X / Visions consent-manager contract-service API on top of the FIWARE TM Forum APIs.
The consent-manager derives its privacy notices and consents from a contract service (configured through its
CONTRACT_SERVICE_BASE_URL). In the FIWARE Data Space Connector there is no dedicated bilateral contract between a user
and a provider - the commercial relationship is a TM Forum ProductOrder (its relatedParty is the data consumer)
for a ProductOffering backed by a ProductSpecification. This service projects that world into the shape the
consent-manager expects:
- a bilateral contract per product order (
/bilaterals/...), - the catalog graph the contracts dereference - service offerings (
/catalog/serviceofferings/{id}), data resources (/catalog/dataresources/{id}), software resources (/catalog/softwareresources/{id}), - the participant self-descriptions (
/participants/{id}) read while building consent receipts.
It replaces the proof-of-concept contract-facade (a small Node script) shipped with the DSC (UF-10).
The API provided towards the consent-manager is defined in api/consent-facade.yaml. The
endpoints mirror exactly what the consent-manager's contract-service client calls:
| Method & path | Purpose |
|---|---|
GET /bilaterals/for/{participantId}?hasSigned=true |
bilateral contracts a participant is party to |
GET /bilaterals/{contractId} |
a single bilateral contract |
GET /contracts/for/{participantId}?hasSigned=true |
ecosystem contracts a participant is party to - 501, not implemented |
GET /contracts/{contractId} |
a single ecosystem contract - 501, not implemented |
GET /verify/{providerId}/{consumerId} |
verify a signed contract exists between two participants |
GET /catalog/serviceofferings/{id} |
service-offering self-description (dataResources / softwareResources) |
GET /catalog/dataresources/{id} |
data-resource self-description |
GET /catalog/softwareresources/{id} |
software-resource self-description (its name becomes the purpose) |
GET /participants/{id} |
participant self-description (legalName, legalPerson.legalAddress) |
{participantId},{providerId}and{consumerId}are the participants' self-description identifiers, base64-encoded (standard or URL-safe alphabet), exactly as the consent-manager passes them. Encoding is required, not sniffed: a value that is not base64 gets a400. It cannot be guessed - any alphanumeric slug whose length is a multiple of four is itself valid base64 and would decode to a different string, silently matching nothing.
Ids the facade mints (
{contractId}, all/catalog/*ids) are provider-scoped (providerKey~localId) and are echoed back verbatim; one carrying/,?,#,\or..is refused with a400.
A failure reaching a provider's TM Forum backend is reported as
502, with the backend's status and body kept out of the response.
Defined in their own spec, api/consent-facade-internal.yaml -
separate from api/consent-facade.yaml (the contract towards the consent-manager) and
never published:
| Method & path | Purpose |
|---|---|
POST /internal/tokens |
mint an OID4VP access token for a configured audience |
| `GET | POST |
POST /internal/tokens lets components that do not implement OID4VP - notably the Go consent-plugin -
authenticate as this participant:
POST /internal/tokens { "audience": "consent-manager" }
200 { "access_token": "…", "token_type": "Bearer", "expires_in": 3540 }
The audience is a name resolved against oid4vp.token-targets, never a caller-supplied URL: a
caller that could name any host would make the facade present this participant's credential to it.
Unknown or blank audience ⇒ 400; credential refused ⇒ 403; verifier unreachable ⇒ 502; broken
local OID4VP setup ⇒ 500. Tokens are cached per audience and refreshed before expiry, and
concurrent misses are coalesced onto one presentation; expires_in is the token's real remaining
lifetime, so a caller may cache on it. The same cache backs the outbound TM Forum calls, so those cost
one authenticated request rather than an unauthorized round trip plus a full presentation each. Only
active when oid4vp.enabled=true. See
doc/adr/0003.
Both specs generate Micronaut server interfaces at build time; the internal one into
org.fiware.consent.internal.api / .model, implemented by InternalTokenController and
ProviderAdminController.
Example configuration:
oid4vp:
enabled: true
token-targets:
# `client-id` is the verifier SERVICE the token is for; `scope` selects that
# service's credential policy (here: present a participant credential).
- audience: consent-manager
url: https://verifier.dataspace-authority.org
client-id: consent-manager
scope: [ "participant" ]- Micronaut 4 (Java 21)
- API-first: server interfaces and TM Forum clients are generated from OpenAPI with the openapi-generator + kokuwa micronaut codegen
- jib for the container image
- Micrometer with a Prometheus registry
Package layout (following fiware/contract-management):
api/consent-facade.yaml # the API provided towards the consent-manager
src/main/java/org/fiware/consent/
Application.java
configuration/ # @ConfigurationProperties
facade/ # controllers implementing the generated server API
tmforum/ # adapters over the generated TM Forum clients (TMForumBackedRepository)
mapping/ # mappers TM Forum <-> contract model
provider/ # provider registry, admin API and per-provider client factory
auth/ # OID4VP authentication and the internal token endpoint
exception/ # exception handlers (backend failure -> 502, bad id -> 400)
src/main/resources/application.yaml
mvn clean verifyGenerated sources land in target/generated-sources/openapi (server API under org.fiware.consent.api / .model,
TM Forum clients under org.fiware.consent.tmforum.*).
mvn mn:run
# or
java -jar target/consent-facade-*.jarPoint it at the TM Forum APIs and set its own public url via application.yaml (or the corresponding env vars):
micronaut:
http:
services:
product-catalog: { url: http://tm-forum-api:8080 }
product-inventory: { url: http://tm-forum-api:8080 }
party: { url: http://tm-forum-api:8080 }
agreement: { url: http://tm-forum-api:8080 }
facade:
self-url: http://consent-facade:8080mvn clean package -Poci # build locally
mvn clean deploy -Poci -Dimage.tag=... # build & push (CI)Image: quay.io/seamware/consent-facade.
Implemented and tested: the controllers and mappers that project TM Forum payloads into the contract and catalog
self-descriptions (facade, tmforum, mapping), multi-provider routing on top of them (provider), OID4VP
authentication of outbound calls and the internal token endpoint (auth).
Not implemented: the ecosystem-contract endpoints (GET /contracts/for/{participantId}, GET /contracts/{id})
answer 501 - ecosystem contracts have no TM Forum source yet.
Management endpoints are served on their own port (endpoints.all.port, default 9090), never on the public listener:
| Path | |
|---|---|
/health |
liveness/readiness |
/prometheus |
Prometheus scrape endpoint |
/metrics |
metric names and values |
Everything else there (info, beans, env, …) stays sensitive and answers 401.
REQUIREMENTS.md is the architecture document, not a wish list: it holds the exact
contract-service API the consent-manager consumes (with source references), the contract/catalog data models, the data
granularity model, the TM Forum projection, the multi-provider design (§11), and the verified gotchas. Around twenty
Javadoc comments cross-reference its sections, so it is load-bearing - keep it in step with the code.
implementation-plan.md is the record of how the service was built, kept for the rationale
behind the decisions rather than as a list of open work.
Apache License, Version 2.0 (see LICENSE). Six files in auth/ are adapted from
FIWARE/contract-management (Apache-2.0); see NOTICE.
Every hand-written Java file carries the Apache-2.0 header from
license-header.txt. This is enforced in CI - the Check code style and license step runs
mvn clean spotless:check - so a file without the header fails the build. Run mvn spotless:apply to add it.