Verify Credentials

This guide shows you how to verify verifiable credentials with TrustWeave. You’ll learn how to check proof validity, issuer resolution, expiration, and revocation status.

Quick Example

Here’s a complete example that verifies a credential:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import com.trustweave.trust.TrustWeave
import com.trustweave.trust.types.VerificationResult
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    try {
        // Create TrustWeave instance
        val trustWeave = TrustWeave.build {
            keys {
                provider("inMemory")
                algorithm("Ed25519")
            }
            did {
                method("key") {
                    algorithm("Ed25519")
                }
            }
        }

        // Assume you have a credential from issuance
        val credential = // ... credential from issuer ...

        // Verify credential with exhaustive error handling
        val result = trustWeave.verify {
            credential(credential)
            checkRevocation()
            checkExpiration()
        }

        when (result) {
            is VerificationResult.Valid -> {
                println("✅ Credential is valid: ${result.credential.id}")
                if (result.warnings.isNotEmpty()) {
                    println("   Warnings: ${result.warnings.joinToString()}")
                }
            }
            is VerificationResult.Invalid.Expired -> {
                println("❌ Credential expired at ${result.expiredAt}")
            }
            is VerificationResult.Invalid.Revoked -> {
                println("❌ Credential revoked")
            }
            is VerificationResult.Invalid.InvalidProof -> {
                println("❌ Invalid proof: ${result.reason}")
            }
            is VerificationResult.Invalid.UntrustedIssuer -> {
                println("❌ Untrusted issuer: ${result.issuer}")
            }
            is VerificationResult.Invalid.SchemaValidationFailed -> {
                println("❌ Schema validation failed: ${result.errors.joinToString()}")
            }
            else -> {
                println("❌ Verification failed")
            }
        }
    } catch (error: Exception) {
        println("❌ Verification error: ${error.message}")
    }
}

Expected Output:

1
✅ Credential is valid: https://example.edu/credentials/123

Step-by-Step Guide

Step 1: Get the Credential

Obtain the credential to verify (from holder, storage, or network):

1
val credential: VerifiableCredential = // ... get credential ...

Step 2: Verify the Credential

Use the verify DSL to verify the credential:

1
2
3
val verification = trustWeave.verify {
    credential(credential)
}

Step 3: Check Verification Result

Examine the verification result using sealed type for exhaustive handling:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
when (verification) {
    is VerificationResult.Valid -> {
        println("✅ Credential is valid")
        println("  - Proof valid: ${verification.proofValid}")
        println("  - Issuer valid: ${verification.issuerValid}")
    }
    is VerificationResult.Invalid.Expired -> {
        println("❌ Credential expired at ${verification.expiredAt}")
    }
    is VerificationResult.Invalid.Revoked -> {
        println("❌ Credential revoked")
    }
    is VerificationResult.Invalid.InvalidProof -> {
        println("❌ Invalid proof: ${verification.reason}")
    }
    is VerificationResult.Invalid.UntrustedIssuer -> {
        println("❌ Untrusted issuer: ${verification.issuer}")
    }
    is VerificationResult.Invalid.SchemaValidationFailed -> {
        println("❌ Schema validation failed: ${verification.errors.joinToString()}")
    }
}

Step 4: Handle Warnings

Check for warnings even if verification passed:

1
2
3
4
5
6
7
8
9
10
11
12
when (verification) {
    is VerificationResult.Valid -> {
        if (verification.warnings.isNotEmpty()) {
            verification.warnings.forEach { warning ->
                println("Warning: $warning")
            }
        }
    }
    else -> {
        // Handle invalid cases
    }
}

Verification Checks Explained

TrustWeave performs multiple checks during verification:

1. Proof Validation

Checks that the cryptographic proof (signature) is valid:

1
2
3
4
5
6
7
8
9
10
11
12
13
when (verification) {
    is VerificationResult.Valid -> {
        if (verification.proofValid) {
            println("Proof signature is valid")
        }
    }
    is VerificationResult.Invalid.InvalidProof -> {
        println("Proof signature is invalid: ${verification.reason}")
    }
    else -> {
        // Other failure cases
    }
}

What it checks:

  • Signature matches the credential content
  • Signature was created with a key from the issuer’s DID document
  • Proof type is supported

2. Issuer Validation

