Skip to content

Commit 98c5ab7

Browse files
committed
UPDATE documentation for OID4VCI and OID4VP protocols
1 parent 72bea65 commit 98c5ab7

13 files changed

Lines changed: 642 additions & 90 deletions

File tree

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,90 @@
11
---
22
title: "OID4VCI"
3-
description: "Verifiable credential issuance with SD-JWT VC and deferred delivery."
3+
description: "OpenID4VCI credential issuance with pre-authorized grants, proof binding, c_nonce freshness, and deferred delivery."
44
---
55

66
# OID4VCI
77

88
## Standards
99

10-
- OpenID for Verifiable Credential Issuance (OID4VCI) 1.0
10+
- OpenID for Verifiable Credential Issuance 1.0 (final)
11+
- OAuth 2.0 Authorization Framework (token and grant semantics)
12+
- SD-JWT VC profile (`dc+sd-jwt` credential format)
1113

1214
## Available Flows
1315

1416
| Flow ID | Name | Description |
1517
|---------|------|-------------|
16-
| `oid4vci-pre-authorized` | Pre-Authorized Code | Credential issuance with pre-authorized grant |
17-
| `oid4vci-pre-authorized-tx-code` | Pre-Authorized + tx_code | Issuance with transaction code challenge |
18-
| `oid4vci-deferred-issuance` | Deferred Issuance | Credential issued asynchronously via polling |
18+
| `oid4vci-pre-authorized` | Pre-Authorized Code | Wallet resolves offer, exchanges `pre-authorized_code`, and requests credential with proof |
19+
| `oid4vci-pre-authorized-tx-code` | Pre-Authorized + tx_code | Same flow with mandatory `tx_code` at token exchange |
20+
| `oid4vci-deferred-issuance` | Deferred Issuance | Credential endpoint returns `transaction_id`, then wallet polls deferred endpoint |
1921

2022
## Demo Scenarios
2123

22-
- **Pre-Authorized Issuance** -- Direct pre-authorized code flow
23-
- **Pre-Authorized + tx_code** -- Issuance with user-provided transaction code
24-
- **Deferred Issuance Polling** -- Deferred credential retrieval
24+
- **Pre-Authorized Issuance** -- Resolve by-reference offer, exchange token, submit proof, receive SD-JWT VC
25+
- **Pre-Authorized + tx_code** -- Enforce out-of-band `tx_code` before token issuance
26+
- **Deferred Issuance Polling** -- Receive `transaction_id`, poll until credential is ready
2527

2628
## Credential Details
2729

2830
| Property | Value |
2931
|----------|-------|
30-
| Credential type | `UniversityDegreeCredential` |
32+
| Credential configuration ID | `UniversityDegreeCredential` |
3133
| VCT | `https://protocolsoup.com/credentials/university_degree` |
3234
| Format | `dc+sd-jwt` (SD-JWT VC) |
35+
| Scope in token response | `vc:issue` |
36+
| Metadata `proof_types_supported` | `jwt` with `proof_signing_alg_values_supported: ["RS256"]` |
37+
38+
## Issuance Flow (Implementation)
39+
40+
1. Issuer creates a credential offer (by-reference or by-value), including `credential_issuer`, `credential_configuration_ids`, and pre-authorized grant data.
41+
2. Wallet resolves `credential_offer_uri` (for by-reference offers) and retrieves issuer metadata.
42+
3. Wallet calls token endpoint with `grant_type=urn:ietf:params:oauth:grant-type:pre-authorized_code`, `pre-authorized_code`, and `tx_code` when required.
43+
4. Token response includes `access_token`, `c_nonce`, and `c_nonce_expires_in`.
44+
5. Wallet submits credential request with `credential_configuration_id` and `proof`/`proofs` (`proof_type: jwt`) bound to current `c_nonce`.
45+
6. Issuer validates proof key binding, nonce freshness, audience/subject constraints, then returns:
46+
- immediate credential (`format`, `credential`, next `c_nonce`), or
47+
- deferred response (`transaction_id`, next `c_nonce`) for later polling.
48+
49+
## Contract Enforcement
50+
51+
- Credential offer envelope enforces XOR: exactly one of `credential_offer` or `credential_offer_uri`.
52+
- `tx_code` is required when the offer grant includes a `tx_code` object.
53+
- Proofs are required when proof types are declared for the credential configuration.
54+
- Proof JWT header `typ` must be `openid4vci-proof+jwt`.
55+
- Proof must include `iss`, `sub`, `aud`, `nonce`, `iat`, `exp`, and `cnf.jwk`.
56+
- Nonce is freshness-bound and one-time in issuance requests (replay and expiry are rejected).
57+
- Deferred polling requires matching `transaction_id` and same access token lineage.
3358

3459
## Endpoints
3560

3661
| Path | Methods | Purpose |
3762
|------|---------|---------|
38-
| `/oid4vci/.well-known/openid-credential-issuer` | GET | Issuer metadata |
39-
| `/.well-known/openid-credential-issuer/oid4vci` | GET | Issuer metadata (alternate) |
63+
| `/oid4vci/.well-known/openid-credential-issuer` | GET | Plugin-local metadata path (supported) |
64+
| `/.well-known/openid-credential-issuer/oid4vci` | GET | Canonical issuer-derived metadata path for issuer `.../oid4vci` |
4065
| `/oid4vci/credential-offer/{offerID}` | GET | Credential offer by reference |
4166
| `/oid4vci/offers/pre-authorized` | POST | Create pre-authorized offer |
4267
| `/oid4vci/offers/pre-authorized/by-value` | POST | Create offer (by value) |
4368
| `/oid4vci/offers/pre-authorized/deferred` | POST | Create deferred offer |
44-
| `/oid4vci/token` | POST | Token endpoint (`pre-authorized_code`, `authorization_code`) |
45-
| `/oid4vci/nonce` | POST | Nonce endpoint |
46-
| `/oid4vci/credential` | POST | Credential request |
47-
| `/oid4vci/deferred_credential` | POST | Deferred credential retrieval |
69+
| `/oid4vci/token` | POST | Token exchange (`pre-authorized_code` and `authorization_code`) |
70+
| `/oid4vci/nonce` | POST | Issue fresh `c_nonce` for valid access token |
71+
| `/oid4vci/credential` | POST | Validate proof and issue credential or deferred transaction |
72+
| `/oid4vci/deferred_credential` | POST | Poll deferred transaction until credential is ready |
73+
74+
## Implementation Profile Notes
75+
76+
- Token endpoint expects `application/x-www-form-urlencoded`; non-form content types are rejected. If `Content-Type` is omitted, request may still parse as form data.
77+
- Metadata for proof signing algorithms advertises `RS256`.
78+
- Proof verifier currently accepts RSA and EC wallet proof keys (`cnf.jwk`) and validates algorithm family alignment (`RS*` or `ES*`).
79+
- For deterministic guided demos, tx_code challenge value is generated as `123456` and surfaced through Looking Glass event data.
4880

4981
## What To Validate
5082

