a2aproject / a2aproject/a2a-rs

a2acli: auth login — OAuth 2.1 device-code, client-credentials, and token storage

未關閉
#188 0 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視
enhancement
主要語言
Rust
星號
75
分支
19
平均合併
11 小時 27 分鐘
30 天內合併 PR
21

描述

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)

貢獻指南

開啟貢獻指南

評估

這個 Issue 還沒有評估資料。

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。