DEV Community

Cover image for Testing sender-constrained tokens in an agent connector
Ahmad Afzal
Ahmad Afzal

Posted on Fully Autonomous

Testing sender-constrained tokens in an agent connector

A resource server that validates an access token has not necessarily established that the presenter is the workload that received it. Sender constraints address that gap. They do not turn a valid request into a permitted business action.

This walkthrough designs acceptance tests for a fictional supplier-record connector. It is not a production implementation, benchmark or claim that any specific stack already passes. The assumed attacker can copy an access token but cannot initially extract or invoke the workload's private key. We will deliberately change that assumption later.

Define the boundary before selecting the library

The fictional path is connector to gateway to supplier API. The connector gets a token from an authorisation server. Supplier reads and supplier amendments have separate permissions. The gateway is externally reachable; the API's internal listener is not intended as a bypass.

The initial property is precise: copying the token into an isolated client must not be sufficient to invoke the protected resource. OAuth Security Best Current Practice discusses sender-constrained tokens as a defence against stolen-token misuse. We still need ordinary token validation, least privilege and resource-level authorisation.

Threat comparison shows copied tokens blocked under stated assumptions, compromised signing capability outside that protection, and harmful legitimate actions requiring business policy.

Threat model for the fictional connector: token-only theft, signing-capability compromise and a harmful request from the legitimate workload.

Do not store private keys or raw tokens in the test report. Use safe correlation identifiers and record the expected decision, observed decision and enforcing component. A security test that creates another credential leak has a certain commitment to realism, but it is not the realism we need.

The DPoP path

Under RFC 9449, the client supplies a signed DPoP proof with the request. Validation covers the signature, permitted algorithm, required claims, HTTP method, target URI and freshness conditions. When a protected resource is accessed, the proof also contains the access-token hash, and its public key must match the token's binding. Use a maintained implementation and the complete standard, not this paragraph as validation code.

The target URI comparison excludes query and fragment components. The base proof does not cover the request body. This matters if someone describes DPoP as signing the purchase order: it does not, by itself, bind an approval to every business field. TLS is still required. Transaction-specific approval and integrity requirements belong in a separately defined design.

DPoP also is not OAuth client authentication by itself. A confidential client can use an appropriate authentication method alongside it. Keep those checks separate in code, telemetry and the mental model, or a later refactor may accidentally treat one successful verification as a substitute for the other.

For a deployment using server nonces, test nonce challenges and acceptance at the correct server. Authorisation-server and resource-server nonces are not interchangeable. Test clock tolerance and duplicate-proof handling across replicas. A single-process happy path does not tell you what happens when a replay reaches another instance.

The mTLS path

RFC 8705 defines OAuth mutual-TLS client authentication and certificate-bound access tokens. For the latter, the resource verifies the relationship between the token binding and the certificate used for the mutual-TLS connection. Client authentication at issuance and sender-constrained use at the resource are distinct capabilities.

If the gateway terminates TLS, write down how the verification result reaches the API. A client-controlled header carrying a certificate-looking string is not adequate provenance. The internal trust boundary, header handling and bypass routes need their own tests. Alternatively, place enforcement where the relevant connection and token can be checked together, according to the supported deployment architecture.

Certificate rotation must be exercised with the token lifecycle. Do not assume a token bound to an earlier certificate becomes usable with a replacement certificate merely because the workload's friendly name stayed the same. Recovery needs a supported issuance and transition plan, not a temporary switch that accepts anything.

Two paths compare DPoP verification of token and signed proof with mTLS verification of token and connection certificate; both still require business authorisation.

DPoP and certificate-bound token paths, with the gateway verification and downstream trust boundary shown explicitly.

Build tests that separate the failure classes

Create a baseline request using the legitimate client, valid token and correct proof material. It should reach the business-policy decision. A token-only copy from an isolated client should fail at the sender-constraint boundary. A different signing key or mismatched certificate should fail there too.

For DPoP, exercise altered methods, incorrect target URIs, incorrect token hashes, expired acceptance windows and the deployment's replay policy. Preserve the difference between the proof identifier and the business operation identifier. A new proof for a retried request is not permission to execute the business operation twice. Idempotency is a separate application property.

For mTLS, test the actual client-to-enforcement connection and any proxy-mediated path. A successful TLS handshake is not the final assertion. Verify that the resource refuses a token whose binding does not match the authenticated certificate. Also verify that an unprotected alternate route is not available to the same resource.

Acceptance matrix distinguishes copied token, wrong key, invalid or replayed proof and valid proof with an excessive action.

Proposed acceptance matrix: copied token, incorrect key, invalid proof, replay and excessive business action each have a named expected enforcement point.

Now make a request with valid credential proof but insufficient business permission. In our fictional case, the read connector attempts to amend a supplier record. The sender check can succeed while the policy check denies the operation. Record that distinction. It demonstrates composition of controls rather than a mysterious overall failure.

Finally, model signing-capability compromise. If an attacker can use the legitimate key or invoke a signing service without adequate restrictions, token binding alone cannot distinguish them from the legitimate holder. The response involves workload isolation, access to the signer and credential recovery. Do not report this case as a failure of the token-only threat assumption; report that the assumption no longer holds.

Make failure behaviour part of the contract

What happens when proof generation or verification is unavailable? For this example, a sensitive amendment is deferred rather than retried through a bearer-only path. That is an explicit example policy, not a universal standard requirement. Read-only operations may have a different availability decision, but the exception needs a named owner and a documented exposure.

The agent audit-trail guide helps with evidence design. Record enough to connect a denied request to its workload, policy version and enforcement point without logging secrets. The incident-to-regression workflow is the next step when a real failure reveals a missing case.

A passing sender-constraint suite establishes a bounded result about credential use. It does not establish prompt-injection resistance, appropriate permissions or correct transaction outcomes. Keeping that boundary visible makes the tests more useful, not less impressive.

Top comments (0)