51-
- Credential offer: `credential_issuer`, `credential_configuration_ids`, `grants`
52-
- Token exchange: `pre-authorized_code` grant type, optional `tx_code`
53-
- Proof: key binding proof JWT with `nonce` from nonce endpoint
54-
- Credential response: SD-JWT VC with selective disclosure claims
55-
- Deferred: `transaction_id` in initial response, polling with `deferred_credential` endpoint
56-
- Wallet credential store: persisted at `{SHOWCASE_DATA_DIR}/vc/wallet_credentials.json`
83+
- Credential offer envelope: exactly one of `credential_offer` or `credential_offer_uri`
84+
- Metadata retrieval: canonical issuer-derived well-known path works for non-root issuer IDs
85+
- Token exchange: `pre-authorized_code` and `authorization_code` behaviors, plus `tx_code` enforcement when offered
86+
- Proof JWT: `typ=openid4vci-proof+jwt`, `cnf.jwk` key binding, `aud` matches issuer, `nonce` matches active `c_nonce`
87+
- Nonce behavior: replayed or expired `c_nonce` is rejected; next nonce is returned after successful issuance
88+
- Credential response: `dc+sd-jwt` serialization includes issuer-signed JWT and disclosures
89+
- Deferred flow: `issuance_pending` before ready time, then final credential on successful poll
90+
- Issuance lineage: wallet credential store persistence at `{SHOWCASE_DATA_DIR}/vc/wallet_credentials.json`
Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,32 @@
11
---
22
title: "OID4VP"
3-
description: "Verifiable presentation verification with DCQL queries and wallet submission."
3+
description: "OpenID4VP verifier flows with DCQL request contracts, direct_post transports, and policy-based presentation evaluation."
44
---
55

66
# OID4VP
77

88
## Standards
99

10-
- OpenID for Verifiable Presentations (OID4VP) 1.0
10+
- OpenID for Verifiable Presentations 1.0 (final)
11+
- OAuth 2.0 Authorization Framework (request/response parameter model)
12+
- JOSE (JWT/JWS/JWE processing for request objects and `direct_post.jwt`)
13+
- DID Core / did:web (for `decentralized_identifier` client_id scheme handling)
1114

1215
## Available Flows
1316

1417
| Flow ID | Name | Description |
1518
|---------|------|-------------|
16-
| `oid4vp-direct-post` | DCQL + direct_post | DCQL query with direct_post response mode |
17-
| `oid4vp-direct-post-jwt` | DCQL + direct_post.jwt | DCQL query with encrypted JWT response |
19+
| `oid4vp-direct-post` | DCQL + direct_post | Verifier creates signed request object; wallet posts `vp_token` + `state` to `response_uri` |
20+
| `oid4vp-direct-post-jwt` | DCQL + direct_post.jwt | Wallet posts encrypted `response` JWE containing signed `oauth-authz-resp+jwt` payload |
1821

1922
## Demo Scenarios
2023

21-
- **DCQL direct_post** -- Standard presentation flow
22-
- **DCQL direct_post.jwt** -- Encrypted response variant
24+
- **DCQL direct_post** -- Create request object, hand off to wallet, evaluate verifier policy
25+
- **DCQL direct_post.jwt** -- Same flow with encrypted response transport and inner JWT validation
2326

2427
## DCQL Query
2528

26-
The default query requests a `university_degree` credential with `degree` and `graduation_year` claims:
29+
Default query (used when neither `dcql_query` nor `scope` is supplied) requests a `university_degree` credential with `degree` and `graduation_year` claims:
2730

