Skip to content

About

AAuth .NET SDK and Samples

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

AAuth SDK for .NET

CI NuGet NuGet Downloads

🚧 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.

What is AAuth?

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-Key header.
  • Resource — verifies the signature, optionally challenges with a resource_token to demand a person-scoped auth_token.
  • Person Server (PS) — represents the user; manages missions, federates to AS, issues aa-auth+jwt proving 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+jwt tokens 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 five 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.

Access Modes

AAuth supports five 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
Agent Identity Agent + Resource Resource authorizes verified agent identity (agent-token) 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
Person Identity Agent + Resource + PS Resource requires a PS-issued person token before issuing an auth-token challenge jwt with a person token Intermediate step in PS authorization; see Getting Started
PS Authorization (three-party) Agent + Resource + PS Resource accepts consent and identity claims (sub, email, tenant, groups, roles) from a trusted Person Server jwt GuidedTour → PS Authorization (Direct Grant) and PS Authorization (Deferred); SampleApp → /calendar and /calendar-deferred
Federated authorization (four-party) Agent + Resource + PS + AS Cross-domain access with the resource's own Access Server enforcing policy jwt GuidedTour → Federated authorization (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.

See It Run

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 UIs

Then 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.

Guided Tour

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.

Sample App

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.

Dev container (recommended)

Open this repo in VS Code → Reopen in Container. The container provides .NET 10, the gh CLI, and the C# Dev Kit extensions.

Local setup

Install the .NET 10 SDK, then:

dotnet build AAuth.slnx

Quick Start

dotnet add package AAuth --prerelease

An 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. Loopback identifiers are a development-only exception: configure only exact localhost/127.0.0.1 origins with AAuthEgressPolicy.ForDevelopmentLoopback(...); AAuth DI registrations reject those policies in Production.

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.

Three-Party Flow (Agent → Resource → Person Server)

The PS authorization flow is the primary authorization model. The resource first asks for a person token, then issues a resource token bound to that presented person token, and the agent exchanges both at the Person Server:

sequenceDiagram
    participant Agent
    participant Resource
    participant PS as Person Server
    participant User

    Agent->>Resource: GET /data (signed, agent token)
    Resource-->>Agent: 401 + requirement=person-token
    Agent->>PS: POST /person (signed, resource)
    PS-->>Agent: person_token
    Agent->>Resource: GET /data (signed, person_token)
    Resource-->>Agent: 401 + requirement=auth-token, resource_token
    Agent->>PS: POST /token (signed, resource_token + presented_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
Loading

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 returns requirement=person-token
// 2. ChallengeHandler requests a person token and retries the resource
// 3. Resource returns requirement=auth-token + resource_token bound by presented_jti
// 4. ChallengeHandler POSTs resource_token + presented_token to the PS
// 5. PS validates the pair, prompts user for consent, issues auth_token
// 6. Agent retries GET signed with auth_token → Resource verifies → 200 OK

What happens step by step:

  1. Agent signs the request with its agent token (Signature-Key: sig=jwt;jwt="...")
  2. Resource verifies the signature and returns 401 with requirement=person-token
  3. Agent POSTs to the PS /person endpoint and receives a PS-issued person_token
  4. Agent retries the resource with the person_token; the resource returns 401 with requirement=auth-token and a resource_token bound to the presented token's jti
  5. Agent POSTs both resource_token and presented_token to the PS token endpoint
  6. PS validates the pair, prompts the user for consent on the requested scope, and issues an auth_token (aa-auth+jwt) containing identity claims (sub, email, etc.)
  7. Agent retries the original request signed with the auth_token
  8. Resource verifies the auth token signature and claims → 200 OK

See Getting Started for a detailed walk-through, including deferred consent.

Building Servers

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.

Resource (Server-Side)

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.Trust.AuthTokenIssuers.Allowed = 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. With no Access Server configured, this is the three-party PS Authorization mode: leave Trust.AuthTokenIssuers unset (or assign AAuthTrust.Any to its Predicate) to accept any verifiable Person Server, with claims namespaced by issuer.

For four-party resources, declare the AS once on the resource registration:

builder.Services.AddAAuthResource(options =>
{
    options.Issuer = "https://resource.example";
    options.AccessServer = "https://as.example";
    options.SigningKeys["key-1"] = resourceKey;
});

That single AccessServer value directs resource-token challenges to the AS and makes auth-token verification fail closed by default: the resource accepts AS-issued aauth-access.json auth tokens from that AS, not direct PS-issued aauth-person.json auth tokens. Mixed PS/AS acceptance is an advanced low-level configuration that must set ExpectedAuthTokenDwk = null and provide a trust policy that checks AAuthTrustContext.TokenDwk.

Self-Hosted Agent (Server-Side)

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(options =>
{
    options.Issuer = issuer;
    options.SigningKeys = new AAuthSigningKeySet(Kid, key);
    // Optional: advertise an AP Events inbox or allow localhost callbacks.
    // options.EventEndpoint = $"{issuer}/events";
    // options.LocalhostCallbackAllowed = true;
});

// 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.

Documentation

Full SDK documentation lives in docs/:

Testing

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

Repository Layout

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, 10 and 11 with pinned companion drafts

Spec Compatibility

This SDK targets draft-11 of the AAuth protocol specification:

Spec Draft
AAuth protocol 11
Bootstrap 02, informational
Rich Resource Requests editor's copy at the draft-11 tag
Events 00, revised
HTTP Signature Keys 09

The pinned source is commit 178e9e68b6578e4d6f7d0bf30f33b4c38833e3a1, published 2026-09-25. The locally validated implementation covers agent-identity verification and agent-token challenges (AgentTokenVerificationTests, ChallengeMiddlewareTests), resource-managed AAuth-Access (ResourceManagedFlowTests), person-token access (AuthorizationEndpointTests), PS authorization with presented_token exchanges (PersonServerMapperTests, ChallengeMiddlewareTests) and four-party trust (DeferredFederationTests, FourPartyTrustTests), mission_s256 hashing, resource-scoped person-token issuance and termination/expiry (MissionS256Tests, MissionPersonTokenIssuanceTests, MissionTerminatedTests), sub-agent identifiers, token verification and parent-mediated minting (AgentIdTests, AgentTokenVerificationTests, PersonServerMapperTests), call chaining through the person's PS (CallChainingTests, CallChainingHandlerTests), {jti, exp} revocation with cascades (RevocationLifecycleTests, PersonTokenRevocationCascadeTests, AgentTokenRevocationCascadeTests), 202 auth-token delivery and polling (ChallengeHandlerTests deferred auth-token cases, HeldInvocationTests, PollingErrorTests), and R3 per-call single use (ResourceR3Tests). Optional accept_signature_algs advertisement, aauth-resource links, Budgets, R3 release gating, 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 delivery deduplicates on (iss, jti).

Local Release, stub and Keycloak policy-mode browser gates pass. External interop against third-party draft-11 deployments has not been run. See SPEC-VERSION, snapshot history, and the conformance dispositions.

Contributing

  1. Open this repo in the dev container (ensures consistent tooling).
  2. Create a branch off main.
  3. Make your changes — run dotnet build AAuth.slnx and dotnet test AAuth.slnx before submitting.
  4. Open a pull request against main.

About

AAuth .NET SDK and Samples

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages