# Verify caller credentials with VP evidence

> Gate a policy on a cryptographically verified issuer and claims, not just a bearer token, without re-implementing verification yourself.

This guide enables a surface’s VP Evidence block, so an inbound verifiable presentation is cryptographically verified and its holder, issuer, and claims are injected into policy input as identity_binding, ready for a Rego rule to gate on. For the conceptual model, see [OPA policies →](/products/affinidi-trust-fabric/agent-stream/concepts/opa-policies.md).

A JWT bearer token proves a caller holds a token signed by whoever issued it; it says nothing about a separate, cryptographically verifiable credential, a verifiable presentation (VP), that a caller may carry on top of that, for example a trust-fabric-issued credential proving the caller is a recognised agent. Without VP evidence enabled, input.identity_binding is never populated, so a policy has no way to gate on it.

Use this guide when:

- Callers present a verifiable presentation alongside or instead of a bearer token, and a policy needs to check its issuer or claims.

- You want a belt-and-braces gate that denies outright when no VP is presented, independent of whatever the policy itself decides.

- You are integrating with another Trust Fabric appliance or an agent that carries a VC-based identity.

You do not need this if your callers authenticate only with a JWT, API key, or mTLS certificate with no separate verifiable presentation involved.

## Prerequisites

- An active LLM Surface, ideally already gated by an OPA policy. See [Control access to a surface with an OPA policy](/products/affinidi-trust-fabric/agent-stream/how-to-guides/policies/control-access-to-a-surface-with-an-opa-policy.md) if you do not have one yet.

- Callers capable of presenting a VP, either as a header or in the request body.

## Steps

Enable VP evidence on the surface

Under SURFACES in the dashboard sidebar, select LLM, open your surface, select its Surface node (the root container the rest of the pipeline sits inside), and select Configure Surface Features in its side panel. Scroll to VP evidence and turn on Evaluate VP evidence.

Choose where the VP travels

Set VP carried in to Request header (the default) or JSON body field, matching how your callers actually attach the presentation. For the header option, confirm Header name matches what callers send; it defaults to X-Agent-Stream-Presentation.

Decide whether to require one outright

Turn on Deny when no VP is presented to deny a request outright when no VP is presented at all, before the surface’s policy even runs. Leave it off if a policy should decide case by case, including for callers with no VP.

Gate your policy on the verified identity

Update your policy’s Rego to check input.identity_binding, populated only once a presented VP verifies successfully:

```rego
package llm.policy

default allow = false

trusted_issuers := {
  "did:web:issuer.example.com",
}

allow if {
  input.identity_binding.verified
  trusted_issuers[input.identity_binding.issuer_did]
}
```

Save the surface

Select the save icon in the toolbar. The change applies immediately; no restart is required.

## Confirm

Replace <YOUR_APPLIANCE_HOST> and <YOUR_SURFACE_ROUTE> with your surface’s values.

### Test 1: a request with a VP from a trusted issuer returns 200

```bash
curl -k -X POST "https:///v1/chat/completions" \
  -H "X-Agent-Stream-Presentation: " \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      { "role": "user", "content": "Say hello." }
    ]
  }'
```

The -k flag disables TLS certificate verification. Use this for local testing only. Remove it in production.

Expected: 200 OK. The VP verified successfully, its issuer matched the policy’s allow-list, and identity_binding.verified was true.

### Test 2: a request with no VP is denied before the policy runs, when Deny when no VP is presented is on

Resend the same request with the X-Agent-Stream-Presentation header removed. Expected: denied before the policy even runs, since Deny when no VP is presented is a gate on top of whatever the policy decides.

## Troubleshooting

| Symptom | Likely cause | Fix |
| input.identity_binding is always absent, even though the caller attaches a VP. | VP evidence is not enabled, or VP carried in/Header name does not match what the caller actually sends. | Confirm Evaluate VP evidence is on, and that the header name (or body location) matches the caller’s request exactly. |
| Every request is denied, not just ones without a valid VP. | Deny when no VP is presented is on, but callers have not yet been issued a VP to present. | Turn off Deny when no VP is presented while onboarding callers, or issue VPs to every caller first. |
| A request from what should be a trusted issuer is still denied by the policy. | The VP’s issuer is not on the policy’s allow-list, or the VP itself failed verification, so identity_binding.verified never became true. | Confirm the issuer DID is listed in the policy, and that the presented VP is not expired or malformed. |
| identity_binding is present but verified is false. | The VP was received but failed cryptographic verification. | This is expected: a policy checking input.identity_binding.verified correctly denies an unverifiable presentation rather than trusting its claims blindly. |

## Next steps

- [Roll out an OPA policy globally with monitor-only mode](/products/affinidi-trust-fabric/agent-stream/how-to-guides/policies/roll-out-an-opa-policy-globally.md): Apply this same VP-based gate across every surface of a type.

- [Attribute usage to members and teams](/products/affinidi-trust-fabric/agent-stream/how-to-guides/teams/attribute-usage-to-members-and-teams.md): Combine a verified credential with per-caller quota enforcement.

- [Trust-fabric integration](/products/affinidi-trust-fabric/agent-stream/concepts/trust-fabric-integration.md): How gateway-to-gateway DIDComm and the model marketplace also rely on verifiable credentials.

## Related

- [OPA policies reference](/products/affinidi-trust-fabric/agent-stream/reference/policies/opa-policies.md): The identity_binding input fields and the VP Evidence block’s full field list.

- [Governance records](/products/affinidi-trust-fabric/agent-stream/concepts/governance-records.md): How a signed, tamper-evident transcript proves what a policy decided and why.

- [Security and access control](/products/affinidi-trust-fabric/agent-stream/concepts/security-and-access-control.md): How VP-based identity fits alongside source authentication and RBAC.
