feat(vault): read access credentials from X-DPP-Credential - #60
feat(vault): read access credentials from X-DPP-Credential#60LKSNDRTMLKV wants to merge 2 commits into
Conversation
Collapses the sector country fields onto countryOfOrigin across code, CSV templates and demo data, adopts the Gtin newtype and the Audience/Disclosure disclosure model, and binds every dpp-render section fixture through the typed SectorData so a renamed field fails a test instead of silently rendering a dash.
A dedicated header rather than a third Authorization scheme, so a credential composes with an API key instead of competing for the same slot; parses and claims-checks the credential but deliberately injects the presented credential rather than an Audience, because signature, issuer trust and revocation are all still unchecked and a bare Audience in extensions would look authoritative while being trivially forgeable.
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 17 |
| Duplication | 0 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
|
Follow-up found while scoping chunk 02 — worth knowing before you merge this.
That makes this PR's central design choice load-bearing rather than cautious: injecting It also means the wire format here is provisional. The module accepts raw credential JSON; if the envelope decision lands on VC-JWT — a compact JWS whose payload is the credential — this layer unwraps the JWS first and the header contract changes. That is a small, contained change to one function, but it is a change. Filed as |
|
Folded into #63, which ships the whole credential path as one feature — transport, trusted issuers, signature verification, revocation and the audience-scoped read. The commits from this branch are in that PR's history. |
Chunk 01 of the audience-access campaign (
dpp-docs/work/AUDIENCE-ACCESS-PLAN.md).What
Reads an access credential from a dedicated
X-DPP-Credentialheader, parses it, and checks the claims core can check without a network — structure and thevalidFrom/validUntilwindow.Nothing gates on it yet. No route changes, no disclosure filtering. This is transport only.
Why a dedicated header, not a third
AuthorizationschemeAuthorizationalready routes by scheme:Bearer→ API-key providers,Basic→ local admin. A credential is a different axis — it says who the reader is, not whether the caller may write. A machine integration can hold both, so they must not compete for one slot.Why it injects a credential and not an
AudienceThe plan's green test said "resolves to an
Audience". I deliberately did not do that.Anyone can mint this JSON. A bare
Audiencein request extensions would look authoritative while being trivially forgeable, and chunk 04 would gate disclosure on it. Instead the layer injectsPresentedCredential, a distinct type whose docs state plainly that it is not a grant, withclaimed_audience()as an explicit method rather than a value that circulates as if it were a decision.Correction to the campaign plan
The plan claimed core ships a complete credential stack. It does not, and this is worth knowing before chunk 02:
verify_credential_claimsdocuments itself as "no signature check — that is the JWS verifier's responsibility"CredentialBuilder::build()returns an unsigned credentialCore owns claims, trust policy and revocation decode. The engine owes signature verification.
dpp_crypto::jws::verify_jwsexists but needs the issuer's public key, so it needsdid:webresolution — real work the plan attributed to core. Recorded in the module docs.Consequence: signature verification must land before chunk 04, otherwise trust and revocation checks guard a credential whose issuer field is unauthenticated.
Public access stays frictionless
An absent header returns
Absent, never an error. ESPR Art. 11(b) requires access free of charge, and the toy and detergent regulations forbid requiring consumers to register or supply a password — so the unauthenticated read is the norm, not a degraded case. There is a test asserting exactly that.Tests
6/6,
just checkgreen. Includes the Art. 77(2) lattice property asserted at the point where roles first become audiences in the engine: an authority does not gain Annex XIII point 4, a recycler does not gain point 3.