Maturity: Limited preview (
0.7.0). Every verification in a
relying-party integration happens here.Two entry points
The package root is framework-free. Everything Fastify lives behind/fastify, which is what keeps the optional peer dependency real rather than
aspirational.
Signing
Both signers use the same registered presenter key. Thetyp is what
distinguishes the artifacts and routes them on the device.
OKP/Ed25519 and EC/P-256 keys are both accepted. The proof signer is
structural, so anything with sign({ htm, htu, grant, body? }) works. You can
sign from your own key management service.
Verifying a login token
typ and algorithm pin, and rejection of jku,
x5u and unknown critical headers; the signature against the embedded
did:jwk; audience and issuer; the holder-of-key binding; the single-use
nonce; the subject mode against expectedMode; and, for chain-aware tokens,
self-certification plus the Baby Jubjub Schnorr key-binding signature. The last
two hard-fail and never downgrade the tier.
expectedMode is required. Pass the mode you asked for: "disclosed" if the
request set scope: "arrayid", "pairwise" if it did not. A token of the other
mode throws subject_mode_mismatch before any proof work. This matters most if
you key accounts on the ArrayID. A pairwise token’s sub is not bound to the
signing key unless a membership proof verifies, so without the check anyone
could sign a pairwise token naming someone else’s ArrayID and be accepted as
them. A tier floor doesn’t replace it, because a pairwise token with a valid
proof reaches membership_proven. selfIssuedPreHandler takes the same
options, so it needs expectedMode too.
Given an idFacetProof config the SDK additionally checks six obligations and
gates unique on all six. You implement none of them. Two are worth
knowing about because they are what makes the proof non-transferable: the scope
is recomputed from your own client_id, and the payload hash binds the
specific nonce you burned. A proof therefore fails at any other RP and cannot
be replayed at yours.
IdFacetProofError.code is one of groth16, root, scope, slot,
payload, mode, tag, replay, malformed, verifier_unavailable.
verdict.mode and verdict.idFacet are server-internal. The facet is the
user’s stable pseudonym at your RP, so keep it out of response bodies and
per-user log lines.The roots provider
Nonce and state stores
The single-use nonce your verifier burns. You run this, and Array never hosts it. It is a pure library with no calls to Array.consume returns true exactly once per issued value. A backend outage makes
consume return false (fail-closed). issue throws rather than hand out a
value that was never durably stored. MemoryNonceBackend covers tests, and the
two-method NonceBackend interface takes a Redis or Postgres implementation.
Also exported
The verdict ladder (VERDICT_TIERS, tierAtLeast); verify-side constructors
with byte-parity against the wire contract (decodeDidJwk, selfCertifies,
schnorrVerifyFr and others); and typed errors, namely SelfIssuedVerifyError,
DpopVerifyError, PresenterProofError and ArrayIdentityError. The wire
contract’s types and constants are re-exported, so you rarely need to import
@vibechecklabs/array-identity-contract directly.