Microsoft Entra / Verifiable Credentials
Verifiable Doesn’t Mean Verified: Getting Issuer, Holder, and Verifier Right
A practical walk through verifiable credentials where “well-formed JSON” and “cryptographically signed” still aren’t the same as “true.”
I’m an engineering manager with experience integrating identity-verification flows conceptually aligned with Microsoft Entra Verified ID. One habit that keeps teams honest is repeating the phrase: verifiable doesn’t mean verified. The W3C Verifiable Credentials data model makes claims portable and cryptographically provable, but it doesn’t magically make those claims true, up to date, or appropriate for your use case.
The failure mode
A team builds a credential verifier that checks JSON shape and a signature. The code reports “valid.” A downstream feature treats the claim as true. Weeks later, someone discovers the issuer did, in fact, sign the credential, but the subject was not the person currently presenting it, or the credential expired months ago. The failure wasn’t cryptography; it was process: treating “verifiable” as “verified.”
What I learned
Roles and responsibilities: issuer, holder, verifier
Issuer: Asserts something about a subject (the holder) and signs the assertion. Their trustworthiness is a human, contractual, and policy question, not a purely technical one.
Holder: Receives and stores the credential; presents it to a verifier. Binding the holder to the subject of the credential (is this person who the claim describes?) is a separate step.
Verifier: Evaluates signature validity, checks schema, ensures the issuer is trusted for that claim type, confirms the subject binding, interprets policy (expiry, audience, revocation), and decides whether to accept the claim.
Well-formed is necessary but not sufficient
A credential can be:
Well-formed: It conforms to expected JSON structure.
Signed: The cryptographic proof validates against the issuer’s public key.
Trusted: The issuer is recognized and allowed to assert this particular claim type.
Bound: The subject is provably the presenter.
Current: It hasn’t expired and isn’t revoked. Only when all are satisfied does “valid” approach “usable.”
Separate signature validation from trust decision
Cryptography answers “did the claimed issuer sign this payload?” Trust answers “do we trust this issuer for this claim type right now?” You need both. If the issuer isn’t in your trust registry for the claim being evaluated, the result shouldn’t be “invalid claim,” it should be “untrusted issuer for this policy.”
Subject binding is a first-class step
A classic failure mode: someone presents a signed credential about a subject, but the presenter isn’t the subject. In practical flows, the verifier should confirm that the presenter’s wallet or identity key pair matches the credential subject, or use an out-of-band binding method aligned with your policy. If binding is missing, treat the result as “unverifiable” rather than “false.”
Expiry and audience are policy gates, not parsing details
Expiry and audience are part of the claim semantics. A credential can be signed and well-formed yet expired (or intended for a different audience). These conditions should result in “policy failure” states distinct from “cryptographic failure.”
Revocation closes the loop
Issuance is easy; ensuring the claim is still valid is the hard part. Revocation checks must avoid “phone home” privacy leaks (more in the revocation article below). Even a toy system benefits from an offline revocation list or privacy-preserving status check.
Distinguish “invalid” from “unverifiable”
Illustrative verification flow
from dataclasses import dataclass
from typing import Literal
Status = Literal["accepted", "unverifiable", "invalid"]
@dataclass(frozen=True)
class ValidationResult:
signature_valid: bool
issuer_trusted: bool
subject_bound: bool
policy_ok: bool
status: Status
# Keep proof validation, issuer trust, subject binding, and policy checks separate.
# The exact proof checks depend on the credential format and verifier library supported by the deployment.
Trade-offs and failure modes
Trade-offs
Strict policy gates vs user friction: Strong audience/expiry checks reduce misuse but can frustrate users who present slightly out-of-date credentials. The fix is short-lived credentials and good UX.
Offline trust lists vs dynamic fetch: Offline lists avoid privacy leaks but risk staleness. Consider periodic sync with no per-verification lookups.
Subject binding rigor vs simplicity: Strong binding reduces fraud but adds ceremony. Pick the approach that fits your risk.
Failure modes
“Signed, therefore true”: Conflating cryptographic validity with trust. Fix by separating proof validation from issuer trust policy.
“Presenter ≠ subject”: Accepting credentials without subject binding. Fix by explicit binding checks.
“Policy drift”: Undocumented audience/expiry rules. Fix with centralized policy and tests.
Try it yourself
Implement a verifier that separately evaluates cryptographic proof, issuer trust, subject binding, and policy gates.
Return “unverifiable” for missing binding and “invalid” for failed policy (e.g., expiry).
Measure performance across well-formed accepts vs policy failures to understand user impact.