Skip to content

Latest commit

 

History

History

README.md

Authentication Sample

This sample demonstrates how to implement Bearer Token authentication. It shows how to intercept requests, validate tokens, extract the user generated by the authentication and pass it to the context.

Key Components

  • index.ts: generates an AgentCard that requires Bearer authentication for incoming requests and starts the server with the authentication middleware and user builder.
  • authentication_middleware.ts: creates the middleware responsible for authentication. It uses Passport to support Bearer token authorization and populates the user parameter in the Request object with the plain extracted object.
  • user_builder.ts: defines the callback for the user extraction. It expects the user object (populated by the middleware) to contain specific values (email, userName, role) which are used to build the CustomUser object.
  • agent_executor.ts: creates a simple agent executor that checks the authentication status of the user object present in the context, which is expected to be an instance of CustomUser in case of successful authentication, and sends back the content.

Running

npm run agents:authentication-agent

The agent will start on http://localhost:41241.

Generating a test JWT

The middleware uses HS256 with the symmetric secret a2a-secret-for-authentication-sample (see authentication_middleware.ts:9). For a successful authentication the JWT payload MUST contain email, userName, and role.

You can generate a token with the jwt.io encoder, or locally:

node -e "
const crypto = require('node:crypto');
const header = Buffer.from(JSON.stringify({alg:'HS256',typ:'JWT'})).toString('base64url');
const payload = Buffer.from(JSON.stringify({userName:'alice',email:'alice@example.com',role:'admin'})).toString('base64url');
const sig = crypto.createHmac('sha256','a2a-secret-for-authentication-sample').update(header+'.'+payload).digest('base64url');
console.log(header+'.'+payload+'.'+sig);
"

Calling the agent

The A2A-Version header is required: when it is omitted the JSON-RPC binding falls back to 0.3, and the server (which advertises only A2A_PROTOCOL_VERSION, currently 1.0) rejects the request with VERSION_NOT_SUPPORTED.

curl -X POST http://localhost:41241/ \
  -H "Authorization: Bearer <jwt-token>" \
  -H "Content-Type: application/json" \
  -H "A2A-Version: 1.0" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "SendMessage",
    "params": {
      "message": {
        "messageId": "9229e770-767c-417b-a0b0-f0741243c589",
        "role": "ROLE_USER",
        "parts": [
          { "text": "Hello, who am I?", "mediaType": "text/plain" }
        ]
      }
    }
  }'

Expected response (successful authentication)

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "message": {
      "messageId": "<server-generated-uuid>",
      "contextId": "<server-generated-uuid>",
      "taskId": "<server-generated-uuid>",
      "role": "ROLE_AGENT",
      "parts": [
        {
          "text": "The request is coming from the authenticated user alice, with email alice@example.com and role admin.",
          "mediaType": "text/plain"
        }
      ],
      "metadata": {}
    }
  }
}

Expected response (no / invalid token)

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "message": {
      "messageId": "<server-generated-uuid>",
      "contextId": "<server-generated-uuid>",
      "taskId": "<server-generated-uuid>",
      "role": "ROLE_AGENT",
      "parts": [
        {
          "text": "The request is not coming from an authenticated user.",
          "mediaType": "text/plain"
        }
      ],
      "metadata": {}
    }
  }
}