Verifying Credentials

Validate W3C Verifiable Credentials with the VCVerifier class. Check signatures, expiration, issuer identity, and detect tampering.

Two verification modes

The SDK supports two ways to verify a credential:

  • verifyWithKey — you supply the public key directly. No network calls.
  • verify — resolves the issuer's DID to fetch the public key automatically.

verifyWithKey

Use when you already know the issuer's public key:

Verify with known key
 import { VCVerifier } from '@attestto-com/vc-sdk'

const verifier = new VCVerifier()

const result = await verifier.verifyWithKey(vc, publicKey, 'Ed25519', {
  expectedType: 'ProfessionalLicense',
  expectedIssuer: 'did:web:issuer.example.com',
  checkExpiration: true,
})

console.log(result.valid)   // true or false
console.log(result.checks)  // detailed check results
console.log(result.errors)  // array of failure reasons 

verify (with DID resolver)

Use when you want the SDK to resolve the issuer's DID document and extract the public key:

Verify with DID resolution
 import { VCVerifier } from '@attestto-com/vc-sdk'

const verifier = new VCVerifier({
  resolver: myDidResolver, // implements DID resolution
})

const result = await verifier.verify(vc, {
  expectedType: 'DrivingLicense',
  checkExpiration: true,
  checkStatus: true,
})

if (result.valid) {
  console.log('Credential is valid')
} else {
  console.log('Errors:', result.errors)
} 

VerifyOptions

VerifyOptions interface
 interface VerifyOptions {
  expectedType?: string      // Assert credential contains this type
  expectedIssuer?: string    // Assert issuer DID matches
  expectedContext?: string   // Assert @context includes this URI
  checkExpiration?: boolean  // Check expirationDate against now (default: true)
  checkStatus?: boolean      // Check credentialStatus endpoint (default: false)
} 

VerificationResult

VerificationResult interface
 interface VerificationResult {
  valid: boolean              // Overall pass/fail
  checks: CheckResult[]      // Individual check outcomes
  errors: string[]            // Failure reasons (empty if valid)
  warnings: string[]          // Non-fatal issues
} 

Verification checks

The verifier runs these checks in order. Each produces a CheckResult entry:

  • structure.context — @context includes the W3C credentials URI.
  • structure.type — type array includes VerifiableCredential.
  • structure.issuer — issuer field is present and is a valid DID.
  • structure.subject — credentialSubject is present.
  • structure.issuanceDate — issuanceDate is a valid ISO 8601 timestamp.
  • proof.signature — cryptographic signature is valid against the public key.
  • proof.algorithm — proof type matches the expected algorithm.
  • temporal.expiration — credential has not expired (if checkExpiration is enabled).
  • temporal.notBefore — current time is after issuance date.
  • policy.type — credential type matches expectedType (if provided).
  • policy.issuer — issuer DID matches expectedIssuer (if provided).
  • policy.context — context matches expectedContext (if provided).
  • status.revocation — credential is not revoked (if checkStatus is enabled).

Tamper detection

If any field in the credential is modified after signing, the signature check fails:

Detecting tampering
 // Tamper with the credential
const tampered = { ...vc }
tampered.credentialSubject = {
  ...tampered.credentialSubject,
  profession: 'Brain Surgeon', // was 'Civil Engineer'
}

const result = await verifier.verifyWithKey(tampered, publicKey, 'Ed25519')

console.log(result.valid)    // false
console.log(result.errors)   // ['proof.signature: Signature verification failed'] 
⚠️
Always verify before trusting. A credential is just a JSON object until its proof is verified. Never extract claims from an unverified credential.

Next steps