Skip to main content

Integration patterns

Wallet integration

  1. Obtain SD-JWT + disclosures from the issuer flow (out of scope for OpenAC).
  2. Ensure cnf.jwk is present for device binding — enforced in precompute. See Credential formats.
  3. Run credential bindingCredential.parse extracts issuer-authenticated input and maps claim names to claim indexes.
  4. Precompute once per credential after keys are available; persist PrecomputedCredential securely. Typical time: ~2 seconds.
  5. Reblind before Show — handled automatically inside present(...). Never reuse a precomputed result across sessions without going through present, which applies fresh randomness each time.
  6. Present per session with a fresh verifierNonce and showInputOptions describing the verifier's policy. Typical time: ~100 ms.
  7. Rotate storage if the device key rotates — a new cnf.jwk requires a new precompute.
Unsafe prepared-state reuse

The profile requires refreshing prepared state before each Show. Reusing the same precomputed result directly across sessions (bypassing present) is not conformant and breaks unlinkability.

Verifier integration

Verification pipeline

Apply checks in this order (Implementation profile §9.1):

parse response
→ check profile version and parameters
→ recover request transcript (nonce, audience, statement)
→ check credential binding identifier
→ check freshness (nonce matches, not expired)
→ check Prepare/Show linkage
→ verify proof
→ evaluate statement semantics
→ check output consistency
→ accept / reject

The order matters. Statement evaluation is only meaningful after transcript, binding, freshness, and linkage have passed.

SDK verifier call

// Load verifying keys only — proving keys stay off the verifier
const result = await openac.verifyProof(proof.serialize(), verifyingKeys);
// result: { valid, expressionResult, deviceKey, verifyMs, error }

Key fields of VerificationResult (types.ts):

FieldMeaning
validProof and all binding checks passed.
expressionResultThe boolean result of the predicate logic expression (what the wallet proved). Verify this matches your policy intent.
deviceKeyThe P-256 public key that signed the verifier nonce. Bind to this if you associate sessions with device keys.
verifyMsTime taken to verify.
errorPresent if valid === false.
expressionResult is not enough on its own

valid === true means the proof passed all OpenAC checks. You must also confirm that expressionResult matches your intended policy (e.g. true for a threshold predicate means the condition was met). A proof can be valid while proving a predicate you did not intend.

OpenAC acceptance vs final relying-party acceptance

valid === true and expressionResult === true together constitute OpenAC acceptance — a proof-layer result. Before granting access you must also complete:

External checkWhy
Issuer trustIs the issuer in your trusted list for this credential type?
Credential status / revocationIs the credential still valid? (No in-proof revocation in 0.1.0 — see Revocation overview.)
Relying-party authenticationWas the request sent to the correct wallet?
User approvalDid the user consciously approve release?
Service policyDoes your access policy allow this credential at this time?

See Implementation profile §10.

Rejection conditions

Reject an OpenAC response if any of the following hold:

  • Profile version or parameters are unsupported.
  • Credential binding identifier is unknown or unsupported.
  • Transcript binding fails: nonce, audience, or statement does not match the request.
  • Freshness check fails: nonce already used, request expired, or freshness material missing.
  • Prepare/Show linkage fails: the Show proof does not commit to the same messages as Prepare.
  • Proof verification fails.
  • Declared statement type is unsupported.
  • Output values are inconsistent with the proven statement.
  • Response carries an undeclared extension or downgrade indicator.

Issuer assumptions

  • P-256 / ES256 issuer keys in the PoC (trusted per paper's security section).
  • Disclosure digests must match issuer-issued _sd entries — malformed issuance surfaces as INVALID_JWT / MISSING_DISCLOSURE errors in the SDK. See Errors.

Revocation hooks

There is no precompute/present parameter for on-chain or accumulator revocation witnesses in 0.1.0. Scenario planning for future in-proof checks appears in OpenAC Studio rulesets. See Revocation overview for the design space.

Threat-model tooling

wallet-unit-poc/openac-studio models verifier/issuer collusion and revocation handling for educational walkthroughs — not required for bare SDK integration.