OpenID for Verifiable Presentations
OpenID for Verifiable Presentations (OID4VP) is a protocol that enables the presentation of verifiable credentials over the internet. The IDK implements OID4VP 1.0 Final with DCQL (Digital Credentials Query Language) for credential requests.
What is OID4VP?
OID4VP extends OAuth 2.0 to support the presentation of verifiable credentials. It allows a holder to present credentials from their digital wallet to a verifier in a standardized, interoperable way. The protocol supports multiple credential formats including ISO mDoc, SD-JWT VC, and W3C Verifiable Credentials.
The protocol defines two primary roles:
| Role | Description |
|---|---|
| Holder | The entity possessing credentials in a wallet application |
| Verifier | The relying party requesting and validating credential presentations |
Protocol Flow
A typical OID4VP presentation follows these steps:
Request Modes
OID4VP supports multiple ways to deliver the authorization request:
Same-Device Flow
The authorization request is delivered directly via a URL or deep link. This is common when the user is already on a mobile device with their wallet app.
Cross-Device Flow
The authorization request is encoded in a QR code displayed by the verifier. The user scans this with their wallet app, creating a bridge between the verifier's session and the mobile wallet.
Request URI
Instead of embedding the full request in the URL, the verifier provides a request_uri pointing to the full request object. This keeps URLs short and allows for signed request objects.
Credential Formats
The IDK supports multiple credential formats within OID4VP:
| Format | Description |
|---|---|
mso_mdoc | ISO/IEC 18013-5 mobile driving license format |
dc+sd-jwt | SD-JWT based verifiable credentials |
jwt_vc_json | JWT-encoded W3C Verifiable Presentations |
DCQL Queries
The verifier specifies what credentials it needs using DCQL (Digital Credentials Query Language). DCQL provides a structured way to describe:
- Required credential types and formats
- Specific claims/attributes needed
- Which claims are required versus optional
- Alternative credentials that can satisfy a requirement
See the DCQL documentation for detailed query syntax and examples.
Response Modes
OID4VP supports different response delivery mechanisms:
| Response Mode | Description |
|---|---|
direct_post | Response POSTed to verifier's endpoint (recommended) |
direct_post.jwt | JWT-secured response POSTed to verifier |
fragment | Response in URL fragment |
query | Response in URL query parameters |
The direct_post mode is recommended for production as it provides better security and supports larger payloads.
Core Components
- Android/Kotlin
- iOS/Swift
// Holder-side component for wallet operations
val holder: Oid4vpHolder = session.component.oid4vpHolder
// Verifier-side component for relying party operations
val rp: Oid4vpRpService = session.component.oid4vpRpService
// Holder-side component for wallet operations
let holder: Oid4vpHolder = session.component.oid4vpHolder
// Verifier-side component for relying party operations
let rp: Oid4vpRpService = session.component.oid4vpRpService
Holder Operations
The holder processes authorization requests from verifiers:
- Android/Kotlin
- iOS/Swift
// 1. Parse authorization request from QR or deep link
val parseResult = holder.parseAuthorizationRequest(
requestUri = "openid4vp://authorize?...",
walletConfig = WalletConfig(audience = "https://wallet.example.com")
)
// 2. Resolve request (fetch metadata, validate client)
val resolveResult = holder.resolveAuthorizationRequest(parseResult.value)
// 3. Create response with selected credentials
val responseResult = holder.createAuthorizationResponse(
request = resolveResult.value,
selectedCredentials = selectedCredentials
)
// 4. Submit response to verifier
val submitResult = holder.submitAuthorizationResponse(
resolvedRequest = resolveResult.value,
response = responseResult.value
)
// 1. Parse authorization request from QR or deep link
let parseResult = try await holder.parseAuthorizationRequest(
requestUri: "openid4vp://authorize?...",
walletConfig: WalletConfig(audience: "https://wallet.example.com")
)
// 2. Resolve request (fetch metadata, validate client)
let resolveResult = try await holder.resolveAuthorizationRequest(
request: parseResult.value
)
// 3. Create response with selected credentials
let responseResult = try await holder.createAuthorizationResponse(
request: resolveResult.value,
selectedCredentials: selectedCredentials
)
// 4. Submit response to verifier
let submitResult = try await holder.submitAuthorizationResponse(
resolvedRequest: resolveResult.value,
response: responseResult.value
)
Verifier Operations
The verifier creates requests and validates responses:
- Android/Kotlin
- iOS/Swift
// 1. Create authorization request with DCQL query
val createResult = rp.createAuthorizationRequest(
CreateAuthorizationRequestArgs(
dcqlQuery = dcqlQuery,
clientId = "https://verifier.example.com",
responseUri = "https://verifier.example.com/response",
responseMode = ResponseMode.DIRECT_POST,
nonce = generateNonce()
)
)
// 2. Build URI for QR code or redirect
val uriResult = rp.buildAuthorizationRequestUri(
BuildAuthorizationRequestUriArgs(
request = createResult.value.request,
scheme = Oid4vpUriScheme.OPENID4VP
)
)
// 3. Parse incoming response
val parseResult = rp.parseAuthorizationResponse(parseArgs)
// 4. Validate response against request
val validateResult = rp.validateAuthorizationResponse(validateArgs)
// 5. Verify holder binding
val bindingResult = rp.verifyHolderBinding(bindingArgs)
// 1. Create authorization request with DCQL query
let createResult = try await rp.createAuthorizationRequest(
args: CreateAuthorizationRequestArgs(
dcqlQuery: dcqlQuery,
clientId: "https://verifier.example.com",
responseUri: "https://verifier.example.com/response",
responseMode: .directPost,
nonce: generateNonce()
)
)
// 2. Build URI for QR code or redirect
let uriResult = try await rp.buildAuthorizationRequestUri(
args: BuildAuthorizationRequestUriArgs(
request: createResult.value.request,
scheme: .openid4vp
)
)
// 3. Parse incoming response
let parseResult = try await rp.parseAuthorizationResponse(args: parseArgs)
// 4. Validate response against request
let validateResult = try await rp.validateAuthorizationResponse(args: validateArgs)
// 5. Verify holder binding
let bindingResult = try await rp.verifyHolderBinding(args: bindingArgs)
Client ID Schemes
OID4VP supports multiple ways for verifiers to identify themselves:
| Scheme | Description |
|---|---|
redirect_uri | Client ID equals the response URI |
x509_san_dns | X.509 certificate with DNS SAN |
x509_san_uri | X.509 certificate with URI SAN |
verifier_attestation | Attestation JWT from trusted party |
decentralized_identifier | DID-based identity |
openid_federation | OpenID Federation entity |
Security Features
OID4VP in the IDK includes several security mechanisms:
Request Object Signing ensures the authorization request hasn't been tampered with. The holder validates the signature against the verifier's published keys.
Response Encryption protects the presented credentials from interception when using direct_post.jwt mode.
Client ID Scheme Validation verifies the verifier's identity according to the declared scheme.
Nonce Binding prevents replay attacks by binding the presentation to a specific transaction.
Response Code Protection for direct_post mode prevents response theft via one-time codes.
Next Steps
- Holder Implementation - Build wallet functionality for presenting credentials
- Verifier Implementation - Create relying party verification flows
- Universal API - Simplified backend API for web applications
- DCQL Queries - Use Digital Credentials Query Language for credential requests