Checks that the issuer DID can be resolved:

1
2
3
4
5
6
7
8
9
10
11
12
13
when (verification) {
    is VerificationResult.Valid -> {
        if (verification.issuerValid) {
            println("Issuer DID resolved successfully")
        }
    }
    is VerificationResult.Invalid.UntrustedIssuer -> {
        println("Issuer DID resolution failed or issuer not trusted: ${verification.issuer}")
    }
    else -> {
        // Other failure cases
    }
}

What it checks:

  • Issuer DID format is valid
  • DID method is registered
  • DID document can be retrieved
  • Issuer DID document contains the verification method used in proof

3. Expiration Check

Checks if the credential has expired:

1
2
3
4
5
6
7
8
9
10
11
when (verification) {
    is VerificationResult.Valid -> {
        println("Credential has not expired")
    }
    is VerificationResult.Invalid.Expired -> {
        println("Credential has expired at ${verification.expiredAt}")
    }
    else -> {
        // Other failure cases
    }
}

What it checks:

  • expirationDate field exists
  • Current time is before expiration date

4. Revocation Check

Checks if the credential has been revoked:

1
2
3
4
5
6
7
8
9
10
11
when (verification) {
    is VerificationResult.Valid -> {
        println("Credential is not revoked")
    }
    is VerificationResult.Invalid.Revoked -> {
        println("Credential has been revoked")
    }
    else -> {
        // Other failure cases
    }
}

What it checks:

  • credentialStatus field exists
  • Status list is accessible
  • Credential index in status list is not set (not revoked)

Verification Policies

Configure verification behavior using verification options:

Basic Verification

Default verification checks all aspects:

1
2
3
4
val verification = trustWeave.verify {
    credential(credential)
    // All checks enabled by default
}

Custom Verification Configuration

Control which checks are performed:

1
2
3
4
5
6
val verification = trustWeave.verify {
    credential(credential)
    checkExpiration()          // Check expiration (default: enabled)
    checkRevocation()          // Check revocation (default: enabled)
    checkTrust(false)          // Check trust registry (default: false)
}

Skip Expiration Check

For credentials without expiration or when expiration doesn’t matter:

1
2
3
4
5
// Simply don't call checkExpiration() - it's optional
val verification = trustWeave.verify {
    credential(credential)
    // Expiration check skipped
}

Skip Revocation Check

For credentials without revocation status or when revocation doesn’t matter:

1
2
3
4
5
// Simply don't call checkRevocation() - it's optional
val verification = trustWeave.verify {
    credential(credential)
    // Revocation check skipped
}

Trust Registry Verification

Verify that the issuer is in the trust registry:

1
2
3
4
val verification = trustWeave.verify {
    credential(credential)
    checkTrust(true)  // Verify issuer is trusted
}

Note: Requires trust registry to be configured in TrustWeave.

Common Patterns

Pattern 1: Verify with Detailed Results

Get detailed information about each check:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
val verification = trustWeave.verify {
    credential(credential)
}

when (verification) {
    is VerificationResult.Valid -> {
        println("✅ Overall valid: true")
        println("  - Proof valid: ${verification.proofValid}")
        println("  - Issuer valid: ${verification.issuerValid}")
        if (verification.warnings.isNotEmpty()) {
            println("  - Warnings:")
            verification.warnings.forEach { warning ->
                println("    - $warning")
            }
        }
    }
    is VerificationResult.Invalid.Expired -> {
        println("❌ Credential expired at ${verification.expiredAt}")
        verification.errors.forEach { error ->
            println("  - $error")
        }
    }
    is VerificationResult.Invalid.Revoked -> {
        println("❌ Credential revoked")
        verification.errors.forEach { error ->
            println("  - $error")
        }
    }
    is VerificationResult.Invalid.InvalidProof -> {
        println("❌ Invalid proof: ${verification.reason}")
    }
    is VerificationResult.Invalid.UntrustedIssuer -> {
        println("❌ Untrusted issuer: ${verification.issuer}")
    }
    is VerificationResult.Invalid.SchemaValidationFailed -> {
        println("❌ Schema validation failed:")
        verification.errors.forEach { error ->
            println("  - $error")
        }
    }
}

Pattern 2: Verify Multiple Credentials

