Skip to main content

Permission Receipts

Issue a tamper-proof token that proves an access decision happened. Verify it later — no database, no trust.

What it is​

An access check tells you "yes, subject X can do Y" right now. A receipt tells you what the decision was at a specific moment — with a cryptographically signed token as proof.

The flow is a single API call. You don't call /access/check then /receipt — you call /receipt and get both the decision and the signed token in one response:

Why receipts instead of plain access checks​

An access check is ephemeral — run it now and the answer may differ from what it was before. Policies change, roles are reassigned, permissions are deleted. If an auditor asks "did Alice really have permission on June 1st?", you can't re-run the check and trust the result.

A receipt freezes the answer. The token is signed with an HMAC only Warden AuthZ can produce, so anyone can verify it later without access to your database or audit logs. The token itself is the proof.

Issuing​

One call evaluates access and mints the receipt. No extra round trip.

cURL

curl -X POST https://api.wardenauthz.com/v1/access/receipt \
-H "x-api-key: YOUR_MANAGEMENT_KEY" \
-H "Content-Type: application/json" \
-d '{
"subjectId": "subject-alice",
"resource": "documents",
"action": "delete"
}'
{
"receipt": "rt1.eyJqdGkiOiI...2FjMjA4M2U",
"decision": {
"allowed": true,
"subjectId": "subject-alice",
"resource": "documents",
"action": "delete",
"scopeId": "workspace-eng",
"reasoning": {
"matchedBy": [
{
"roleId": "role-editor",
"roleName": "Editor",
"effect": "allow",
"permissionId": "perm-delete-docs",
"scopeId": "workspace-eng"
}
],
"deniedBy": [],
"scopeChain": ["workspace-eng", "org-acme"]
}
}
}

The reasoning shows exactly which roles and permissions matched (or denied) the request — the evidence behind the decision.

Verifying​

Anyone with the token can verify it. They call your API, your server re-computes the HMAC, and confirms it's genuine.

curl -X POST https://api.wardenauthz.com/v1/access/receipt/verify \
-H "x-api-key: YOUR_MANAGEMENT_KEY" \
-H "Content-Type: application/json" \
-d '{ "receipt": "rt1.eyJqdGkiOiI...2FjMjA4M2U" }'
{
"valid": true,
"claims": {
"jti": "unique-receipt-id",
"subjectId": "subject-alice",
"resource": "documents",
"action": "delete",
"scopeId": "workspace-eng",
"allowed": true,
"issuedAt": 1750000000,
"expiresAt": 1750003600
}
}

Token format​

rt1.<base64url(claims)>.<base64url(HMAC-SHA256)>

The token is self-contained — the claims are in the payload, the signature proves they haven't been tampered with. Symmetric HMAC means only Warden AuthZ can mint or verify tokens. There is no offline verification path, by design.

Edge cases​

  • Tampered receipts return valid: false. The signature won't match.
  • The receipt proves the decision happened, not that it's still valid. Verification doesn't re-evaluate access — it only checks the signature. Policies may have changed since the receipt was issued.
  • Receipts don't expire. They are permanent evidence. The HMAC signature verifies the claims haven't been tampered with — there is no time gate.
  • Verifying a receipt costs one access check. Each call to /receipt/verify is metered. Rate-limit abuse the same way you would any other endpoint.
  • Receipts are replayable within their TTL. There is no nonce or revocation list — by design (no database). If replay is a concern, treat receipts as short-lived tokens.