Skip to main content
Version: v0.13

Universal OID4VP

The Universal OID4VP module provides a backend-focused API for web applications and services that need to verify credentials without managing the low-level protocol details.

Overview

While the standard Verifier API gives you full control over the OID4VP protocol, the Universal API simplifies common use cases:

  • Session Management - Automatic session creation and lifecycle
  • QR Code Generation - Built-in QR code creation with customizable styling
  • Status Polling - Simple endpoint to check verification status
  • Webhook Callbacks - Receive notifications when verification completes

Use Cases

The Universal API is ideal for:

ScenarioDescription
Web ApplicationsDisplay QR code, poll for result, redirect on success
Kiosk SystemsStateless verification with QR display
Backend ServicesWebhook-based verification for async workflows
Mobile WebSame-device flows with deep links

Architecture

Universal OID4VP Architecture

REST Endpoints

Create Authorization Request

Creates a new OID4VP authorization session.

POST /oid4vp/backend/auth/requests
Content-Type: application/json

{
"query_id": "age-verification",
"client_id": "https://verifier.example.com",
"callback": {
"url": "https://my-app.example.com/webhook",
"statuses": ["AUTHORIZATION_RESPONSE_VERIFIED"],
"include_verified_data": true
},
"ttl_seconds": 600,
"qr_code": {
"size": 400,
"color_dark": "#000000",
"color_light": "#ffffff"
}
}

Request Fields:

FieldTypeRequiredDescription
query_idstringEither query_id or dcql_queryReference to pre-configured DCQL query
dcql_queryobjectEither query_id or dcql_queryInline DCQL query
client_idstringNoOverride default client ID
callbackobjectNoWebhook configuration
ttl_secondsnumberNoSession TTL (default: 600)
qr_codeobjectNoQR code styling options

Response:

{
"correlation_id": "sess-abc123",
"query_id": "age-verification",
"request_uri": "openid4vp://authorize?request_uri=https%3A%2F%2Fverifier.example.com%2Foid4vp%2Frequests%2Fsess-abc123",
"qr_code_content": "openid4vp://authorize?request_uri=...",
"qr_code_data_uri": "data:image/png;base64,iVBORw0KGgo...",
"status_uri": "https://verifier.example.com/oid4vp/backend/auth/requests/sess-abc123",
"expires_at": 1704200400000,
"status": "CREATED"
}

Get Authorization Request Status

Check the current status of an authorization session.

GET /oid4vp/backend/auth/requests/{correlationId}

Response:

{
"correlation_id": "sess-abc123",
"query_id": "age-verification",
"status": "AUTHORIZATION_RESPONSE_VERIFIED",
"created_at": 1704199800000,
"last_updated": 1704199850000,
"expires_at": 1704200400000,
"verified_data": {
"credentials": [
{
"id": "age_over_18",
"format": "dc+sd-jwt",
"type": "VerifiedPerson",
"claims": {
"age_over_18": true,
"given_name": "John"
}
}
]
}
}

Delete Authorization Request

Clean up an authorization session.

DELETE /oid4vp/backend/auth/requests/{correlationId}

Session Status Values

StatusDescription
CREATEDSession created, waiting for wallet scan
REQUEST_RETRIEVEDWallet has retrieved the authorization request
AUTHORIZATION_RESPONSE_RECEIVEDWallet submitted response, verification in progress
AUTHORIZATION_RESPONSE_VERIFIEDCredentials verified successfully
ERRORVerification failed
EXPIREDSession timed out

Usage Examples

Web Application Flow

// 1. Create session and get QR code
val createCommand: CreateAuthRequestEndpointCommand = session.component.createAuthRequestCommand

val result = createCommand.execute(
CreateAuthorizationRequestInput(
queryId = "age-verification",
qrCodeOptions = QrCodeOptions(size = 300)
),
sessionContext
)

val output = result.getOrThrow()
println("Display QR: ${output.qrCodeDataUri}")
println("Poll status at: ${output.statusUri}")

// 2. Poll for completion
val statusCommand: GetAuthRequestStatusEndpointCommand = session.component.getAuthRequestStatusCommand

while (true) {
delay(2000)
val status = statusCommand.execute(
correlationId = output.correlationId,
sessionContext
).getOrThrow()

when (status.status) {
AuthorizationSessionStatus.AUTHORIZATION_RESPONSE_VERIFIED -> {
println("Verified: ${status.verifiedData}")
break
}
AuthorizationSessionStatus.ERROR -> {
println("Error: ${status.error}")
break
}
AuthorizationSessionStatus.EXPIRED -> {
println("Session expired")
break
}
else -> continue
}
}

Webhook Integration

Configure a webhook to receive verification results:

val result = createCommand.execute(
CreateAuthorizationRequestInput(
queryId = "kyc-verification",
callback = CallbackConfig(
url = "https://my-app.example.com/webhook/oid4vp",
statuses = listOf(
AuthorizationSessionStatus.AUTHORIZATION_RESPONSE_VERIFIED,
AuthorizationSessionStatus.ERROR
),
includeVerifiedData = true
)
),
sessionContext
)

Your webhook endpoint will receive:

{
"correlation_id": "sess-abc123",
"status": "AUTHORIZATION_RESPONSE_VERIFIED",
"verified_data": {
"credentials": [...]
}
}

Dependencies

dependencies {
// Universal OID4VP
implementation("com.sphereon.idk:lib-openid-oid4vp-universal-impl:0.13.0")

// Required dependencies
implementation("com.sphereon.idk:lib-openid-oid4vp-verifier-impl:0.13.0")
implementation("com.sphereon.idk:lib-openid-oid4vp-dcql:0.13.0")
}

Configuration

Configure default DCQL queries for reuse:

@Inject
@SingleIn(AppScope::class)
class Oid4vpQueryRegistry {
val queries = mapOf(
"age-verification" to DcqlQuery(
credentials = listOf(
DcqlCredential(
id = "age_over_18",
format = "dc+sd-jwt",
claims = listOf(
DcqlClaim(path = listOf("age_over_18"))
)
)
)
),
"kyc-verification" to DcqlQuery(
credentials = listOf(
DcqlCredential(
id = "identity",
format = "dc+sd-jwt",
claims = listOf(
DcqlClaim(path = listOf("given_name")),
DcqlClaim(path = listOf("family_name")),
DcqlClaim(path = listOf("birth_date"))
)
)
)
)
)
}

Next Steps