2831
```json
2932
{
@@ -40,24 +43,63 @@ The default query requests a `university_degree` credential with `degree` and `g
4043
}
4144
```
4245

46+
## Request and Response Contracts
47+
48+
- Authorization request enforces XOR: exactly one of `dcql_query` or `scope`.
49+
- For `response_mode` `direct_post` and `direct_post.jwt`, `response_uri` is required and `redirect_uri` must be absent.
50+
- Request object header `typ` must be `oauth-authz-req+jwt`.
51+
- VP token header `typ` must be `vp+jwt`.
52+
- `direct_post.jwt` inner response header `typ` must be `oauth-authz-resp+jwt`.
53+
4354
## Client ID Schemes
4455

45-
- `redirect_uri` -- standard redirect-based client identification
46-
- `did:web` -- DID Web method for decentralized verifier identity
56+
- Supported in MVP profile:
57+
- `redirect_uri:` (prefix form)
58+
- `decentralized_identifier:` with `did:web`
59+
- `decentralized_identifier:did:web` requests are resolved and validated against runtime DID document trust checks:
60+
- URL derivation from DID syntax
61+
- host allowlist enforcement
62+
- DID document fetch and parse
63+
- DID document `id` match and verification material presence
64+
65+
## Verifier Evaluation (Implementation)
66+
67+
1. Verifier creates request object (`request`) and `request_uri`, binding fresh `nonce` and `state`.
68+
2. Wallet fetches request object from `request_uri` (GET or POST retrieval supported).
69+
3. Wallet submits response:
70+
- `direct_post`: `vp_token` + `state`
71+
- `direct_post.jwt`: `response` JWE (RSA-OAEP + A256GCM), decrypted to signed inner response JWT
72+
4. Verifier resolves session by `state`, validates expiry, and evaluates presentation policy:
73+
- nonce binding
74+
- audience binding
75+
- token expiry
76+
- holder binding (`iss`/`sub` and `cnf.jkt` consistency)
77+
- credential lineage/signature and required DCQL claim-path checks
78+
5. Result is stored and returned as a policy decision (`allowed`, `code`, `message`, `reasons`, `reason_codes`).
4779

4880
## Endpoints
4981

5082
| Path | Methods | Purpose |
5183
|------|---------|---------|
5284
| `/oid4vp/request/create` | POST | Create authorization request |
53-
| `/oid4vp/request/{requestID}` | GET, POST | Retrieve/update authorization request |
54-
| `/oid4vp/response` | POST | Wallet response (`direct_post`, `direct_post.jwt`) |
55-
| `/oid4vp/result/{requestID}` | GET | Verification result |
85+
| `/oid4vp/request/{requestID}` | GET, POST | Retrieve request object by request URI |
86+
| `/oid4vp/response` | POST | Wallet callback (`direct_post` or `direct_post.jwt`) |
87+
| `/oid4vp/result/{requestID}` | GET | Retrieve verification status (`pending`/`completed`) and result |
88+
89+
## Implementation Profile Notes
90+
91+
- Request objects are signed in current implementation even when using `redirect_uri:` client_id scheme.
92+
- In OpenID4VP final profile language, `redirect_uri:` has limitations for signed-request trust models; treat this as current implementation profile behavior and validate interop requirements per relying wallet.
93+
- For `direct_post.jwt`, implementation expects encrypted `response` using `RSA-OAEP` + `A256GCM` and validates inner response JWT audience against `response_uri`.
94+
- Wallet response parser accepts JSON and form payloads; protocol requirements for direct_post transports still apply.
5695

5796
## What To Validate
5897

59-
- Authorization request: signed JWT with `client_id`, `nonce`, `dcql_query`, `response_mode`
60-
- Request URI: `request_uri` for wallet to fetch the full request
61-
- Wallet response: `vp_token` and `presentation_submission` matching DCQL query
62-
- Verification result: credential validation, claim extraction, proof verification
63-
- Response binding: `state` and `nonce` alignment between request and response
98+
- Authorization request: signed request object with `typ=oauth-authz-req+jwt`, `client_id`, `response_mode`, `response_uri`, `nonce`, `state`, `exp`
99+
- DCQL contract: exactly one of `dcql_query` or `scope`; required claim paths derived from DCQL are enforced
100+
- Client identification: only supported scheme matrix accepted (`redirect_uri`, `decentralized_identifier`)
101+
- `direct_post` response: `state` maps to active request and `vp_token` is present
102+
- `direct_post.jwt` response: decrypts successfully, inner JWT signature/type/audience/subject/expiry checks pass
103+
- VP token checks: `typ=vp+jwt`, nonce match, audience match, expiry valid, holder binding verified
104+
- Credential evidence checks: presented credential subject/issuer/signature/lineage align with stored issuance record and required claim disclosures
105+
- Result endpoint: returns `pending` until callback is processed, then `completed` with policy decision object

0 commit comments

Comments
 (0)