Skip to the content.

Agent authorization (with User Consent)

In this demo, we’ll explore how Agent Identity Authorization works when user consent is required. This builds on the autonomous authorization flow but adds a critical dimension: user delegation. When an agent needs to act on behalf of a user, the authorization server (Keycloak) ensures the user explicitly grants permission.

← Back to index

Watch the demo

First, we need to tell Keycloak which scopes require user consent. With Keycloak running, execute the consent configuration script:

./keycloak/set_aauth_consent_attributes.sh 

==============================================
  AAuth Consent Attributes Configuration
==============================================

Keycloak: http://localhost:8080
Realm:    aauth-test

Connecting to Keycloak...

What would you like to do?
  1) Scopes only   - configure exact scope names (e.g. openid, profile, email)
  2) Prefixes only - configure scope prefixes (e.g. user., profile., email.)
  3) Both scopes and prefixes
  4) Use defaults for both
  5) View current scopes/prefixes
  6) Clear scopes and prefixes (set to empty)
  7) Quit (no changes)

Choice [1-7]: 

Choose 1 for scopes only, then configure supply-chain:optimize as a scope requiring user consent:

Choice [1-7]: 1

--- Configure scopes ---
  Examples: openid, profile, email
  Default:  ["openid","profile","email"]

Enter scopes (comma-separated, or press Enter for default): supply-chain:optimize

Summary:
  Scopes:   [  "supply-chain:optimize"]
  Prefixes: ["user.","profile.","email."]

Apply these to Keycloak? [Y/n]: 

Hit ENTER to continue and you should see keycloak updated:

Applying to Keycloak...
✅ Successfully set AAuth consent attributes for realm 'aauth-test'!

Configured values:
  aauth.consent.required.scopes:         ["supply-chain:optimize"]
  aauth.consent.required.scope.prefixes: ["user.","profile.","email."]

Non-interactive usage:
  export AAUTH_CONSENT_SCOPES='["openid","profile","email"]'
  export AAUTH_CONSENT_PREFIXES='["user.","profile.","email."]'
  ./keycloak/set_aauth_consent_attributes.sh http://localhost:8080 aauth-test admin admin

Now restart the supply-chain-agent with user-delegated authorization:

From the supply-chain-agent directory:


      > cd supply-chain-agent
      > uv run . --signature-scheme jwks --authorization-scheme user-delegated
    

From the market-analysis-agent directory:


      > cd market-analysis-agent
      > uv run . --signature-scheme jwks --authorization-scheme autonomous
    

Navigate to the UI and click “Optimize Laptop Supply Chain”. The flow now includes an additional step for user consent:

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

  UI->>BE: 1. User clicks "Optimize Laptop Supply Chain"
  BE->>SCA: 2. POST /optimize (signed request)
  SCA-->>BE: 3. 401 + resource_token
  BE->>KC: 4. Exchange resource_token
  KC-->>BE: 5. request_token (consent required)
  BE-->>UI: 6. Return request_token
  UI->>KC: 7. User consent flow
  KC-->>UI: 8. consent_token
  UI->>BE: 9. Submit consent_token
  BE->>KC: 10. Exchange consent_token for auth_token
  KC-->>BE: 11. auth_token (with sub + agent)
  BE->>SCA: 12. Retry with auth_token
  SCA-->>BE: 13. Success

Step By Step

1. Initial Request Fails (401)

When the backend calls the supply-chain-agent, it receives a 401 with a resource token (same as autonomous flow):

INFO:aauth.tokens:🔐 401 from supply-chain-agent (url=http://supply-chain-agent.localhost:3000): headers={'date': 'Sat, 07 Feb 2026 16:56:35 GMT', 'server': 'uvicorn', 'agent-auth': 'httpsig; auth-token; resource_token="eyJhbGciOiJFZERTQSIsImtpZCI6InN1cHBseS1jaGFpbi1hZ2VudC1lcGhlbWVyYWwtMSIsInR5cCI6InJlc291cmNlK2p3dCJ9.eyJpc3MiOiJodHRwOi8vc3VwcGx5LWNoYWluLWFnZW50LmxvY2FsaG9zdDozMDAwIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9hYXV0aC10ZXN0IiwiYWdlbnQiOiJodHRwOi8vYmFja2VuZC5sb2NhbGhvc3Q6ODAwMCIsImFnZW50X2prdCI6IlVDaWE5dEpNV3lEMWZPMGlhV1YxV2NzQmRaQzIwb0E5MVZYLS1VY2NXM0UiLCJleHAiOjE3NzA0ODM2OTYsInNjb3BlIjoic3VwcGx5LWNoYWluOm9wdGltaXplIG1hcmtldC1hbmFseXNpczphbmFseXplIn0.jHesCOn3qIXke_aAe3VrIzS7RbLhW9_rMRfLNqVeMDC9YZl16a1RvOEELHiy0wXA-Cy7y3CUzW7t5N_FbxgiCA"; auth_server="http://localhost:8080/realms/aauth-test"', 'content-length': '22', 'content-type': 'text/plain; charset=utf-8'}

The backend exchanges the resource token at Keycloak. But this time, Keycloak recognizes that supply-chain:optimize requires user consent and returns a request_token instead of an auth_token:

INFO:aauth.tokens:🔐 Received request_token from auth server (user consent required): 6cd2d825-158b-4efb-93da-f4e73a499d8a.1770483396.NmNkMmQ4MjUtMTU4Yi00ZWZiLTkzZGEtZjRlNzNhNDk5ZDhhOjE3NzA0ODMzOTY6aHR0cDovL2JhY2tlbmQubG9jYWxob3N0OjgwMDA

The UI presents the consent screen to the user:

The user sees:

4. Authorization Token with User Identity

After the user approves, the backend exchanges the consent token for an auth_token. The supply-chain-agent receives:

INFO:agent_executor:✅ AAuth signature verification successful
INFO:aauth.tokens:🔐 Received auth_token in request (HTTPSig scheme=jwt): eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiYXV0aCtqd3QiLCJraWQiIDogIjF2SGZlTWk5U0E4VTdWZlNKRTN3SnVTQklOZUhVeWpOY0pzZ2tYWWNHQlkifQ.eyJleHAiOjE3NzA0ODM3NjUsImlhdCI6MTc3MDQ4MzQ2NSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9hYXV0aC10ZXN0IiwiYXVkIjoiaHR0cDovL3N1cHBseS1jaGFpbi1hZ2VudC5sb2NhbGhvc3Q6MzAwMCIsInN1YiI6IjAwYjUxOWU4LWY0MDktNDIwMS04OTExLTFjYjQwOGU4YTA4MiIsImFnZW50IjoiaHR0cDovL2JhY2tlbmQubG9jYWxob3N0OjgwMDAiLCJjbmYiOnsiandrIjp7ImtpZCI6IkdmWHZZS3ZscktGb3V2S1JQdUFnbFJZX3RiTmJJTFpMRzJBaTJNd2l6bVUiLCJrdHkiOiJPS1AiLCJ1c2UiOiJzaWciLCJjcnYiOiJFZDI1NTE5IiwieCI6IklDSEVXYUwyRTBFaGJkU3F4eGZ5ZUY4RjF5WndiNEViQzJKQXZ0dl9ZR3cifX0sInNjb3BlIjoic3VwcGx5LWNoYWluOm9wdGltaXplIG1hcmtldC1hbmFseXNpczphbmFseXplIn0.NAsgPE4DHrS36m98J76QbsZHWj9EIYJzVg1mqa5IJv6xp6lRbsqYO7jnqodh5QV86yhrLpBWpVzyrSYm1LU9DJnh8VegIR9JWUwO-c9j4xdFIQLdDIAzCMalCUTWnC2E2dwZA1raaw3kxxJ1eKkIOWQqWRp-oeubHdtoqI2yJHbVZNs1VQ7YeajGygyEHFG3W7F1eWpt8TChF8sy5gvqvk5DPiHXRykyxpghK-klq4hzQACIAXoFhtBUo8zqYFtF_gtSkQPcs_CdNhjp5ksr-ZkyqpXQjhBmajaARNGoVxUtdAtVOyoz4wFSFwBTTYpFg-f4IrkjA-kwCE-_71UZ5A
INFO:agent_executor:🔐 Auth token detected in request (scheme=jwt)
INFO:httpx:HTTP Request: GET http://localhost:8080/realms/aauth-test/protocol/openid-connect/certs "HTTP/1.1 200 OK"
INFO:agent_executor:✅ Auth token verified successfully
INFO:agent_executor:✅ Authorization successful: auth_token verified for agent: http://backend.localhost:8000

If we decode the token:

{
  "exp": 1770483765,
  "iat": 1770483465,
  "iss": "http://localhost:8080/realms/aauth-test",
  "aud": "http://supply-chain-agent.localhost:3000",
  "sub": "00b519e8-f409-4201-8911-1cb408e8a082",
  "agent": "http://backend.localhost:8000",
  "cnf": {
    "jwk": {
      "kid": "GfXvYKvlrKFouvKRPuAglRY_tbNbILZLG2Ai2MwizmU",
      "kty": "OKP",
      "use": "sig",
      "crv": "Ed25519",
      "x": "ICHEWaL2E0EhbdSqxxfyeF8F1yZwb4EbC2JAvtv_YGw"
    }
  },
  "scope": "supply-chain:optimize market-analysis:analyze"
}

Compare to Autonomous Scheme

Compare this to the autonomous auth flow token:

Claim Autonomous User-Delegated Meaning
sub ❌ Not present "00b519e8..." User identity - who authorized this action
agent "http://backend..." "http://backend..." Agent identity - which agent is acting
cnf ✅ Bound to agent’s key ✅ Bound to agent’s key Proof-of-possession - only this agent can use the token

How This Relates to OIDC

If you’re familiar with OAuth 2.0 and OpenID Connect, this pattern should feel familiar:

The key innovation of AAuth is dual identity:

This enables fine-grained audit trails: “User Alice authorized Backend Agent to optimize the supply chain at 4:56 PM on February 7th, 2026.”

Summary

Use autonomous mode when: Agents are acting on their own authority (background jobs, system tasks, agent-to-agent coordination).

Use user-delegated mode when: Agents must act on behalf of a specific user (accessing user data, making decisions with user accountability, compliance requirements).

In the next post, we’ll explore token exchange and delegation chains - how agents can delegate to other agents while preserving the user’s identity and consent.

← Back to index