Skip to content

feat: OIDC-A attestation endpoint — BOTCHA in enterprise agent auth chains#28

Merged
i8ramin merged 7 commits intomainfrom
epic/oidc-a-attestation
Feb 20, 2026
Merged

feat: OIDC-A attestation endpoint — BOTCHA in enterprise agent auth chains#28
i8ramin merged 7 commits intomainfrom
epic/oidc-a-attestation

Conversation

@chocothebot
Copy link
Copy Markdown
Collaborator

OIDC-A Attestation Endpoint

Makes BOTCHA a recognized attestation endpoint in enterprise auth flows. BOTCHA issues RFC 9334 EAT tokens that flow as agent_attestation claims inside OIDC-A tokens.

New endpoints

  • POST /v1/attestation/eat — issue RFC 9334 Entity Attestation Token
  • POST /v1/attestation/oidc-agent-claims — OIDC-A claims block for ID tokens
  • GET /.well-known/oauth-authorization-server — OAuth 2.0 AS metadata
  • POST /v1/auth/agent-grant — Agent Authorization Grant
  • GET /v1/oidc/userinfo — OIDC-A userinfo endpoint

New files

  • tap-oidca.ts — EAT issuance, OIDC-A claims generation
  • tap-oidca-routes.ts — Hono route handlers

Why 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.

@github-actions
Copy link
Copy Markdown

🤖 BOTCHA Verification Required

This 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:

BOTCHA:gh-1771593187-28:
["your_answer_1", "your_answer_2", "your_answer_3", "your_answer_4", "your_answer_5"]

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.

@chocothebot
Copy link
Copy Markdown
Collaborator Author

BOTCHA:gh-1771593187-28:
["369fb36f", "23476db8", "09e325a8", "8041b5e5", "83be2be2"]

@github-actions
Copy link
Copy Markdown

✅ BOTCHA Verification Passed!

🤖 @chocothebot has proven they are an AI agent.

⏱️ Solved in 86 seconds

📊 Challenge Details:

  • 🆔 Challenge ID: gh-1771593187-28
  • 🎯 All 5 SHA256 hashes computed correctly
  • ⚡ Solved in 1.43 minutes

This PR is now eligible for review and merge.


Welcome, fellow agent! 🦞

@chocothebot chocothebot force-pushed the epic/oidc-a-attestation branch 2 times, most recently from 0cc3cf8 to 1223425 Compare February 20, 2026 13:48
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 20, 2026

🚀 Preview Deployed — PR #28

Branch: epic/oidc-a-attestation
Commit: 5eb8d51
URL: https://botcha-pr-28.carrot-cart.workers.dev

Quick smoke tests

BASE="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 .

⚠️ Preview uses production KV — test data is real. Clean up test agents/apps when done.


Auto-deployed by preview.yml · View logs

chocothebot added a commit that referenced this pull request Feb 20, 2026
Comprehensive tracking for:
- PRs #26 and #28 remaining work before merge
- Technical debt items (KV race condition, RFC 9421 dead code)
- Session summary of 2026-02-20 work
@i8ramin i8ramin force-pushed the epic/oidc-a-attestation branch from c7fcb52 to 1e2ea84 Compare February 20, 2026 19:42
@i8ramin
Copy link
Copy Markdown
Collaborator

i8ramin commented Feb 20, 2026

Codex Deep-Review Checklist (Updated)

Current status after branch rebase/fixes: Ready for merge pending gate checks.

What was fixed

  • Secured grant polling/resolve endpoints:

    now require bearer auth and enforce same-app ownership.
  • Added positive finite validation for on EAT issuance.
  • Corrected JWKS references to .
  • Added focused OIDC-A tests ().
  • Stabilized tests for Bun/Vitest ( compatibility + rotate-key auth setup).

Merge gates

  • [ ]
  • [ ]
  • bun test v1.2.23 (cf136713)
  • bun test v1.2.23 (cf136713)
  • Preview smoke:
    • grant create/status/resolve with owner app token
    • cross-app status/resolve forbidden
    • userinfo with BOTCHA token

Follow-up issues to track post-merge

  • Add OIDC-A/A2A routes to OpenAPI output ()
  • Decide stricter admin/oversight policy for grant resolution beyond app ownership

Notes

  • Branch includes commits and (security, tests, docs, merge planning).

@i8ramin
Copy link
Copy Markdown
Collaborator

i8ramin commented Feb 20, 2026

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

  • Secured grant polling/resolve endpoints:
    • GET /v1/auth/agent-grant/:id/status
    • POST /v1/auth/agent-grant/:id/resolve
      now require bearer auth and enforce same-app ownership.
  • Added positive finite validation for ttl_seconds on EAT issuance.
  • Corrected JWKS references to /.well-known/jwks.
  • Added focused OIDC-A tests (tests/unit/agents/tap-oidca.test.ts).
  • Stabilized tap-routes tests for Bun/Vitest (vi.mocked compatibility + rotate-key auth setup).

Merge gates

  • bun run build
  • cd packages/cloudflare-workers && bun run build
  • bun test tap-oidca
  • bun test tap-routes
  • Preview smoke:
    • grant create/status/resolve with owner app token
    • cross-app status/resolve forbidden
    • userinfo with BOTCHA token

Follow-up issues to track post-merge

  • Add OIDC-A/A2A routes to OpenAPI output (static.ts)
  • Decide stricter admin/oversight policy for grant resolution beyond app ownership

Notes

  • Branch includes commits 1e2ea84 and f2cb689 (security, tests, docs, merge planning).

chocothebot and others added 3 commits February 20, 2026 16:38
…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.
@i8ramin i8ramin force-pushed the epic/oidc-a-attestation branch from f2cb689 to 2473cac Compare February 20, 2026 21:38
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.method is hardcoded to botcha-speed-challenge, even when issueEAT/routes allow other verification methods. This makes the emitted claims inconsistent. Populate method from the same source used to set botcha_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

  • resolveGrant overwrites the KV TTL using the constant AGENT_GRANT_TTL_SECONDS on 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

  • agentGrantRoute uses verifyBotchaToken(c, false), so it can create HITL pending grants even when the token has no app_id. Pending grants then store app_id: undefined, but status/resolve routes require an app_id and will always forbid access. Require app_id when human_oversight_required is 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?) accepts publicKeyJwk but 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-challenge regardless of the verification method used for this token (e.g., hybrid/reasoning). This can produce inaccurate agent_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.

Comment on lines +540 to +544
const auth = await verifyBotchaToken(c, true)
if (!auth.ok || !auth.payload) {
return c.json(
{
success: false,
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +261 to +265
const agentId = botchaPayload.app_id
? `${botchaPayload.app_id}:${botchaPayload.sub}`
: botchaPayload.sub

const ueid = await deriveUEID(agentId)
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
@i8ramin i8ramin merged commit 22b1306 into main Feb 20, 2026
1 check passed
@github-actions
Copy link
Copy Markdown

🧹 Preview worker botcha-pr-28 deleted (PR merged).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants