Implement local agent JWT authentication for adapters
Add HS256 JWT-based authentication for local adapters (claude_local, codex_local) so agents authenticate automatically without manual API key configuration. The server mints short-lived JWTs per heartbeat run and injects them as PAPERCLIP_API_KEY. The auth middleware verifies JWTs alongside existing static API keys. Includes: CLI onboard/doctor JWT secret management, env command for deployment, config path resolution from ancestor directories, dotenv loading on server startup, event payload secret redaction, multi-status issue filtering, and adapter transcript parsing for thinking/user message kinds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
72
doc/plans/agent-authentication-implementation.md
Normal file
72
doc/plans/agent-authentication-implementation.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Agent Authentication — P0 Local Adapter JWT Implementation
|
||||
|
||||
## Scope
|
||||
|
||||
- In-scope adapters: `claude_local`, `codex_local`.
|
||||
- Goal: zero-configuration auth for local adapters while preserving static keys for all other call paths.
|
||||
- Out-of-scope for P0: rotation UX, per-device revocation list, and CLI onboarding.
|
||||
|
||||
## 1) Token format and config
|
||||
|
||||
- Use HS256 JWTs with claims:
|
||||
- `sub` (agent id)
|
||||
- `company_id`
|
||||
- `adapter_type`
|
||||
- `run_id`
|
||||
- `iat`
|
||||
- `exp`
|
||||
- optional `jti` (run token id)
|
||||
- New config/env settings:
|
||||
- `PAPERCLIP_AGENT_JWT_SECRET`
|
||||
- `PAPERCLIP_AGENT_JWT_TTL_SECONDS` (default: `172800`)
|
||||
- `PAPERCLIP_AGENT_JWT_ISSUER` (default: `paperclip`)
|
||||
- `PAPERCLIP_AGENT_JWT_AUDIENCE` (default: `paperclip-api`)
|
||||
|
||||
## 2) Dual authentication path in `actorMiddleware`
|
||||
|
||||
1. Keep the existing DB key lookup path unchanged (`agent_api_keys` hash lookup).
|
||||
2. If no DB key matches, add JWT verification in `server/src/middleware/auth.ts`.
|
||||
3. On JWT success:
|
||||
- set `req.actor = { type: "agent", agentId, companyId }`.
|
||||
- optionally guard against terminated agents.
|
||||
4. Continue board fallback for requests without valid authentication.
|
||||
|
||||
## 3) Opt-in adapter capability
|
||||
|
||||
1. Extend `ServerAdapterModule` (likely `packages/adapter-utils/src/types.ts`) with a capability flag:
|
||||
- `supportsLocalAgentJwt?: true`.
|
||||
2. Enable it on:
|
||||
- `server/src/adapters/registry.ts` for `claude_local` and `codex_local`.
|
||||
3. Keep `process`/`http` adapters unset for P0.
|
||||
4. In `server/src/services/heartbeat.ts`, when adapter supports JWT:
|
||||
- mint JWT per heartbeat run before execute.
|
||||
- include token in adapter execution context.
|
||||
|
||||
## 4) Local env injection behavior
|
||||
|
||||
1. In:
|
||||
- `packages/adapters/claude-local/src/server/execute.ts`
|
||||
- `packages/adapters/codex-local/src/server/execute.ts`
|
||||
|
||||
inject `PAPERCLIP_API_KEY` from context token.
|
||||
|
||||
- Preserve existing behavior for explicit user-defined env vars in `adapterConfig.env`:
|
||||
- if user already sets `PAPERCLIP_API_KEY`, do not overwrite it.
|
||||
- Continue injecting:
|
||||
- `PAPERCLIP_AGENT_ID`
|
||||
- `PAPERCLIP_COMPANY_ID`
|
||||
- `PAPERCLIP_API_URL`
|
||||
|
||||
## 5) Documentation updates
|
||||
|
||||
- Update operator-facing docs to remove manual key setup expectation for local adapters:
|
||||
- `skills/paperclip/SKILL.md`
|
||||
- `cli/src/commands/heartbeat-run.ts` output/help examples if they mention manual API key setup.
|
||||
|
||||
## 6) P0 acceptance criteria
|
||||
|
||||
- Local adapters authenticate without manual `PAPERCLIP_API_KEY` config.
|
||||
- Existing static keys (`agent_api_keys`) still work unchanged.
|
||||
- Auth remains company-scoped (`req.actor.companyId` used by existing checks).
|
||||
- JWT generation and verification errors are logged as non-leaking structured events.
|
||||
- Scope remains local-only (`claude_local`, `codex_local`) while adapter capability model is generic.
|
||||
Reference in New Issue
Block a user