Authdog

OpenAI Agents SDK

Last updated Oct 20, 2026
View as Markdown

The OpenAI Agents SDK tool is the caller. It reads an access token from run context and sends Authorization: Bearer to your API. require_auth on that API is the gate. The tool does not decide who the caller is, and it does not invent a subject.

Sample: openai-agents/python. Python 3.10+. Package openai-agents==0.24.0 (import agents).

Hold the token in context

@dataclass(frozen=True)
class Caller:
    token: str

async def fetch_subject(token: str, client: httpx.AsyncClient, url: str) -> str:
    headers: dict[str, str] = {}
    if token:
        headers["Authorization"] = f"Bearer {token}"
    response = await client.get(url, headers=headers)
    response.raise_for_status()
    return str(response.json()["subject"])

def build_agent(client: httpx.AsyncClient, url: str) -> Agent:
    @function_tool
    async def whoami(ctx: RunContextWrapper[Caller]) -> str:
        """Return the Authdog subject for this caller. Do not invent one."""
        return await fetch_subject(ctx.context.token, client, url)

    return Agent(
        name="authdog-caller",
        instructions="Call whoami. Never invent a subject.",
        tools=[whoami],
    )

Caller.token is the access token from the client-credentials exchange. The client secret stays in the process that minted it. The model sees the tool result, which is the subject string the API returned.

Run it by passing context=Caller(token=...) to Runner.run. OPENAI_API_KEY is required for that run. Proving the tool attaches the header does not require a model: call fetch_subject directly.

The route the tool calls

authdog = Authdog(public_key=public_key)

@app.get("/whoami")
async def whoami(user=Depends(authdog.require_auth)):
    return {"subject": subject_of(user)}

Authdog comes from authdog.fastapi. A missing or rejected token is 401 from this route, including when the tool sends an empty header because Caller.token is empty.

To act for a person, keep this route and this tool. Put an RFC 8693 token in Caller.token so sub stays the user and act names the agent. The samples mint a client_credentials token whose subject is the agent.

What you should see

  1. GET /whoami with no token returns 401.
  2. The same call with a bearer userinfo accepts returns {"subject": "<id>"}.
  3. Runner.run(..., context=Caller(token=os.environ["AUTHDOG_ACCESS_TOKEN"])) with instructions to call whoami prints that subject as the model output.

pytest covers the route and fetch_subject. It does not call Runner.

Configure

Variable Process Purpose
PK_AUTHDOG API Public key for require_auth.
WHOAMI_URL Agent Absolute URL of GET /whoami.
AUTHDOG_ACCESS_TOKEN Agent Bearer to place in Caller.
AUTHDOG_CLIENT_ID / AUTHDOG_CLIENT_SECRET Minting process Only if you exchange a token yourself.
OPENAI_API_KEY Agent Only for Runner.run.
uvicorn tool_api:create_app --factory --port 8000
Read To learn how to
LangGraph Protect the workflow the tool is calling
Agent delegation Exchange a user token when the agent acts for a person
Agents quickstart Register the client that mints AUTHDOG_ACCESS_TOKEN

Learn more