Verify a batch of credentials:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
val credentials: List<VerifiableCredential> = // ... get credentials ...

val results = credentials.map { cred ->
    val verification = trustWeave.verify {
        credential(cred)
    }
    cred.id to verification
}

val valid = results.filter { (_, verification) -> 
    verification is VerificationResult.Valid 
}
val invalid = results.filter { (_, verification) -> 
    verification !is VerificationResult.Valid 
}

println("Valid: ${valid.size}/${results.size}")
println("Invalid: ${invalid.size}/${results.size}")

invalid.forEach { (credId, verification) ->
    when (verification) {
        is VerificationResult.Invalid -> {
            println("Credential $credId failed: ${verification.errors.joinToString()}")
        }
        else -> {
            println("Credential $credId failed: Unknown error")
        }
    }
}

Pattern 3: Verify with Error Handling

Handle verification errors gracefully:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Verification returns a sealed result type, so no exceptions needed
val verification = trustWeave.verify {
    credential(credential)
}

when (verification) {
    is VerificationResult.Valid -> {
        println("✅ Credential is valid")
    }
    is VerificationResult.Invalid -> {
        println("❌ Credential invalid: ${verification.errors.joinToString()}")
    }
}

Pattern 4: Conditional Verification

Verify with different policies based on context:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fun verifyCredential(
    credential: VerifiableCredential,
    strict: Boolean = false
): VerificationResult {
    return trustWeave.verify {
        credential(credential)
        if (strict) {
            checkExpiration()      // Only check expiration if strict
            checkRevocation()      // Only check revocation if strict
            checkTrust(true)       // Only check trust if strict
        }
    }
}

// For production: strict verification
val productionResult = verifyCredential(credential, strict = true)

// For testing: lenient verification
val testResult = verifyCredential(credential, strict = false)

Error Handling

Verification can fail in several ways:

Verification Result Errors

The verification result is a sealed type with detailed error information:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
val verification = trustWeave.verify {
    credential(credential)
}

when (verification) {
    is VerificationResult.Valid -> {
        println("✅ Credential is valid")
    }
    is VerificationResult.Invalid.Expired -> {
        println("❌ Credential expired at ${verification.expiredAt}")
    }
    is VerificationResult.Invalid.Revoked -> {
        println("❌ Credential revoked")
    }
    is VerificationResult.Invalid.InvalidProof -> {
        println("❌ Proof validation failed: ${verification.reason}")
    }
    is VerificationResult.Invalid.UntrustedIssuer -> {
        println("❌ Issuer validation failed: ${verification.issuer}")
    }
    is VerificationResult.Invalid.SchemaValidationFailed -> {
        println("❌ Schema validation failed")
        verification.errors.forEach { error ->
            println("  - $error")
        }
    }
}

Exception Handling

Verification returns a sealed result type, so exceptions are rare. However, you should handle the result exhaustively:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
val verification = trustWeave.verify {
    credential(credential)
}

// Exhaustive handling ensures all cases are covered
when (verification) {
    is VerificationResult.Valid -> {
        // Use valid credential
        println("Credential is valid: ${verification.credential.id}")
    }
    is VerificationResult.Invalid -> {
        // Handle all invalid cases
        println("Credential invalid: ${verification.errors.joinToString()}")
    }
}

Verification Result Structure

The VerificationResult is a sealed class for exhaustive error handling:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
sealed class VerificationResult {
    data class Valid(
        val credential: VerifiableCredential,
        val proofValid: Boolean,
        val issuerValid: Boolean,
        val warnings: List<String> = emptyList()
    ) : VerificationResult()
    
    sealed class Invalid : VerificationResult() {
        data class Expired(
            val expiredAt: Instant,
            val errors: List<String>
        ) : Invalid()
        
        data class Revoked(
            val revokedAt: Instant?,
            val errors: List<String>
        ) : Invalid()
        
        data class InvalidProof(
            val reason: String,
            val errors: List<String>
        ) : Invalid()
        
        data class UntrustedIssuer(
            val issuer: String,
            val errors: List<String>
        ) : Invalid()
        
        data class SchemaValidationFailed(
            val errors: List<String>
        ) : Invalid()
    }
}

API Reference

For complete API documentation, see:

Next Steps

Ready to issue credentials?

Want to configure verification?

Want to learn more?