Skip to the content.

Agent Identity with JWKS

In this demo, we’ll establish Agent Identity with HTTP message signing using JWKS as described in detail in this flow. For the draft of AAuth please visit the GitHub repo.

← Back to index

Watch the demo

Run the components

To run this demo, please set up the prerequisites (Keycloak, Agentgateway, Jaeger).

We will run the Supply-Chain UI with the following:

> cd supply-chain-ui
> cp env.example .env
> npm start

The supply-chain-ui is secured with OIDC and Keycloak. AAuth CAN replace OIDC in our demo, but I wanted to be more realistic in an enterprise setting. That is, most applications will be secured with SSO/OIDC but within our agent calls, we can use AAuth.

Make sure you have Keycloak running, naviate your browser to http://localhost:3050 and your browser should direct you to Keycloak to login with OIDC:

Supply Chain UI

You can login with “mcp-user / user123”. Then you should see the UI:

Supply Chain UI

Run the Backend Agents and Components

Now you have access to the UI, but the backend components need to be started. Run each component in its own terminal:

From the backend directory:


      > cd backend
      > cp env.jwks .env # just need to do this one time
      > uv run . --signature-scheme jwks
    

From the agentgateway directory:


      > ./agw -f agentgateway/config.yaml
    

From the supply-chain-agent directory:


      > cd supply-chain-agent
      > cp env.jwks .env # just need to do this one time
      > uv run . --signature-scheme jwks --authorization-scheme signature-only
    

From the market-analysis-agent directory:


      > cd market-analysis-agent
      > cp env.jwks .env # just need to do this one time
      > uv run . --signature-scheme jwks --authorization-scheme signature-only
    

You’ll end up with the following:

Component Port Description
Keycloak 8080 Identity Provider (IdP); issues OIDC tokens, implements the AAuth protocol for agent/user auth tokens
Agentgateway 3000 Implements AAuth message signing/verification; used for policy enforcement, observability, rate limiting, etc.
UI 3050 Main UI for user interaction with the system
Backend 8000 Handles OIDC auth flows with Keycloak; backend API that calls the supply-chain-agent
Supply-Chain Agent 9999 Performs supply chain calculations; can call out to market-analysis-agent
Market Analysis Agent 9998 Can call out to MCP servers to perform additional analysis

Walking through the Demo Flow

From the main UI page, if you click the "Optimize Laptop Supply Chain" button, it should kick off the flow for the backend components:

sequenceDiagram
  participant UI as UI
  participant BE as Backend
  participant SCA as Supply-Chain Agent

  UI->>BE: 1. User clicks "Optimize Laptop Supply Chain"
  BE->>SCA: 2. POST /optimize (with agent identity JWT/JWS)
  SCA-->>BE: 3. Response: Optimization in progress/result
  BE-->>UI: 4. Return progress/result to user

This diagram illustrates the core sequence:

Supply Chain UI

Review Flow in Detail

Let’s take a look at what the backend logs look like:

INFO:     127.0.0.1:54969 - "GET /.well-known/aauth-agent HTTP/1.1" 200 OK
INFO:     127.0.0.1:54970 - "GET /jwks.json HTTP/1.1" 200 OK
INFO:     127.0.0.1:54965 - "POST /optimization/start HTTP/1.1" 200 OK
INFO:     127.0.0.1:54965 - "OPTIONS /optimization/progress/7c595657-a4b4-45b6-a8c4-f49696328963 HTTP/1.1" 200 OK
INFO:     127.0.0.1:54965 - "GET /optimization/progress/7c595657-a4b4-45b6-a8c4-f49696328963 HTTP/1.1" 200 OK

What we can see from these access logs is that something called the /.well-known/aauth-agent and jwks.json files to verify its public keys. So what happens is backend calls supply-chain-agent with the correct AAuth headers (which we’ll see shortly) and supply-chain-agent uses the JWKS scheme to identify the backend by verifying its signing keys.

If we look at the logs for the supply-chain-agent, it should look similar to:

