Verifiable Credentials (VCs)

Verifiable Credentials are a W3C standard for expressing credentials in a way that is cryptographically secure, privacy-respecting, and machine-verifiable. They enable trusted digital claims about subjects.

Verifiable Credential Data Model

LearnCard implements the W3C Verifiable Credentials Data Model, with support for both VC 1.0 and VC 2.0 formats. The core data types are defined using Zod validators.

Credential Structure

Component
Description
Required

@context

JSON-LD contexts defining the vocabulary

Yes

id

Unique identifier for the credential

No

type

Array of credential types

Yes

issuer

Entity that issued the credential

Yes

credentialSubject

Entity the credential is about

Yes

proof

Cryptographic proof of authenticity

Yes

Version differences:

  • VC 1.0 uses issuanceDate and expirationDate

  • VC 2.0 uses validFrom and validUntil

https://github.com/learningeconomy/LearnCard/blob/942bb5f7/packages/learn-card-types/src/vc.ts#L129-L177
export const UnsignedVCValidator = z
    .object({
        '@context': ContextValidator,
        id: z.string().optional(),
        type: z.string().array().nonempty(),
        issuer: ProfileValidator,
        credentialSubject: CredentialSubjectValidator.or(CredentialSubjectValidator.array()),
        refreshService: RefreshServiceValidator.or(RefreshServiceValidator.array()).optional(),
        credentialSchema: CredentialSchemaValidator.or(
            CredentialSchemaValidator.array()
        ).optional(),

        // V1
        issuanceDate: z.string().optional(),
        expirationDate: z.string().optional(),
        credentialStatus: CredentialStatusValidator.or(
            CredentialStatusValidator.array()
        ).optional(),

        // V2
        name: z.string().optional(),
        description: z.string().optional(),
        validFrom: z.string().optional(),
        validUntil: z.string().optional(),
        status: CredentialStatusValidator.or(CredentialStatusValidator.array()).optional(),
        termsOfUse: TermsOfUseValidator.or(TermsOfUseValidator.array()).optional(),
        evidence: VC2EvidenceValidator.or(VC2EvidenceValidator.array()).optional(),
    })
    .catchall(z.any());
export type UnsignedVC = z.infer<typeof UnsignedVCValidator>;

export const ProofValidator = z
    .object({
        type: z.string(),
        created: z.string(),
        challenge: z.string().optional(),
        domain: z.string().optional(),
        nonce: z.string().optional(),
        proofPurpose: z.string(),
        verificationMethod: z.string(),
        jws: z.string().optional(),
    })
    .catchall(z.any());
export type Proof = z.infer<typeof ProofValidator>;

export const VCValidator = UnsignedVCValidator.extend({
    proof: ProofValidator.or(ProofValidator.array()),
});
export type VC = z.infer<typeof VCValidator>;

Verifiable Presentations (VPs)

Verifiable Presentations allow holders to bundle and selectively disclose credentials:

Last updated

Was this helpful?