3.5. Verifiable Credentials
This tutorial covers the Verifiable Credentials (VCs) system in the dAIEDGE Middleware, enabling decentralized identity attestations following W3C standards.
Overview
Verifiable Credentials allow you to:
- Request attestations about your identity (claims)
- Issue credentials as a trusted issuer
- Verify credential authenticity cryptographically
- Present credentials for authentication
- Manage credential lifecycle (activate/revoke)
What are Verifiable Credentials?
VCs are tamper-proof digital attestations issued by trusted parties:
- Issuer: Entity that creates and signs the credential
- Holder: Entity that receives and presents the credential
- Verifier: Entity that checks the credential’s validity
Prerequisites
- Completed Authentication, Wallet Management, and Identity Management tutorials
- Active session and wallet
- At least one identity (DID) created
User Roles
Issuer Role
The VC system uses application-level roles (not blockchain roles):
| Role | Purpose | Who Has It |
|---|---|---|
| Issuer | Issue VCs, view requests, activate/revoke VCs | Users granted issuer role by admin |
| Admin | Grant/revoke issuer role | Platform administrators |
| Regular User | Request VCs, view own VCs, verify/decode VCs | All authenticated users |
Role Hierarchy
Admin (platform level)
└── Can grant/revoke Issuer role
Issuer
├── View VC requests
├── Accept VC requests (issue credentials)
├── Activate VCs
└── Revoke VCs
Regular User
├── Request VCs
├── View own VCs
├── Verify credentials
└── Decode credentialsStep 1: Request a Verifiable Credential
Request a VC with specific claims about your identity.
Endpoint
POST /api/v1/vcs/requestsRequest
curl -X POST https://middleware-daiedge.bisite.usal.es/api/v1/vcs/requests \
-H "Content-Type: application/json" \
-H "x-session-id: {SESSION_ID}" \
-d '{
"walletId": "0000019a9c4f8d2b1e7a6f3c9b5e8d1a",
"password": "WalletPass123!",
"did": "did:ethr:development:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb7",
"claims": [
{
"type": "group",
"value": "Group1"
}
]
}'Request Body
| Field | Type | Required | Description |
|---|---|---|---|
walletId | string | Yes | Your wallet ID |
password | string | Yes | Wallet password |
did | string | Yes | Your DID (must be owned by your wallet) |
claims | array | Yes | Array of claim objects |
Supported Claims
| Claim Type | Data Type | Allowed Values | Description |
|---|---|---|---|
group | enum | Group1, Group2 | User membership group for access control |
Claim Object Structure
{
"type": "group",
"value": "Group1"
}Response (200 OK)
Note: This endpoint returns an empty body. To find your request after submitting it, use the list endpoint (Step 2) and filter by your did.
{}Error Responses
| Code | Error | Cause |
|---|---|---|
| 404 | WALLET_NOT_FOUND | Wallet doesn’t exist |
| 400 | WRONG_PASSWORD | Wallet password incorrect |
| 400 | INVALID_DID | DID format invalid |
| 400 | NOT_IDENTITY_OWNER | Wallet doesn’t own this DID |
| 400 | INVALID_CLAIMS_FORMAT | Claims array structure invalid |
| 400 | INVALID_CLAIM_TYPE | Unsupported claim type |
| 400 | INVALID_CLAIM_VALUE | Value not allowed for claim type |
| 403 | INVALID_WALLET | Wallet doesn’t belong to you |
Step 2: List Your Verifiable Credentials
View all VCs issued to your identity.
Endpoint
GET /api/v1/vcs?did={did}&page={page}Request
curl -X GET "https://middleware-daiedge.bisite.usal.es/api/v1/vcs?did=did:ethr:development:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb7&page=1" \
-H "x-session-id: {SESSION_ID}"Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
did | string | Yes | Your DID |
page | number | No | Page number (default: 1) |
Response (200 OK)
{
"vcs": [
{
"id": "0000019a9c6a7b8c9d1e2f3a4b5c6d7e",
"did": "did:ethr:development:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb7",
"credential": "eyJhbGciOiJFUzI1NksifQ...",
"active": true
}
],
"page": 1,
"totalPages": 1,
"total": 1
}Response Fields
| Field | Description |
|---|---|
id | Verifiable Credential ID |
did | DID the credential was issued to |
credential | JWT-encoded Verifiable Credential |
active | Whether the credential is currently active |
Error Responses
| Code | Error | Cause |
|---|---|---|
| 400 | INVALID_DID | DID format invalid |
| 404 | USER_DID_NOT_FOUND | DID not found in system |
| 403 | INVALID_USER_DID | DID doesn’t belong to you |
Step 3: Verify a Verifiable Credential
Cryptographically verify that a credential is valid.
Endpoint
POST /api/v1/vcs/verify-credentialRequest
curl -X POST https://middleware-daiedge.bisite.usal.es/api/v1/vcs/verify-credential \
-H "Content-Type: application/json" \
-H "x-session-id: {SESSION_ID}" \
-d '{
"vcJwt": "eyJhbGciOiJFUzI1NksifQ..."
}'Request Body
| Field | Type | Required | Description |
|---|---|---|---|
vcJwt | string | Yes | JWT-encoded Verifiable Credential |
Response (200 OK)
{
"verified": true,
"credential": {
"verified": true,
"payload": {
"iat": 1763540833,
"vc": {
"@context": [
"https://www.w3.org/2018/credentials/v1"
],
"type": [
"VerifiableCredential"
],
"credentialSubject": {
"did": "did:ethr:development:0x7018205409a98fb9580b53e268d58c96d58ba640",
"group": "Group1"
}
},
"sub": "did:ethr:development:0x7018205409a98fb9580b53e268d58c96d58ba640",
"jti": "0000019a9b39952bad9a53740f7ce5ed",
"iss": "did:ethr:development:0x9AEd54E5cBD5e7d1aE523c104B9C23019B567B05"
},
"didResolutionResult": {
"didDocumentMetadata": {},
"didResolutionMetadata": {
"contentType": "application/did+ld+json"
},
"didDocument": {
"id": "did:ethr:development:0x9AEd54E5cBD5e7d1aE523c104B9C23019B567B05",
"verificationMethod": [
{
"id": "did:ethr:development:0x9AEd54E5cBD5e7d1aE523c104B9C23019B567B05#controller",
"type": "EcdsaSecp256k1RecoveryMethod2020",
"controller": "did:ethr:development:0x9AEd54E5cBD5e7d1aE523c104B9C23019B567B05",
"blockchainAccountId": "eip155:1337:0x9AEd54E5cBD5e7d1aE523c104B9C23019B567B05"
}
],
"authentication": [
"did:ethr:development:0x9AEd54E5cBD5e7d1aE523c104B9C23019B567B05#controller"
],
"assertionMethod": [
"did:ethr:development:0x9AEd54E5cBD5e7d1aE523c104B9C23019B567B05#controller"
],
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/secp256k1recovery-2020/v2",
"https://w3id.org/security/v3-unstable"
]
}
},
"issuer": "did:ethr:development:0x9AEd54E5cBD5e7d1aE523c104B9C23019B567B05",
"signer": {
"id": "did:ethr:development:0x9AEd54E5cBD5e7d1aE523c104B9C23019B567B05#controller",
"type": "EcdsaSecp256k1RecoveryMethod2020",
"controller": "did:ethr:development:0x9AEd54E5cBD5e7d1aE523c104B9C23019B567B05",
"blockchainAccountId": "eip155:1337:0x9AEd54E5cBD5e7d1aE523c104B9C23019B567B05"
},
"jwt": "eyJhbGciOiJFUzI1NkstUiIsInR5cCI6IkpXVCJ9...",
"policies": {},
"verifiableCredential": {
"credentialSubject": {
"did": "did:ethr:development:0x7018205409a98fb9580b53e268d58c96d58ba640",
"group": "Group1",
"id": "did:ethr:development:0x7018205409a98fb9580b53e268d58c96d58ba640"
},
"issuer": {
"id": "did:ethr:development:0x9AEd54E5cBD5e7d1aE523c104B9C23019B567B05"
},
"id": "0000019a9b39952bad9a53740f7ce5ed",
"type": [
"VerifiableCredential"
],
"@context": [
"https://www.w3.org/2018/credentials/v1"
],
"issuanceDate": "2025-11-19T08:27:13.000Z",
"proof": {
"type": "JwtProof2020",
"jwt": "eyJhbGciOiJFUzI1NkstUiIsInR5cCI6IkpXVCJ9..."
}
}
}
}Error Responses
| Code | Error | Cause |
|---|---|---|
| 400 | INVALID_VCJWT | VC JWT is empty or invalid |
Step 4: Decode a Verifiable Credential
Decode the JWT without cryptographic verification.
Endpoint
POST /api/v1/vcs/decode-credentialRequest
curl -X POST https://middleware-daiedge.bisite.usal.es/api/v1/vcs/decode-credential \
-H "Content-Type: application/json" \
-H "x-session-id: {SESSION_ID}" \
-d '{
"vcJwt": "eyJhbGciOiJFUzI1NksifQ..."
}'Response (200 OK)
{
"iat": 1763540833,
"vc": {
"@context": [
"https://www.w3.org/2018/credentials/v1"
],
"type": [
"VerifiableCredential"
],
"credentialSubject": {
"did": "did:ethr:development:0x7018205409a98fb9580b53e268d58c96d58ba640",
"group": "Group1"
}
},
"sub": "did:ethr:development:0x7018205409a98fb9580b53e268d58c96d58ba640",
"jti": "0000019a9b39952bad9a53740f7ce5ed",
"iss": "did:ethr:development:0x9AEd54E5cBD5e7d1aE523c104B9C23019B567B05"
}Error Responses
| Code | Error | Cause |
|---|---|---|
| 400 | INVALID_VCJWT | VC JWT is empty or invalid |
Issuer Operations (Issuer Role Required)
The following operations require the Issuer role.
Step 5: List VC Requests (Issuer Only)
View all pending credential requests.
Endpoint
GET /api/v1/vcs/requests?page={page}Request
curl -X GET "https://middleware-daiedge.bisite.usal.es/api/v1/vcs/requests?page=1" \
-H "x-session-id: {SESSION_ID}"Response (200 OK)
{
"requests": [
{
"id": "0000019a9c7f3e2d1c8b4a7e6f9c3d2a",
"did": "did:ethr:development:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb7",
"info": "{\"did\":\"did:ethr:development:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb7\",\"group\":\"Group1\"}",
"created": 1697000000000,
"accepted": false
}
],
"page": 1,
"totalPages": 1,
"total": 1
}Response Fields
| Field | Description |
|---|---|
id | Request ID |
did | Requester’s DID |
info | JSON string with requested claims |
created | Request timestamp |
accepted | Whether the request has been accepted |
Error Responses
| Code | Error | Cause |
|---|---|---|
| 403 | NOT_ISSUER | You don’t have issuer role |
Step 6: Accept VC Request (Issuer Only)
Issue a credential by accepting a request.
Endpoint
POST /api/v1/vcs/requests/{id}/acceptRequest
curl -X POST https://middleware-daiedge.bisite.usal.es/api/v1/vcs/requests/{REQUEST_ID}/accept \
-H "Content-Type: application/json" \
-H "x-session-id: {SESSION_ID}" \
-d '{
"walletId": "0000019a9c8e4d3f2a1b7c6e5f9d8a3b",
"password": "IssuerPass123!"
}'Request Body
| Field | Type | Required | Description |
|---|---|---|---|
walletId | string | Yes | Issuer’s wallet ID |
password | string | Yes | Wallet password |
Response (200 OK)
{}The VC is created and associated with the requester’s DID.
Error Responses
| Code | Error | Cause |
|---|---|---|
| 404 | VC_REQUEST_NOT_FOUND | Request doesn’t exist |
| 400 | REQUEST_ALREADY_ACCEPTED | Request was already accepted |
| 400 | WRONG_PASSWORD | Wallet password incorrect |
| 403 | NOT_ISSUER | You don’t have issuer role |
| 403 | INVALID_WALLET | Wallet doesn’t belong to you |
Step 7: Activate/Revoke VCs (Issuer Only)
Activate a VC
POST /api/v1/vcs/active/{VC_ID}curl -X POST https://middleware-daiedge.bisite.usal.es/api/v1/vcs/active/{VC_ID} \
-H "x-session-id: {SESSION_ID}"Revoke a VC
POST /api/v1/vcs/revoke/{VC_ID}curl -X POST https://middleware-daiedge.bisite.usal.es/api/v1/vcs/revoke/{VC_ID} \
-H "x-session-id: {SESSION_ID}"Response (200 OK)
{}Error Responses
| Code | Error | Cause |
|---|---|---|
| 404 | VC_NOT_FOUND | VC doesn’t exist |
| 403 | NOT_ISSUER | You don’t have issuer role |
Admin Operations (Admin Role Required)
The following operations require the Admin role.
Step 8: Grant/Revoke Issuer Role (Admin Only)
Grant Issuer Role
POST /api/v1/vcs/grant-issuer-rolecurl -X POST https://middleware-daiedge.bisite.usal.es/api/v1/vcs/grant-issuer-role \
-H "Content-Type: application/json" \
-H "x-session-id: {SESSION_ID}" \
-d '{
"uid": "0000019a9ca2e3f4a5b6c7d8e9f1a2b3"
}'Revoke Issuer Role
POST /api/v1/vcs/revoke-issuer-rolecurl -X POST https://middleware-daiedge.bisite.usal.es/api/v1/vcs/revoke-issuer-role \
-H "Content-Type: application/json" \
-H "x-session-id: {SESSION_ID}" \
-d '{
"uid": "0000019a9ca1d2e3f4a5b6c7d8e9f1a2"
}'Request Body
| Field | Type | Required | Description |
|---|---|---|---|
uid | string | Yes | Target user’s UID |
Response (200 OK)
{}Error Responses
| Code | Error | Cause |
|---|---|---|
| 404 | USER_NOT_FOUND | User doesn’t exist |
| 403 | NOT_ADMIN | You don’t have admin role |
VC Lifecycle
1. User Requests VC
→ Submits claims for attestation
2. Issuer Reviews Request
→ Views pending requests
→ Validates claims
3. Issuer Accepts Request
→ Signs and issues credential
→ VC becomes active
4. User Receives VC
→ JWT stored in system
→ Can present to verifiers
5. Verifier Checks VC
→ Cryptographic verification
→ Checks issuer trust
→ Validates claims
6. Lifecycle Management
→ Issuer can revoke VC
→ Issuer can re-activate VCNext Steps
- Role Management - Manage blockchain-level roles
- Hardware Registration - Register hardware with attested identity
Related Topics
- Identity Management - Create DIDs for VC requests
- Wallet Management - Manage wallets for signing
- Authentication - Session management