a2aproject / a2aproject/a2a-rs
a2acli: auth login — OAuth 2.1 device-code, client-credentials, and token storage
- Ngôn ngữ chính
- Rust
- Star
- 75
- Fork
- 19
- Merge trung bình
- 11 giờ 27 phút
- Pull request đã merge (30 ngày)
- 21
Mô tả
Part of #181 (Tier 2). **Phase 2 — surface not yet defined upstream.** Largest item in the tier.
## Summary
Three requirements, one command (§12.2):
| ID | Requirement |
| --- | --- |
| `A2ACLI_AUTH_005` | `auth login` — OAuth 2.1 device-code flow |
| `A2ACLI_AUTH_006` | `auth login` — OAuth 2.1 client-credentials flow |
| `A2ACLI_AUTH_007` | Secure token storage with automatic attachment on later calls |
`a2acli` has no `auth` command. Tier 1 credentials are caller-supplied only: `--bearer`, `--api-key`, `--svc-param` and their env equivalents (#176).
The device-code flow ([RFC 8628](https://www.rfc-editor.org/rfc/rfc8628)) is the one the spec calls out as *"designed for input-constrained clients such as a CLI"*.
## Why
Today the caller must obtain a token by some other means and pass it in. That is fine for CI with a service credential, but for a person at a terminal against an OAuth-protected agent there is no path at all.
## Tension with the agent-first principle, worth resolving explicitly
§6.1 requires **no interactive prompts** in the default path, and §8.3 requires the tool to persist *configuration, never session state*. A login command sits awkwardly against both:
- Device-code is interactive by nature — it prints a URL and a code and waits. It should therefore be confined to `auth login` and never triggered implicitly by another command, so `send` and `task get` stay unattended-safe.
- A stored access token is closer to session state than configuration. §8.3's prohibition targets `taskId`/`contextId` replay specifically, and `AUTH_007` explicitly requires storage, so storing a token is sanctioned — but the boundary should be stated in the implementation, not assumed.
## Surface to agree first
The Go CLI has no `auth` command, so there is **nothing to conform to**. Open questions for upstream:
- where the OAuth endpoints come from: the Agent Card's `securitySchemes` (A2A §4.5.2), or explicit flags?
- token storage location and mechanism: OS keychain versus a mode-`0600` file under `~/.config/a2a-cli/`. A keychain is more defensible but adds a platform-specific dependency and behaves badly in headless CI — exactly where a CLI runs.
- `auth logout` / `auth status`: neither is in §7.1's command table, but stored credentials with no way to inspect or clear them is a poor security posture.
- refresh-token handling and what happens when refresh fails mid-command.
## Scope (pending the above)
- [ ] Agree the surface upstream in `a2a-cli`; record the outcome here.
- [ ] `auth login` with the device-code flow: display the verification URI and user code, poll the token endpoint honoring the server's `interval` and `slow_down` per RFC 8628 §3.5, and respect `--timeout`.
- [ ] `auth login` with the client-credentials flow, for unattended use.
- [ ] Secure token storage, automatically attached to later calls, with explicit precedence against `--bearer`/`A2ACLI_BEARER` — an explicit flag must still win (§6.5).
- [ ] Warn if the token store is readable by other users, matching the existing `.env` warning from #177.
- [ ] Never log token material, including under `--debug` wire logging (`A2ACLI_AUTH_004`; see #183).
- [ ] `config show` reports whether a stored token is present and its source, redacted — never the value (the existing `(set, redacted)` form).
- [ ] Device-code must not be reachable from any non-`auth` command, so the default path stays non-interactive (§6.1).
- [ ] Tests: both flows against a fixture authorization server; `slow_down` honored; an expired token refreshed; `--bearer` overriding a stored token; no token material in `--debug` output.
## Requirements closed
`A2ACLI_AUTH_005`, `A2ACLI_AUTH_006`, `A2ACLI_AUTH_007` (§12.2)
Hướng dẫn đóng góp
Đánh giá
Issue này chưa được đánh giá.