Skip to content
Registry StackDocsDevelopment (unreleased)

See Evidence Gateway refuse unsafe requests

For the assertion provider and consumer or verifier

View as Markdown

Complete Return a governed value before starting this tutorial. You will reuse that project to observe two boundaries: Evidence Gateway denies a purpose the provider did not authorize, and the verifier rejects a response changed after signing.

Outcome
Two unsafe changes are refused without producing trusted evidence.
Time
About 10 minutes
Level
Local development with synthetic data
Prerequisites
The adult-status project after completing the governed-value tutorialThe registry.py source from the first Evidence Gateway tutorialPython 3 and curl

In the terminal that owns the Python registry, return to the first-evidence-assertion directory and start the source:

Terminal window
python3 registry.py

In another terminal, enter the existing project and start a fresh local generation:

Terminal window
cd adult-status
evidencectl dev --detach
Evidence Gateway ready at http://127.0.0.1:8080
Mint ready at http://127.0.0.1:8081

Prepare a normal age-bracket request. This records the exact verification expectations before any response exists:

Terminal window
evidencectl request prepare age-bracket \
--purpose service-path-selection \
--subject person_id=person-123 \
--name refusal-check

The provider configured service-path-selection for this question. Preparation also obtained a short-lived local token carrying the tutorial caller’s configured authority claims. The token does not authorize one exact request. Evidence Gateway applies those claims to the request it receives.

Copy the request, then change only its purpose:

Terminal window
cp .evidence/requests/refusal-check/request.json unauthorized-request.json
python3 - <<'PY'
import json
from pathlib import Path
path = Path("unauthorized-request.json")
request = json.loads(path.read_text())
request["purpose"] = "age-check"
path.write_text(json.dumps(request))
PY

The retained authorization does not grant age-check for this requirement. Send the changed request across the same HTTP boundary:

Terminal window
curl --silent --show-error \
--config .evidence/requests/refusal-check/authorization.curl \
--request POST \
--url http://127.0.0.1:8080/v1/evidence \
--header 'Content-Type: application/json' \
--header 'Accept: application/jose+json' \
--data-binary @unauthorized-request.json \
--output unauthorized-response.json \
--write-out 'HTTP %{http_code}\n'
HTTP 403

Inspect the public problem:

Terminal window
python3 -m json.tool unauthorized-response.json

The response identifies only the closed not_authorized problem and an opaque operation ID. It does not reveal source facts, selector values, grants, or credentials. The registry terminal shows no new GET /people/person-123 because authorization failed before source access.

Now send the unchanged prepared request:

Terminal window
curl --silent --show-error \
--config .evidence/requests/refusal-check/authorization.curl \
--request POST \
--url http://127.0.0.1:8080/v1/evidence \
--header 'Content-Type: application/json' \
--header 'Accept: application/jose+json' \
--data-binary @.evidence/requests/refusal-check/request.json \
--output authorized-response.jws.json

Verify it against the expectations retained during preparation:

Terminal window
evidencectl verify authorized-response.jws.json \
--context .evidence/requests/refusal-check/verification.json \
--output authorized-response.verified.json
VERIFIED

This is the control case. The authorized, unchanged response is trusted.

Copy the flattened JWS and change one character in its signed payload:

Terminal window
cp authorized-response.jws.json tampered-response.jws.json
python3 - <<'PY'
import json
from pathlib import Path
path = Path("tampered-response.jws.json")
document = json.loads(path.read_text())
payload = document["payload"]
document["payload"] = ("A" if payload[0] != "A" else "B") + payload[1:]
path.write_text(json.dumps(document))
PY

Try to verify the changed response:

Terminal window
if evidencectl verify tampered-response.jws.json \
--context .evidence/requests/refusal-check/verification.json \
--output tampered-response.verified.json
then
echo 'unexpected verification success' >&2
exit 1
fi
test ! -e tampered-response.verified.json
echo 'TAMPER REFUSED'
evidencectl: Evidence Gateway response verification failed
TAMPER REFUSED

Verification fails without publishing a trusted payload. Your application must make decisions only from the verifier output, never from an unverified response body.

Stop the local services:

Terminal window
evidencectl dev stop

Return to the registry terminal and press Ctrl+C.

  • A valid access token does not widen the provider’s authorized purpose.
  • Authorization failure happens before the protected source is called.
  • A signed response cannot be changed without invalidating its signature.
  • Only verified output is safe for an application to consume.