Agent Trust End-to-End Example
This example demonstrates an end-to-end path:
- Agent runtime evaluates policy and mints a capability SD-JWT.
- Agent sends the token to a tool API.
- Tool API verifies token + policy and authorizes endpoint execution.
1. Shared Setup
using Microsoft.IdentityModel.Tokens;
using SdJwt.Net.AgentTrust.Core;
using SdJwt.Net.AgentTrust.Policy;
using System.Security.Cryptography;
var signingKey = new SymmetricSecurityKey(RandomNumberGenerator.GetBytes(32));
var nonceStore = new MemoryNonceStore();
var policyEngine = new DefaultPolicyEngine(
new PolicyBuilder()
.Deny("*", "ledger", "Delete")
.Allow("agent://finance-*", "ledger", "Read", c =>
{
c.MaxLifetime(TimeSpan.FromSeconds(45));
c.Limits(new CapabilityLimits { MaxResults = 100 });
c.RequireDisclosure("ctx.correlationId");
})
.Build());
2. Agent Runtime (Outbound)
using SdJwt.Net.AgentTrust.Maf;
var issuer = new CapabilityTokenIssuer(
signingKey,
SecurityAlgorithms.HmacSha256,
nonceStore);
var mcpAdapter = new McpTrustAdapter(
issuer,
policyEngine,
"agent://finance-eu",
new Dictionary<string, string> { ["ledger"] = "https://tools.example.com" });
var token = await mcpAdapter.MintForToolCallAsync(
toolName: "ledger",
arguments: new Dictionary<string, object> { ["action"] = "Read" },
context: new CapabilityContext
{
CorrelationId = Guid.NewGuid().ToString("N"),
WorkflowId = "wf-ledger-sync"
});
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("Authorization", $"SdJwt {token.Token}");
var response = await http.GetAsync("https://tools.example.com/ledger/entries");
3. Tool API (Inbound)
using Microsoft.IdentityModel.Tokens;
using SdJwt.Net.AgentTrust.AspNetCore;
using SdJwt.Net.AgentTrust.Policy;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSingleton<IPolicyEngine>(policyEngine);
builder.Services.AddAgentTrustVerification(options =>
{
options.Audience = "https://tools.example.com";
options.TrustedIssuers = new Dictionary<string, SecurityKey>
{
["agent://finance-eu"] = signingKey
};
options.EmitReceipts = true;
});
var app = builder.Build();
app.UseAgentTrustVerification();
app.MapControllers();
app.Run();
Controller:
using Microsoft.AspNetCore.Mvc;
using SdJwt.Net.AgentTrust.AspNetCore;
[ApiController]
[Route("ledger")]
public sealed class LedgerController : ControllerBase
{
[HttpGet("entries")]
[RequireCapability("ledger", "Read")]
public IActionResult GetEntries()
{
var issuer = HttpContext.GetAgentIssuer();
var context = HttpContext.GetCapabilityContext();
return Ok(new
{
issuer,
correlationId = context?.CorrelationId,
entries = new[] { "entry-1", "entry-2" }
});
}
}
4. Expected Outcomes
- Token missing/invalid:
401or403 - Capability mismatch:
403 - Policy deny:
403 - Valid token + allowed action:
200
5. Hardening Notes
- Replace in-memory nonce store in production.
- Keep capability token TTL short.
- Scope audiences per tool endpoint, not per environment.
- Persist receipts in centralized observability stack.