INFO:http_headers_middleware:🔐 AAuth headers received: ['signature', 'signature-key', 'signature-input']
INFO:agent_executor:🔐 AAuth signature headers detected: ['signature', 'signature-key', 'signature-input']
INFO:agent_executor:🔐 AAuth scheme: JWKS - identified agent
INFO:agent_executor:🔐 Verifying AAuth signature (scheme: jwks)
INFO:agent_executor:🔐 VERIFYING with: method=POST, target_uri='http://supply-chain-agent.localhost:3000/'
INFO:aauth.signing:🔐 VERIFIER: verify_signature() called
INFO:aauth.signing:🔐 VERIFIER: method=POST, target_uri=http://supply-chain-agent.localhost:3000/
INFO:aauth.signing:🔐 VERIFIER: signature_input_header=sig1=("@method" "@authority" "@path" "signature-key");created=1770414041
INFO:httpx:HTTP Request: GET http://backend.localhost:8000/.well-known/aauth-agent "HTTP/1.1 200 OK"
INFO:httpx:HTTP Request: GET http://backend.localhost:8000/jwks.json "HTTP/1.1 200 OK"
INFO:agent_executor:✅ AAuth signature verification successful
INFO:agent_executor:🔐 Accepting request with valid signature (signature-only mode, scheme=jwks)
INFO:agent_executor:✅ Authorization successful: auth_token verified for agent: None
INFO:agent_executor:🔐 Using AAuth JWKS signing for downstream agent calls
INFO:     127.0.0.1:54968 - "POST / HTTP/1.1" 200 OK

In these logs, we can see the request to supply-chain-agent come in, that it verifies the message was signed with the expectd keys to verify backend’s identity and then a successful response.

Lastly, if we look at the logs for Agentgateway:

2026-02-06T21:19:22.418895Z     info    state_manager   loaded config from File("./agentgateway/config.yaml")
2026-02-06T21:19:22.419230Z     info    app     serving UI at http://localhost:15000/ui
2026-02-06T21:19:22.419244Z     info    agent_core::readiness   Task 'agentgateway' complete (7.722275ms), still awaiting 1 tasks
2026-02-06T21:19:22.419255Z     info    management::hyper_helpers       listener established    address=127.0.0.1:15000 component="admin"
2026-02-06T21:19:22.419344Z     info    management::hyper_helpers       listener established    address=[::]:15020 component="stats"
2026-02-06T21:19:22.419386Z     info    proxy::gateway  started bind    bind="bind/3000"
2026-02-06T21:19:22.419395Z     info    agent_core::readiness   Task 'state manager' complete (7.872532ms), marking server ready
2026-02-06T21:40:41.536353Z     info    request gateway=default/default listener=listener0 route=default/route0 endpoint=localhost:9999 src.addr=127.0.0.1:54966 http.method=POST http.host=supply-chain-agent.localhost http.path=/ http.version=HTTP/1.1 http.status=200 trace.id=3aef7d77d62861bad0066c70fc24a1db span.id=a4085bd01952f77f aauth.scheme=Jwks aauth.agent=http://backend.localhost:8000 protocol=a2a a2a.method=message/send duration=117ms aauth_scheme="Jwks" aauth_agent_identity="http://backend.localhost:8000" sig_key="sig1=(scheme=jwks id=\"http://backend.localhost:8000\" kid=\"backend-ephemeral-1\")"

We can see that agentgateway also verified the agent’s identity and added some of this metadata to the access logging. The fields aauth_scheme, aauth_agent_identity, sig_key are all added to the log output (see the last line). These fields can also be used for authorization policy in the gateway. We’ll see that in the last post of this series.

This flow from the UI (just clicking the button) doesn’t trigger the market-analysis-agent but if we type “perform market analysis” in the UI prompt text box it will. And since all of the components are configured for AAuth JWKS agent identity, you should see the supply-chain-agent call the market-analysis-agent in that flow. Check the logs to verify.

Summary: End-to-End Flow Diagram

The following diagram summarizes the complete Agent Identity with JWKS flow described above:

flowchart LR
    UI[UI] --> BE[Backend] --> AGW[Agentgateway] --> SCA[Supply-Chain Agent]
    SCA -.->|fetch JWKS to verify| BE

Key: Backend signs with JWKS → Agentgateway verifies → Supply-Chain Agent fetches Backend’s JWKS to verify identity.

In the next post, we’ll look at how the AAuth authorization flow works in this demo!

← Back to index