Skip to main content
claim() verifies the claim on the phone, but your backend must not trust the phone. Send claim.encoded (the canonical bytes, base64) to your backend and verify it there.

Configure once

  • The operator public key, PEM. Pin it in configuration; never fetch it.
  • The trust bundle URL. Fetch it at least every 60 seconds and keep the highest version you accepted. Refuse a lower one. If you have no fresh copy for 30 minutes, stop accepting claims.

The checks

  1. Signature: Ed25519 over the canonical encoding, by a claim key the bundle lists for the claim’s epoch, on a verifier that is not revoked, in a valid epoch.
  2. Time: issued_at at most 60 seconds ahead, expires_at not passed.
  3. Holder binding: refused unless your backend checks one.
  4. Policy: profile_id and policy_id are the ones you asked for, and the outputs say what you need.
  5. Nullifier: if an account may prove a fact once, refuse a nullifier you have seen.
The libraries do 1 to 3. Checks 4 and 5 are yours. Verify a claim when it arrives: a claim stops verifying once its epoch closes, which can be before expires_at.

Holder-bound claims

A policy may bind its claim to a key the user holds. verify refuses such a claim. When your backend has checked that the presenter controls that key (a challenge they signed), call verifyWithHolder (TypeScript), VerifyClaimWithHolder (Go) or verify_with_holder (Rust).

On-chain

On Stellar, the claims contract does checks 1 and 2 against registered verifier keys, and the orchestrator does 4 and 5 against the order.