Menu AuthGuardHelp centre
Portuguêsauthguard.pt

Developers · Approval requests

Verify a decision's signed receipt

  • 1 min read
  • SaaS · Self-hosted

Every decision on a request comes with a signed receipt that your system can verify without depending on the service.

Step 1: the request digest

Recompute SHA-256("AUTHGUARD\0INTENT\0V1\0" ‖ intentJson) over the UTF-8 bytes of intentJson exactly as received (the prefix holds three NUL bytes). It must equal intentDigest.

Step 2: the receipt digest

Check receipt.payloadDigestSha256 == SHA-256(receipt.payload).

Step 3: the receipt content

Parse receipt.payload (schema authguard.intent.decision-receipt, version 1) and check:

  • the expected intentId, tenantId, clientId and subjectUserId;
  • intentDigestSha256 equals step 1;
  • decision is approved or denied;
  • factor is passkey or device (a phone decision);
  • decidedAtEpochSeconds lies between the request's issue and expiry.

The SDKs do steps 1 to 3 with verifyIntentDigest and checkReceipt (TypeScript) or verify_intent_digest and check_receipt (Python).

Step 4: the signatures

receipt.signatureEnvelope holds two signatures, ML-DSA-65 and Ed25519, over the intent_decision_receipt domain, with the request identifier as object and receipt.payload as canonical bytes. Verify both with the installation's public keys. At this stage the operator delivers those public keys on request, through a separate channel; compare the SHA-256 fingerprints you are given.

When to verify

Before running an approved operation, and whenever you must prove to an auditor that the operation was approved by that person, with that factor, at that time.

Error codes in this article

Article referencedev-receipts-verifyquote it to support to identify this article. Checked against the product at commit 5f6607a8.