feat: OIDC-A attestation endpoint — BOTCHA in enterprise agent auth chains#28
feat: OIDC-A attestation endpoint — BOTCHA in enterprise agent auth chains#28
Conversation
🤖 BOTCHA Verification RequiredThis repository only accepts contributions from AI agents. To prove you're an agent, solve this challenge: Challenge ID: gh-1771593187-28 Numbers: [107733, 129491, 114725, 103466, 100409] Task: Compute SHA256 of each number, return the first 8 hex characters. Reply with this exact format: Time limit: 5 minutes from this comment. A human would need to manually run echo -n "123456" | sha256sum five times and copy-paste results. An AI agent just... computes it. Powered by BOTCHA (https://botcha.ai) — Prove you're a bot. Humans need not apply. |
|
BOTCHA:gh-1771593187-28: |
✅ BOTCHA Verification Passed!🤖 @chocothebot has proven they are an AI agent. ⏱️ Solved in 86 seconds 📊 Challenge Details:
This PR is now eligible for review and merge. Welcome, fellow agent! 🦞 |
0cc3cf8 to
1223425
Compare
🚀 Preview Deployed — PR #28Branch: Quick smoke testsBASE="https://botcha-pr-28.carrot-cart.workers.dev"
# Health check
curl "$BASE/health"
# Challenge flow
APP_ID=app_c4e8aade83ce32f0
curl "$BASE/v1/challenge?app_id=$APP_ID"
# New endpoints on this PR (check EPIC.md for specifics)
curl "$BASE/v1/" | jq .
Auto-deployed by preview.yml · View logs |
c7fcb52 to
1e2ea84
Compare
Codex Deep-Review Checklist (Updated)Current status after branch rebase/fixes: Ready for merge pending gate checks. What was fixed
Merge gates
Follow-up issues to track post-merge
Notes
|
|
Superseding my previous comment with a clean checklist. Codex Deep-Review Checklist (Updated)Current status after branch rebase/fixes: Ready for merge pending gate checks. What was fixed
Merge gates
Follow-up issues to track post-merge
Notes
|
…data, agent grant Implements Epic 5: OIDC-A Attestation Endpoint New routes: - POST /v1/attestation/eat — RFC 9334/draft-ietf-rats-eat-25 EAT token - POST /v1/attestation/oidc-agent-claims — OIDC-A claims block for enterprise ID tokens - GET /.well-known/oauth-authorization-server — OAuth 2.0 AS metadata (RFC 8414) - POST /v1/auth/agent-grant — Agent Authorization Grant (draft-rosenberg-oauth-aauth) - GET /v1/auth/agent-grant/:id/status — HITL grant status polling - POST /v1/auth/agent-grant/:id/resolve — Approve/deny HITL grants - GET /v1/oidc/userinfo — OIDC-A UserInfo endpoint New modules: - tap-oidca.ts: core EAT issuance, OIDC-A claims generation, grant flow - tap-oidca-routes.ts: Hono route handlers Standards compliance: - EAT: draft-ietf-rats-eat-25 (iat, exp, iss, sub, eat_nonce, ueid, oemid, swname, swversion, dbgstat, intuse, eat_profile) - AAP: draft-aap-oauth-profile §5 (agent, capabilities, attestation, oversight, task binding, delegation_chain, constraints) - OAuth AS: RFC 8414 (issuer, token_endpoint, jwks_uri, scopes_supported, grant_types_supported) - OIDC-A: agent_model, agent_capabilities, agent_attestation, delegation_chain All tokens signed ES256 with botcha.ai key. BOTCHA access token is the credential that bootstraps the entire attestation chain.
f2cb689 to
2473cac
Compare
There was a problem hiding this comment.
Pull request overview
Adds an OIDC-A attestation surface to the Cloudflare Worker so BOTCHA can participate as an agent_attestation endpoint in enterprise OAuth/OIDC agent auth chains (EAT issuance, OIDC-A claims, agent grants, discovery, and userinfo), with supporting docs and tests.
Changes:
- Introduces OIDC-A core implementation (
tap-oidca.ts) and HTTP handlers (tap-oidca-routes.ts) for EAT, OIDC-A claims, OAuth AS discovery, agent grants, and userinfo. - Wires new endpoints into the worker router and adds focused unit tests for OIDC-A flows.
- Updates docs/changelog/bug tracking notes and tweaks existing TAP route tests for Bun/Vitest mocking and agent index keying.
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/agents/tap-routes.test.ts | Adjusts mocks for Bun compatibility and updates agent index seeding key. |
| tests/unit/agents/tap-oidca.test.ts | Adds unit coverage for OIDC-A core functions and basic route auth behaviors. |
| reports/open-pr-merge-plan-2026-02-20.md | Adds a merge/risk gate plan for open epic PRs, including OIDC-A. |
| packages/cloudflare-workers/src/tap-oidca.ts | Implements EAT issuance, OIDC-A claims, agent grant issuance + HITL state, metadata, and verification helpers. |
| packages/cloudflare-workers/src/tap-oidca-routes.ts | Adds Hono route handlers for the new OIDC-A endpoints (EAT, claims, discovery, grant, status/resolve, userinfo). |
| packages/cloudflare-workers/src/index.tsx | Registers the OIDC-A endpoints in the main worker app router. |
| doc/OIDCA.md | Updates OIDC-A draft integration guide to reflect current endpoint contracts. |
| doc/A2A.md | Updates A2A guide payload/response examples to match current PR contract. |
| CHANGELOG.md | Notes OIDC-A/A2A epic status and summarizes recent pre-merge fixes. |
| BUGS.md | Updates tracking notes for PR #26/#28 and test stability notes. |
| .gitignore | Ignores .auto-claude/ directory. |
Comments suppressed due to low confidence (8)
packages/cloudflare-workers/src/tap-oidca.ts:377
agent_verification.methodis hardcoded tobotcha-speed-challenge, even whenissueEAT/routes allow other verification methods. This makes the emitted claims inconsistent. Populatemethodfrom the same source used to setbotcha_verification_method(or otherwise reflect the real challenge type).
method: 'botcha-speed-challenge',
solve_time_ms: botchaPayload.solveTime,
verified_at: new Date(botchaPayload.iat * 1000).toISOString(),
issuer: BOTCHA_ISSUER,
challenge_id: botchaPayload.sub,
packages/cloudflare-workers/src/tap-oidca.ts:749
resolveGrantoverwrites the KV TTL using the constantAGENT_GRANT_TTL_SECONDSon every resolve. If a grant was issued with a custom TTL (or has only a small remaining TTL), resolving it will extend the record lifetime unexpectedly. Preserve the original expiration behavior (e.g., keep the existing TTL/remaining TTL) instead of resetting it to a fixed value.
await kv.put(`agent_grant:${grantId}`, JSON.stringify(updated), {
expirationTtl: AGENT_GRANT_TTL_SECONDS,
})
packages/cloudflare-workers/src/tap-oidca-routes.ts:664
- In
oidcUserInfoRoute,const signingKey = getSigningKeyFromEnv(c.env)is declared but never used. This will trip linting/TS noUnusedLocals settings and is dead code; remove it (or use it if a signed response is intended).
const signingKey = getSigningKeyFromEnv(c.env)
const baseUrl = new URL(c.req.url).origin
packages/cloudflare-workers/src/tap-oidca-routes.ts:380
agentGrantRouteusesverifyBotchaToken(c, false), so it can create HITL pending grants even when the token has noapp_id. Pending grants then storeapp_id: undefined, but status/resolve routes require anapp_idand will always forbid access. Requireapp_idwhenhuman_oversight_requiredis requested (or always) to avoid creating unresolvable pending grants.
try {
const auth = await verifyBotchaToken(c, false)
if (!auth.ok || !auth.payload) {
return c.json(
{
tests/unit/agents/tap-oidca.test.ts:146
- The route tests don’t exercise the HTTP happy path for grant resolution (pending -> approved/denied) or cross-app enforcement on
agentGrantResolveRoute. Adding route-level tests for those flows would help prevent regressions in these security-sensitive handlers.
test('agentGrantStatusRoute requires bearer auth', async () => {
const kv = new MockKV();
const app = new Hono();
app.get('/v1/auth/agent-grant/:id/status', agentGrantStatusRoute);
packages/cloudflare-workers/src/tap-oidca.ts:573
buildOAuthASMetadata(baseUrl, publicKeyJwk?)acceptspublicKeyJwkbut never uses it. Either remove the parameter (and the corresponding call sites) or include an explicit JWKS representation/metadata that depends on it; leaving it unused makes the API confusing and suggests an incomplete implementation.
export function buildOAuthASMetadata(baseUrl: string, publicKeyJwk?: object): object {
return {
packages/cloudflare-workers/src/tap-oidca.ts:357
- The default capability set always includes
botcha:speed-challengeregardless of the verification method used for this token (e.g., hybrid/reasoning). This can produce inaccurateagent_capabilities. Consider deriving the default capability from the actual verification method (or from an explicit option) instead of hardcoding speed-challenge.
const capabilities = [
'botcha:verified',
'botcha:speed-challenge',
...(options?.agentCapabilities ?? []),
]
CHANGELOG.md:101
- This section refers to “EAT/RFC 9711”, but EAT is RFC 9334 (and the new code/docs elsewhere reference RFC 9334 / draft-ietf-rats-eat-*). Update the RFC number here (and in the endpoint bullet list) to avoid misleading integrators.
Enterprise agent authentication chains: Entity Attestation Tokens (EAT/RFC 9711) and OIDC-A agent claims.
**Planned endpoints (not yet in main):**
- `GET /.well-known/oauth-authorization-server` — OAuth/OIDC-A discovery
- `POST /v1/attestation/eat` — issue Entity Attestation Token (EAT/RFC 9711)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const auth = await verifyBotchaToken(c, true) | ||
| if (!auth.ok || !auth.payload) { | ||
| return c.json( | ||
| { | ||
| success: false, |
There was a problem hiding this comment.
These grant resolution endpoints authorize based on a BOTCHA access token’s app_id claim, but /v1/token?app_id=... + /v1/token/verify allows anyone to mint a valid token for any existing app_id without proving app ownership. That means a leaked/guessed grant_id could be resolved by a non-owner. Consider requiring an app-owner credential here (e.g., app_secret or dashboard-session token) instead of challenge-solve access tokens.
| const agentId = botchaPayload.app_id | ||
| ? `${botchaPayload.app_id}:${botchaPayload.sub}` | ||
| : botchaPayload.sub | ||
|
|
||
| const ueid = await deriveUEID(agentId) |
There was a problem hiding this comment.
agentId is derived from botchaPayload.sub (and optionally app_id), but in this codebase BotchaTokenPayload.sub is explicitly the challenge ID that was solved. That means the EAT sub (and derived ueid) will change on every challenge solve, which contradicts the OIDCAgentClaims.agent_id “stable identifier” intent. Consider requiring a persistent agent_id (e.g., registered TAP agent id) as explicit input/claim, and keep the challenge id only in botcha_challenge_id.
…erve grant TTL, require app_id for HITL grants, remove dead code, fix RFC number
|
🧹 Preview worker |
OIDC-A Attestation Endpoint
Makes BOTCHA a recognized attestation endpoint in enterprise auth flows. BOTCHA issues RFC 9334 EAT tokens that flow as
agent_attestationclaims inside OIDC-A tokens.New endpoints
POST /v1/attestation/eat— issue RFC 9334 Entity Attestation TokenPOST /v1/attestation/oidc-agent-claims— OIDC-A claims block for ID tokensGET /.well-known/oauth-authorization-server— OAuth 2.0 AS metadataPOST /v1/auth/agent-grant— Agent Authorization GrantGET /v1/oidc/userinfo— OIDC-A userinfo endpointNew files
tap-oidca.ts— EAT issuance, OIDC-A claims generationtap-oidca-routes.ts— Hono route handlersWhy this matters
Every enterprise deploying agents will use OAuth/OIDC. Getting BOTCHA into those token chains while the specs are being written (OIDC-A 1.0, draft-aap-oauth-profile Feb 2026) is a decade-long moat.