Prerequisites
Before you begin, ensure you have:
- Docker installed on your machine
- Basic understanding of Verifiable Credentials
- A wallet capable of storing Verifiable Credentials (for testing)
For a complete local setup with all required components (Trusted Issuers Registry, credential issuers, etc.), see the VC-Integration-Test repository.
Run with Docker
The fastest way to get started is using the official Docker image:
Pull and run the container
docker run -p 8080:8080 quay.io/fiware/vcverifier
This starts VCVerifier on port 8080 with default configuration.Verify the service is running
Check the health endpoint:curl http://localhost:8080/health
You should receive a successful health check response. Check the JWKS endpoint
Verify that the public keys are available:curl http://localhost:8080/.well-known/jwks
This returns the public keys used to verify JWTs issued by the verifier.
Configuration
VCVerifier requires a configuration file to define verification rules, trust anchors, and credential scopes.
Basic configuration file
Create a server.yaml file:
server:
port: 8080
templateDir: "views/"
staticDir: "views/static/"
logging:
level: "INFO"
jsonLogging: true
logRequests: true
verifier:
did: "did:key:your-verifier-did"
clientIdentification:
id: "did:key:your-client-id"
supportedModes: ["urlEncoded", "byReference", "byValue"]
sessionExpiry: 30
generateKey: true
keyAlgorithm: "ES256"
configRepo:
services:
- id: testService
defaultOidcScope: "default"
oidcScopes:
default:
credentials:
- type: CustomerCredential
trustedParticipantsLists:
VerifiableCredential:
- https://tir-pdc.ebsi.fiware.dev
trustedIssuersLists:
VerifiableCredential:
- https://tir-pdc.ebsi.fiware.dev
Run with custom configuration
docker run -p 8080:8080 \
-v $(pwd)/server.yaml:/app/server.yaml \
-e CONFIG_FILE=/app/server.yaml \
quay.io/fiware/vcverifier
Testing the authentication flow
Let’s walk through a same-device authentication flow to see VCVerifier in action.
Step 1: Start the authentication flow
Initiate a same-device flow to get the authentication request:
curl -X GET 'http://localhost:8080/api/v1/samedevice?state=274e7465-cc9d-4cad-b75f-190db927e56a'
The state parameter is a unique identifier for this authentication session. In production, your frontend application would generate this.
Response: A 302 redirect with location header:
location: http://localhost:8080/?response_type=vp_token&response_mode=direct_post
&client_id=did:key:z6MkigCEnopwujz8Ten2dzq91nvMjqbKQYcifuZhqBsEkH7g
&redirect_uri=http://localhost:8080/api/v1/authentication_response
&state=OUBlw8wlCZZOcTwRN2wURA
&nonce=wqtpm60Jwx1sYWITRRZwBw
Step 2: Submit the Verifiable Presentation
The wallet responds with the Verifiable Presentation via the authentication response endpoint:
curl -X POST \
'http://localhost:8080/api/v1/authentication_response?state=OUBlw8wlCZZOcTwRN2wURA' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'presentation_submission=<base64-url-encoded-submission>&vp_token=<base64-url-encoded-vp>'
Make sure to use Base64-URL-Safe encoding (not standard Base64) for the vp_token and presentation_submission parameters.
Response: Another 302 redirect with the authorization code:
location: http://localhost:8080/?state=274e7465-cc9d-4cad-b75f-190db927e56a
&code=IwMTgvY3JlZGVudGlhbHMv
Step 3: Exchange code for JWT
Use the authorization code to retrieve the signed JWT:
curl -X POST \
'http://localhost:8080/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=authorization_code' \
-d 'code=IwMTgvY3JlZGVudGlhbHMv' \
-d 'redirect_uri=http://localhost:8080/'
Response: The verified credential as a signed JWT:
{
"token_type": "Bearer",
"expires_in": 3600,
"access_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6IldPSEZ1NEhaNTlTTTg1M0M3ZU4wT3ZsS0dyTWVlckRDcEhPVVJvVFF3SHciLCJ0eXAiOiJKV1QifQ..."
}
Decode the JWT
The JWT contains the verified credential in its payload. You can decode it at jwt.io or programmatically. Verify the signature
Use the JWKS endpoint to get the public key and verify the JWT signature:curl http://localhost:8080/.well-known/jwks
Use for authorization
Pass the JWT as a Bearer token to your downstream services:curl -H "Authorization: Bearer <access_token>" \
https://your-api.example.com/protected-resource
Frontend integration
For browser-based applications, VCVerifier provides a login page with QR code:
Display QR code for wallet scanning
curl 'http://localhost:8080/api/v1/loginQR?state=user-session-123&client_callback=https://my-app.com/auth/callback'
This returns an HTML page with:
- A QR code containing the authentication request
- The
openid4vp:// connection string
- JavaScript to handle the callback
Integration flow
Redirect user to login page
const state = generateRandomState();
const callbackUrl = encodeURIComponent('https://my-app.com/auth/callback');
window.location.href = `http://verifier.example.com/api/v1/loginQR?state=${state}&client_callback=${callbackUrl}`;
User scans QR code
The user scans the QR code with their wallet app, which contains the credential.
VCVerifier verifies and calls back
After verification, VCVerifier calls your callback URL:https://my-app.com/auth/callback?state=user-session-123&code=authorization-code
Exchange code for token
Your backend exchanges the code for a JWT:const response = await fetch('http://verifier.example.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: authorizationCode,
redirect_uri: 'https://my-app.com/auth/callback'
})
});
const { access_token } = await response.json();
Deploy to Kubernetes
For production deployments, use the Helm chart:
helm repo add i4trust https://i4trust.github.io/helm-charts
helm repo update
helm install vcverifier i4trust/vcverifier \
--set verifier.did=did:key:your-verifier-did \
--set configService.enabled=true
Next steps
Architecture
Learn how VCVerifier works under the hood
Configuration
Configure trust anchors and credential policies
API Reference
Explore all available endpoints
Production Setup
Best practices for production deployment