Skip to content

feat(vault): read access credentials from X-DPP-Credential - #60

Closed
LKSNDRTMLKV wants to merge 2 commits into
mainfrom
feat/credential-transport
Closed

feat(vault): read access credentials from X-DPP-Credential#60
LKSNDRTMLKV wants to merge 2 commits into
mainfrom
feat/credential-transport

Conversation

@LKSNDRTMLKV

Copy link
Copy Markdown
Member

Chunk 01 of the audience-access campaign (dpp-docs/work/AUDIENCE-ACCESS-PLAN.md).

What

Reads an access credential from a dedicated X-DPP-Credential header, parses it, and checks the claims core can check without a network — structure and the validFrom/validUntil window.

Nothing gates on it yet. No route changes, no disclosure filtering. This is transport only.

Why a dedicated header, not a third Authorization scheme

Authorization already 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 Audience

The plan's green test said "resolves to an Audience". I deliberately did not do that.

Anyone can mint this JSON. A bare Audience in request extensions would look authoritative while being trivially forgeable, and chunk 04 would gate disclosure on it. Instead the layer injects PresentedCredential, a distinct type whose docs state plainly that it is not a grant, with claimed_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_claims documents itself as "no signature check — that is the JWS verifier's responsibility"
  • CredentialBuilder::build() returns an unsigned credential

Core owns claims, trust policy and revocation decode. The engine owes signature verification. dpp_crypto::jws::verify_jws exists but needs the issuer's public key, so it needs did:web resolution — 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 check green. 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.

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.
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 17 complexity · 0 duplication

Metric Results
Complexity 17
Duplication 0

View in Codacy

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.

@LKSNDRTMLKV

Copy link
Copy Markdown
Member Author

Follow-up found while scoping chunk 02 — worth knowing before you merge this.

DppAccessCredential has no proof and no jws member. It cannot carry a signature at all, and SignedCredential in dpp-domain wraps PassportCredential, not this type. So there is no signed representation of an access credential anywhere in the workspace, and the issuer field is a self-asserted string.

That makes this PR's central design choice load-bearing rather than cautious: injecting PresentedCredential instead of an Audience is the only safe option, because the document being parsed cannot currently be authenticated by any means.

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 dpp-core#69 with the two envelope options. Nothing in this PR needs to change to merge; it gates nothing, and the transport is needed either way.

@LKSNDRTMLKV

Copy link
Copy Markdown
Member Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant