A2A Authentication
This guide covers how to configure authentication for Agent-to-Agent (A2A) communication. There are two supported authentication methods:
- DataRobot API key — simple bearer token auth for DataRobot-hosted agents.
- Okta cross-application access (XAA) — two-step token exchange for federated Okta environments (hybrid RFC 8693 / RFC 7523 flow).
Both methods use the authenticated_a2a_client function group on the client
side and the cross_application_access server-side config to publish
requirements in the agent card.
Installation
# With Okta XAA support (adds okta-client-python)
pip install "datarobot-genai[dragent,langgraph,auth]>=0.15.40"
Replace langgraph with crewai or llamaindex depending on your framework.
Option 1: DataRobot API key authentication
Use this when calling a DataRobot-hosted A2A agent that accepts a standard DataRobot API token.
Environment variables
| Variable | Description |
|---|---|
DATAROBOT_API_TOKEN |
Your DataRobot API token. |
The token is loaded automatically — no need to put it in workflow.yaml.
workflow.yaml
general:
front_end:
_type: dragent_fastapi
step_adaptor:
mode: 'off'
a2a:
server:
name: My Agent
description: A helpful assistant agent.
function_groups:
remote_agent:
_type: authenticated_a2a_client
url: "https://app.datarobot.com/api/v2/deployments/<deployment-id>/directAccess/a2a/"
auth_provider: datarobot_auth
llms:
datarobot_llm:
_type: datarobot-llm-component
workflow:
_type: langgraph_agent
llm_name: datarobot_llm
description: My agent
tool_names:
- remote_agent
authentication:
datarobot_auth:
_type: datarobot_api_key
How it works
- On each A2A call, the
datarobot_api_keyauth provider injects theDATAROBOT_API_TOKENas anAuthorization: Bearer <token>header. - The remote agent's A2A endpoint validates the token against the DataRobot platform.
This is the simplest setup — no agent card extensions or multi-step flows involved.
Important:
datarobot_api_keyis the default authentication mechanism for DataRobot-hosted agents. However, when the remote agent card declares a specific mechanism (e.g. OAuth2 viacross_application_access), security-scheme negotiation validates and requires a matching auth provider on the client side. Useokta_cross_app_access(Option 2) for OAuth2-protected agents.
Option 2: Okta cross-application access (XAA)
Use this when calling an agent protected by Okta's federated identity model. The flow obtains a scoped access token through a two-step exchange.
Prerequisites
- An Okta organization with Cross-Application Access enabled.
- A registered AI agent principal in Okta with a private key pair.
- The
authextra installed (pip install "datarobot-genai[auth]").
Environment variables
| Variable | Description |
|---|---|
IDP_AGENT_ID |
Okta AI agent principal ID (used as iss/sub in JWT client assertions). |
IDP_AGENT_PRIVATE_KEY_JWK |
Base64-encoded or raw-JSON private JWK (signs JWT client assertions). |
Both are loaded automatically from env vars, .env, DataRobot Runtime
Parameters, or file_secrets.
Full workflow.yaml example
general:
front_end:
_type: dragent_fastapi
step_adaptor:
mode: 'off'
a2a:
server:
name: Blog Content Writer
description: >-
An AI content writing agent that researches and writes
well-structured blog posts.
skills:
- id: write_blog
name: Write Blog Post
description: Researches and writes a blog post on the given topic.
tags: []
examples:
- Write a blog post about the future of AI in healthcare
- Create an article about sustainable energy trends
# Server-side: advertise XAA requirements in the agent card
cross_application_access:
# Step 1: Token exchange to fetch the ID-JAG (RFC 8693)
token_exchange:
trusted_issuer: "https://your-org.okta.com"
audience: "https://your-org.okta.com/oauth2/ausXXXXXXXXXXXXXXX"
# Step 2: Execute the Final Grant (RFC 7523)
token_request:
token_url: "https://your-org.okta.com/oauth2/ausXXXXXXXXXXXXXXX/v1/token"
audience: "https://app.datarobot.com/<org_id>/<agent_id>"
scopes:
- "blog:write"
# Optional: external identity and URL overrides published on the agent card
external:
id: "my-agent-id" # Emitted as urn:datarobot:agent:identity:external
url: "https://my-agent-id.example.com/a2a/" # Overrides the auto-generated card URL
# Client-side: call a remote XAA-protected agent
function_groups:
remote_agent:
_type: authenticated_a2a_client
url: "https://app.datarobot.com/api/v2/deployments/<deployment-id>/directAccess/a2a/"
auth_provider: okta_auth
llms:
datarobot_llm:
_type: datarobot-llm-component
workflow:
_type: langgraph_agent
llm_name: datarobot_llm
description: LangGraph planner/writer agent
tool_names:
- remote_agent
authentication:
okta_auth:
_type: okta_cross_app_access
# okta_token_header: "x-custom-header" # Optional: override default header name
How it works
The XAA flow operates in two steps:
-
Token Exchange (RFC 8693) — The caller's incoming Okta access token (from the
okta_token_header) is exchanged for an ID-JAG (Identity Assertion Authorization Grant) via the org-level Authorization Server (token_exchange.trusted_issuer). -
JWT Bearer Grant (RFC 7523) — The ID-JAG is exchanged for a scoped access token at the resource AS token endpoint (
token_request.token_url), granting access to the target agent with the requested scopes.
Both steps authenticate the client using the same method
(private key jwt), signing JWT client assertions
with the private key from IDP_AGENT_PRIVATE_KEY_JWK.
Server-side configuration reference: cross_application_access
These fields are declared in the serving agent's workflow.yaml and published
on the A2A agent card.
| Field | Required | Default | Purpose |
|---|---|---|---|
token_exchange.trusted_issuer |
Yes | — | Org-level Authorization Server issuer URL. |
token_exchange.audience |
Yes | — | Resource AS base URL (where ID-JAG is fetched from). |
token_request.token_url |
Yes | — | Token endpoint of the resource AS. |
token_request.audience |
Yes | — | Final resource identifier for the agent. |
token_request.scopes |
No | ["read_data"] |
Scopes the caller must request. |
Note:
grant_typeURNs are injected automatically by the generator — do not set them inworkflow.yaml. The agent card will always containurn:ietf:params:oauth:grant-type:token-exchange(Step 1) andurn:ietf:params:oauth:grant-type:jwt-bearer(Step 2).
Server-side configuration reference: external
Optional fields under general.frontend.a2a.external that control additional
identity metadata and the agent card URL.
| Field | Purpose |
|---|---|
external.id |
Catalog discovery identifier. Emitted as the urn:datarobot:agent:identity:external extension on the agent card. |
external.url |
Overrides the auto-generated agent card endpoint URL. Used as-is — no normalization is applied. |
Client-side configuration reference: okta_cross_app_access
These fields configure how the calling agent authenticates when invoking a remote XAA-protected agent.
| Field | Default | Purpose |
|---|---|---|
okta_token_header |
x-datarobot-external-access-token |
Incoming request header carrying the caller's Okta access token. |
principal_id |
IDP_AGENT_ID env var |
Okta AI agent principal ID. |
private_jwk |
IDP_AGENT_PRIVATE_KEY_JWK env var |
Base64-encoded or raw-JSON private JWK. |
Agent card mapping
The cross_application_access configuration is split across two parts of the
published A2A agent card:
securitySchemes.oauth2.flows.clientCredentials— Standard OpenAPI fields:tokenUrl(fromtoken_request.token_url) andscopes(fromtoken_request.scopes).capabilities.extensions[](URI:urn:ietf:params:oauth:grant-type:jwt-bearer) — Non-standard XAA parameters for SDK consumption:token_endpoint_auth_method,token_exchange.*(including hardcodedgrant_typeandrequested_token_typeURNs), andtoken_request.audience.
In addition, the agent card may include up to two identity extensions:
urn:datarobot:agent:identity:internal— Emitted automatically in deployed environments (whenMLOPS_DEPLOYMENT_IDis set). Carriesdeployment_idfor internal DataRobot routing. Not emitted in local development.urn:datarobot:agent:identity:external— Emitted whenexternal.idis set inworkflow.yaml. Carries the developer-supplied catalog identifier.
This separation follows the A2A spec convention: standard OAuth fields belong
in securitySchemes, while flow-specific parameters go in
capabilities.extensions.
Troubleshooting
Common issues
| Symptom | Cause | Fix |
|---|---|---|
Authorization header missing on A2A RPC calls |
The remote agent card declares securitySchemes but the client uses datarobot_api_key. When securitySchemes are present, the A2ACredentialService performs OAuth2 security-scheme negotiation and drops incompatible credentials. |
Switch to an OAuth2-compatible auth provider (e.g. okta_cross_app_access) that matches the security scheme advertised by the remote agent card. |
RuntimeError: Header 'x-datarobot-external-access-token' not found |
The incoming request doesn't carry the Okta token. | Ensure the upstream caller forwards the Okta access token in the expected header. |
ValueError: principal_id is required |
IDP_AGENT_ID env var not set. |
Set IDP_AGENT_ID in your environment or Runtime Parameters. |
ValueError: Could not parse private_jwk |
IDP_AGENT_PRIVATE_KEY_JWK is neither valid base64-encoded JSON nor raw JSON. |
Verify your JWK — try echo $IDP_AGENT_PRIVATE_KEY_JWK | base64 -d | python -m json.tool. |
ValueError: Agent card ... missing required fields |
Remote agent card doesn't have the XAA extension. | Verify the remote agent has cross_application_access configured. |
RuntimeError: Failed to fetch agent card |
Network/auth issue reaching the agent card URL. | Check the url in your function_groups config and network connectivity. |