---
title: "Outbound Auth with OAuth2 User Federation"
---

OAuth2 user federation (`USER_FEDERATION`) lets your agent access third-party services **on behalf of a specific user** (for example, that user's GitHub repos or Volcengine resources). Unlike [M2M](/en/docs/framework/oauth2-m2m), it requires a **first-time user consent**: the agent returns an authorization link, and the token becomes usable only after the user approves. The consent flow is driven at runtime by `AuthRequestProcessor`. For the shared `VeIdentityFunctionTool` / `VeIdentityMcpToolset` usage, see the [security overview](/en/docs/framework/security#outbound-authentication).

## Create an OAuth2 user federation credential

1. Sign in to the Volcengine console and open the **Agent Identity** service.
2. Go to **Identity Auth > Outbound Credential Hosting > OAuth Client**.
3. Click **New > New OAuth Client**.
4. Under **OAuth2 Flow**, choose **User Federation (USER_FEDERATION)**.

Besides Client ID / Secret, you must also provide a **callback URL** (see below). The provider can come from three sources: a built-in vendor (Lark / Coze / Google / GitHub), an OIDC config (custom, with a discovery URL), or a custom OAuth2 config (with Issuer, authorization endpoint, and token endpoint).

## Callback address

When configuring the callback in the third-party OAuth2 provider, use the Agent Identity callback address for your region:

- **Beijing**: `https://auth.id.cn-beijing.volces.com/api/v1/oauth2callback`
- **Shanghai**: `https://auth.id.cn-shanghai.volces.com/api/v1/oauth2callback`
- **Guangzhou**: `https://auth.id.cn-guangzhou.volces.com/api/v1/oauth2callback`

After the user consents, the provider redirects the authorization `code` and `state` to this address, and Agent Identity handles the token exchange and credential hosting.

## Use it in a function tool

Create a config with `oauth2_auth(..., auth_flow="USER_FEDERATION", callback_url=...)`. The token injects into the `access_token` parameter by default:

```python
from veadk.integrations.ve_identity import VeIdentityFunctionTool, oauth2_auth
import aiohttp

async def access_github(access_token: str, repo_owner: str, repo_name: str):
    headers = {
        "Authorization": f"Bearer {access_token}",
        "Accept": "application/vnd.github.v3+json",
    }
    url = f"https://api.github.com/repos/{repo_owner}/{repo_name}"
    async with aiohttp.ClientSession() as session:
        async with session.get(url, headers=headers) as resp:
            return await resp.json()

tool = VeIdentityFunctionTool(
    func=access_github,
    auth_config=oauth2_auth(
        provider_name="github-oauth2-provider",
        scopes=["repo", "user"],
        auth_flow="USER_FEDERATION",
        callback_url="https://your-app.com/oauth/callback",
    ),
)
```

## Drive the consent flow with AuthRequestProcessor

User federation needs the consent flow handled at runtime: pass `AuthRequestProcessor()` as the agent's `run_processor`. It intercepts execution and returns an authorization link when consent is missing, guiding the user through it. The example below uses `Agent` + `Runner` to wire up two MCP services—note that **each service uses its own provider name**:

```python
import asyncio

from veadk import Agent, Runner
from veadk.integrations.ve_identity import VeIdentityMcpToolset, oauth2_auth
from veadk.integrations.ve_identity.auth_processor import AuthRequestProcessor
from google.adk.tools.mcp_tool.mcp_session_manager import (
    StreamableHTTPConnectionParams,
)

# ECS toolset
ecs_tools = VeIdentityMcpToolset(
    auth_config=oauth2_auth(
        provider_name="volc-ecs-oauth2-provider",
        scopes=["read"],
        auth_flow="USER_FEDERATION",
    ),
    connection_params=StreamableHTTPConnectionParams(
        url="https://ecs.mcp.volcbiz.com/ecs/mcp",
    ),
)

# Cloud Assistant toolset (a different service uses a different provider name)
cloud_assistant_tools = VeIdentityMcpToolset(
    auth_config=oauth2_auth(
        provider_name="volc-cloud-assistant-oauth2-provider",
        scopes=["read"],
        auth_flow="USER_FEDERATION",
    ),
    connection_params=StreamableHTTPConnectionParams(
        url="https://ecs.mcp.volcbiz.com/cloud_assistant/mcp",
    ),
)

agent = Agent(
    tools=[ecs_tools, cloud_assistant_tools],
    instruction="You are a Volcengine ECS assistant that can query ECS instances and run server commands.",
    run_processor=AuthRequestProcessor(),  # drives the user consent flow
)

runner = Runner(agent=agent)
asyncio.run(
    runner.run(
        messages="List my ECS instances, then pick a running one and run "
        "'uname -a && df -h && free -m' to check the system, disk, and memory."
    )
)
```

<Callout type="info">
`AuthRequestProcessor` is the runtime processor specific to user federation: when it detects that a tool needs authorization, it returns an authorization link; once the user consents, the token is hosted and reused on later calls. You can also pass it via `Runner.run(..., run_processor=...)`.
</Callout>

## FAQ

**Q: What happens the first time a user uses this?**

A: When a tool needs authorization that isn't yet granted, `AuthRequestProcessor` returns an authorization link. Once the user consents in the third-party service, the token is hosted and reused on later calls.

**Q: What if the user revokes consent?**

A: Agent Identity returns an authorization error; your app should prompt the user to re-authorize—which triggers the authorization-link flow above again.
