🚧 Draft Specification — The AAuth protocol is under active development. APIs and wire formats may change as the spec evolves. See aauth-spec/ for the current draft. This SDK is not yet spec-complete — open an issue to give feedback or report bugs.
The AAuth protocol SDK for .NET — agent-to-resource authorization with cryptographic proof-of-possession. Visit aauth.dev for the full protocol documentation, tutorials, and community resources.
AAuth is a four-party authorization protocol for AI agents. Every HTTP request carries a cryptographic signature; protocol tokens are proof-of-possession bound. See the protocol spec for full details.
The four parties are:
- Agent — signs every outbound HTTP request (RFC 9421) and presents keying material in the
Signature-Keyheader. - Resource — verifies the signature, optionally challenges with a
resource_tokento demand a person-scopedauth_token. - Person Server (PS) — represents the user; manages missions, federates to AS, issues
aa-auth+jwtproving the person delegated access. - Access Server (AS) — issues auth tokens; enforces resource access policy.
Agent Provider (AP) is a supporting role that issues
aa-agent+jwttokens binding an agent's signing key to its identity.
The SDK supports six Signature-Key schemes (hwk, jkt-jwt, jwks_uri, jwks, jwt, self-jwt). AAuth agent requests use jwt across all four resource access modes; the other schemes serve server signing, AP ceremonies, Events or explicit generic demonstrations. The SDK includes challenge/exchange flows, verification middleware, token builders, admitted discovery and a Blazor GuidedTour. See the SDK documentation for usage guides.
The AAuth.Events companion adds subscribe tokens,
self-jwt event delivery, durable provider contracts and agent verification.
Run its six-step flow in either app at /events, or use make agent-events
after starting the stack. See Events for transport,
persistence and draft limitations.
AAuth supports four resource access modes. Each adds parties and capabilities, and they build on one another — adoption is incremental. Run make demo (no Docker) to start every service plus both UIs, then follow the demo column below. For the live-Keycloak federated experience, use make demo-keycloak.
| Mode | Parties | When to Use | Signing | See it in the demos |
|---|---|---|---|---|
| Identity-Based | Agent + Resource | Resource authorizes verified agent identity | jwt |
Profile /identified accepts agent JWT; generic signing demonstrations are separate |
| Resource-Managed (two-party) | Agent + Resource | Resource manages authorization without an external PS or AS | jwt plus opaque AAuth-Access |
GuidedTour → Resource-Managed (Two-Party); SampleApp → /inbox |
| PS-Asserted (three-party) | Agent + Resource + PS | Resource accepts identity claims (sub, email, tenant, groups, roles) from any Person Server |
jwt |
GuidedTour → PS-Asserted (Direct Grant) and PS-Asserted (Deferred); SampleApp → /calendar and /calendar-deferred |
| Federated (four-party) | Agent + Resource + PS + AS | Cross-domain access with the resource's own Access Server enforcing policy | jwt |
GuidedTour → Federated (Four-Party); SampleApp → /wallet. Live Keycloak consent: make demo-keycloak |
GuidedTour runs on http://localhost:5400 and SampleApp on http://localhost:5240. The GuidedTour home page lists every flow; pick one to walk it step by step. See Getting Started for the full breakdown of each mode.
Before writing any code, watch the protocol in action. The repo ships sample services and two interactive Blazor apps. The dev container has everything pre-configured; you can also run locally with the .NET 10 SDK.
make demo # starts every service + the stub Access Server + both UIsThen open the two UIs and click through the modes from the table above:
Guided Tour — http://localhost:5400
Step-by-step walk-through showing every HTTP exchange, header, and token claim across all protocol flows.
Sample App — http://localhost:5240
Self-contained Blazor app with AAuth authorization flows and separately labeled generic signing demonstrations. Both apps include Wallet Protocol, Catalog Gateway, account-bound Bookings and Events.
For the live-Keycloak federated experience, run make demo-keycloak instead. See
samples/README.md for the full list of sample projects and
configuration options.
Open this repo in VS Code → Reopen in Container. The container
provides .NET 10, the gh CLI, and the C# Dev Kit extensions.
Install the .NET 10 SDK, then:
dotnet build AAuth.slnxdotnet add package AAuth --prereleaseAn enrolled agent uses an AP-issued agent JWT and proves possession of its locally held key. Replace the example HTTPS endpoints with your configured provider and resource. For the runnable loopback configuration, use the sample setup.
using AAuth.Crypto;
using AAuth;
var keyStore = FileKeyStore.Default();
var key = keyStore.LoadOrCreate("my-agent");
var enrollment = await AAuthClientBuilder.Bootstrap("https://ap.example/enrol")
.WithKey(key).WithKeyStore(keyStore).EnrolAsync();
using var client = AAuthClientBuilder.Enrolled(key)
.RefreshingFrom("https://ap.example/refresh", enrollment.LocalKeyHandle!)
.WithKeyStore(keyStore)
.Build();
var response = await client.GetAsync("https://resource.example/data");
// Signature-Key: sig=jwt;jwt="<aa-agent+jwt>"Generic HWK signing remains available for explicitly generic Signature Keys endpoints; it is not an AAuth access mode.
The PS-Asserted flow is the primary authorization model. The resource delegates authorization to the agent's Person Server, which prompts the user for consent:
sequenceDiagram
participant Agent
participant Resource
participant PS as Person Server
participant User
Agent->>Resource: GET /data (signed, agent token)
Resource-->>Agent: 401 + resource_token (aud=PS)
Agent->>PS: POST /token (signed, resource_token)
PS->>User: Consent prompt (scope, justification)
User-->>PS: Grant consent
PS-->>Agent: auth_token (aa-auth+jwt)
Agent->>Resource: GET /data (signed, auth_token)
Resource-->>Agent: 200 OK
On the agent side, building the client with WithChallengeHandling makes the entire 401 → exchange → retry cycle automatic — your code just makes the request:
using AAuth.Crypto;
using AAuth;
var key = AAuthKey.Generate();
// A hosted service acts as its own Agent Provider (self-issuing).
using var client = AAuthClientBuilder.SelfIssuing(key)
.As("https://my-service.example", "aauth:my-service@my-service.example")
.WithKid("svc-key-1")
.WithPersonServer("https://ps.example")
.WithChallengeHandling() // automatic 401 → PS exchange → retry
.Build();
var response = await client.GetAsync("https://resource.example/data");
// 1. Agent signs GET with agent token → Resource verifies, returns 401 + resource_token
// 2. ChallengeHandler POSTs resource_token to PS token endpoint
// 3. PS validates agent, prompts user for consent, issues auth_token
// 4. Agent retries GET signed with auth_token → Resource verifies → 200 OKWhat happens step by step:
- Agent signs the request with its agent token (
Signature-Key: sig=jwt;jwt="...") - Resource verifies the signature, reads the
psclaim, returns401with aresource_token(audience = PS URL) - Agent POSTs the
resource_tokento the PS's token endpoint (signed request) - PS validates the agent token, prompts the user for consent on the requested scope
- User grants consent; PS issues an
auth_token(aa-auth+jwt) containing identity claims (sub,email, etc.) - Agent retries the original request signed with the
auth_token - Resource verifies the auth token signature and claims →
200 OK
See Getting Started for a detailed walk-through, including deferred consent.
The snippets above are agent-side (the client). Hosting a party — a resource, or a self-issuing agent service — uses the SDK's server helpers. Start with the resource, since it's the party that issues the challenge.
The resource verifies signatures, publishes metadata, and issues resource token challenges:
using AAuth.Crypto;
using AAuth;
var builder = WebApplication.CreateBuilder(args);
var resourceKey = AAuthKey.Generate();
// One DI call registers the verifier, discovery clients, JTI store, and metadata.
builder.Services.AddAAuthResource(options =>
{
options.Issuer = "https://resource.example";
options.SigningKeys["resource-key-1"] = resourceKey;
options.ScopeDescriptions = new() { ["read"] = "Read your data" };
});
builder.Services.AddAAuthAuthentication();
builder.Services.AddAAuthAuthorization();
var app = builder.Build();
// Serve /.well-known/aauth-resource.json + JWKS
app.MapAAuthWellKnown();
// One declarative pipeline. Per-route scope/role lives on the endpoint; this
// single post-routing middleware verifies and challenges each matched endpoint.
app.UseRouting();
app.UseAAuth(o => o.TrustedAuthTokenIssuers = new HashSet<string> { "https://ps.example" });
app.UseAuthentication();
app.UseAuthorization();
// Protected endpoint — reached only after the auth token is verified.
app.MapGet("/data", (HttpContext ctx) => Results.Ok(new { ok = true }))
.RequireAAuth(scope: "read");The single UseAAuth middleware (placed after UseRouting()) reads each endpoint's .RequireAAuth(...) requirement: it verifies the HTTP signature and, when an auth token is required, automatically returns 401 with an AAuth-Requirement header carrying a resource token. The optional TrustedAuthTokenIssuers allow-list restricts which Person Servers the resource will accept auth tokens from; omit it (or assign AAuthTrust.Any) to accept any verifiable Person Server — the spec default — with claims namespaced by issuer.
Hosted services act as their own Agent Provider — generate a key, publish metadata, and self-issue tokens:
using AAuth.Crypto;
using AAuth;
using AAuth.Server.Metadata;
var builder = WebApplication.CreateBuilder(args);
var key = AAuthKey.Generate();
const string Kid = "svc-key-1";
var issuer = "https://my-service.example";
var app = builder.Build();
// Publish agent metadata so resources can discover the JWKS
app.MapAAuthAgentWellKnown(new AAuthAgentMetadataOptions
{
Issuer = issuer,
SigningKeys = new Dictionary<string, IAAuthKey> { [Kid] = key },
});
// Build signed client with automatic token refresh and challenge handling
using var client = AAuthClientBuilder.SelfIssuing(key)
.As(issuer, "aauth:my-service@my-service.example")
.WithKid(Kid)
.WithPersonServer("https://ps.example")
.WithChallengeHandling()
.Build();See the Server Guide for the full resource-side token issuance, Person Server, and Access Server code.
Full SDK documentation lives in docs/:
- Getting Started — install, generate a key, three-party flow deep dive, enrollment models
- Concepts — the four participants and how the SDK maps to them
- Glossary & Acronyms — every acronym and short protocol term used across the repo
- Signing Modes - six carriers, distinct from four AAuth access modes
- Workflows — identity-based, PS-asserted, federated
- Server Guide — verification middleware, token issuance
- Configuration Reference
dotnet test AAuth.slnx # full suite (unit + conformance)
dotnet test tests/AAuth.Tests # SDK unit + integration tests only
dotnet test tests/AAuth.Conformance # spec conformance suite only| Path | Description |
|---|---|
| src/AAuth/ | AAuth SDK library (the NuGet package) |
| docs/ | SDK documentation — signing modes, workflows, server guides |
| samples/ | Seven focused resources including Bookings and Catalog, PS/AS/AP hosts, console agents, GuidedTour and SampleApp |
| tests/ | Unit, integration, and spec-conformance tests |
| aauth-spec/ | Immutable protocol snapshots 01, 02, 08, 09 and 10 with pinned companion drafts |
This SDK targets draft-10 of the AAuth protocol specification:
| Spec | Draft |
|---|---|
| AAuth protocol | 10 |
| Bootstrap | 02, informational |
| Rich Resource Requests | 01 |
| Events | 00, revised |
| HTTP Signature Keys | 08 |
The pinned source is commit 9dee49fbf49074d1460d0a7c0670bf355aef5e1e,
published 2026-08-06. All four access modes, account binding, AS clarification,
issuer-qualified revocation and parent-mediated four-party delegation are
implemented. Optional X.509/cached carriers and third-party login hosting are
not implemented. Platform attestation, production stores/policies and native
push transports remain deployment responsibilities. Events uses single-shot
sample delivery with literal issuer/eid deduplication; recurring-event ambiguity
is not hidden by the supported-carrier claim.
Local Release and both policy-mode browser gates pass. External whoami identity
access succeeds, but its scoped endpoint returned person-token rather than the
pinned auth-token challenge; full external authorization interop is not claimed.
See SPEC-VERSION,
snapshot history, and the
conformance dispositions.
- Open this repo in the dev container (ensures consistent tooling).
- Create a branch off
main. - Make your changes — run
dotnet build AAuth.slnxanddotnet test AAuth.slnxbefore submitting. - Open a pull request against
main.

