Conversation
…es response formats
There was a problem hiding this comment.
This PR adds OAuth/PAT authentication, credential storage, URL-based board endpoint resolution, and Bearer propagation to board HTTP/WebSocket flows. The authentication behavior is largely isolated to the board-client path, but the endpoint migration changes the default contract used by every ostool board command.
Blocking issue:
- A newly created global config now resolves to
http://localhost/(HTTP port 80), whereas the existing client default and ostool-server default both use port 2999. Consequently a fresh install—or any board command before configuring a port—cannot reach the bundled/default server. Preserve:2999in the URL default or keepport = Some(2999)while retaining URL overrides, and add a resolution-level regression test.
Validation: helper self-tests passed and git diff --check passed. I could not run cargo fmt --check, clippy, or crate tests locally because this environment has no cargo binary. Both current GitHub Actions check (stable, x86_64-unknown-linux-gnu) runs succeeded; no CI failure is attributed to this PR.
Previous review comments and PR discussion: none were present. I found no materially overlapping authentication PR in the repository search. Remaining risk after the blocking fix is the unrun hardware/OAuth integration path; the current code changes real board, serial, and credential workflows.
Powered by gpt-5.6-terra
There was a problem hiding this comment.
This PR adds OAuth/PAT authentication and credential lifecycle handling, moves board endpoints to URL-based configuration, and propagates Bearer credentials through board HTTP and serial WebSocket requests. It addresses public authenticated board gateways, while retaining anonymous LAN mode; however, the global endpoint configuration is a shared contract for every existing ostool board command.
The current blocking issue is migration of existing global configuration, described inline. The base release writes [board] server_ip and port, but the new schema rejects that persisted format outright. This breaks upgrades before board commands—or the new login/status commands that load the same global config—can reach their endpoint.
Validation: both current GitHub Actions check (stable, x86_64-unknown-linux-gnu) runs succeeded, with no CI failure attributable to this PR. Locally, prepare-review, helper self-tests, and git diff --check origin/main...HEAD passed. The planned cargo fmt --check, package clippy with -D warnings, and package tests could not run because this environment has neither cargo nor rustc; no hardware/OAuth gateway integration was attempted.
Previous review feedback was considered: the earlier blocking default-port issue is resolved at this head (http://localhost:2999) and has a regression test. Its old inline comment is therefore resolved; no other actionable review or PR comments remain. A repository PR search found no overlapping authentication/client change (the other open PR is unrelated boot-readiness work).
Remaining risk after the requested migration is the unexercised real credential-store, OAuth, board, and serial-hardware paths. The configuration migration remains blocking.
Powered by gpt-5.6-terra
There was a problem hiding this comment.
This PR adds OAuth Device Authorization / PAT authentication, URL-based board endpoint configuration, credential storage across OS keyrings, and Bearer token propagation through all board HTTP and WebSocket flows. It is a well-structured, carefully reviewed change that addresses both issues from the two previous review rounds.
Previous review findings resolved
- Default port regression (review #4741140519):
DEFAULT_BOARD_SERVERis nowhttp://localhost:2999, matching the established ostool-server default. Thedefault_config_resolves_to_the_board_server_default_porttest verifies this. - Legacy
server_ipmigration (review #4741937731):BoardGlobalConfigInputacceptsserver_ipduring deserialization and rewrites it asserveron save.deny_unknown_fieldsapplies to the input struct, not the output struct. Theload_migrates_base_server_ip_config_and_save_writes_url_formattest covers the round-trip.
Validation
- Both GitHub Actions
check (stable, x86_64-unknown-linux-gnu)runs succeeded with no CI failure attributable to this PR. git diff --check origin/main...HEADpassed.prepare-reviewconfirmed the workspace HEAD matches314ed40cba70c8d570563db99d2127929fee6f66.- The container lacks
cargo, socargo fmt --check, clippy, and crate tests could not be run locally; CI already covers the fullcheckworkflow.
Review notes
- The PR modifies only the
ostoolcrate; no cross-crate breakage is expected. - Auth behaviour is gated behind
auth_modeand returnsNone(anonymous) when disabled, preserving the existing LAN workflow. - The
RefreshLockusesfs4file locks to serialise cross-process OAuth refresh; token rotation and invalidation-on-401 are handled correctly. - Legacy bare-IP configurations are auto-wrapped with
http://and IPv6 brackets, then migrated on next save. - The TUI preserves
auth_modebut does not expose an editable control; users should edit the config file or use the new CLI commands (ostool login/ostool auth status) for auth setup. resolve_auth_endpointforcesauth_mode = Requiredwhen--serveris explicit for login/logout—this is intentional: providing a server URL during authentication means you intend to authenticate against it.
Design & risk
The separation of auth from board::client is clean: TokenManager owns credential lifecycle, BoardServerClient consumes tokens through authorization_token() without knowing OAuth internals. The endpoint resolution cascade (CLI → project config → global config) follows existing patterns. No unwrap() or expect() calls exist in the new production paths beyond well-justified unreachable! after a runtime-checked variant.
Residual risk is the unexercised hardware/OAuth gateway integration path since no real board server or credential store was available for manual testing. The unit tests, CI, and structured error handling provide reasonable confidence.
Powered by deepseek-v4-pro
There was a problem hiding this comment.
This PR adds OAuth Device Authorization / PAT authentication, URL-based board endpoint configuration, credential storage across OS keyrings, and Bearer token propagation through all board HTTP and WebSocket flows.
Previous review findings resolved
Both blocking issues from prior review rounds are satisfactorily addressed at this head:
- Default port regression (review #4741140519):
DEFAULT_BOARD_SERVERis nowhttp://localhost:2999, matching the established ostool-server default. Thedefault_config_resolves_to_the_board_server_default_porttest verifies this. - Legacy
server_ipmigration (review #4741937731):BoardGlobalConfigInputacceptsserver_ipduring deserialization and rewrites it asserveron save.deny_unknown_fieldsapplies to the input struct only. Theload_migrates_base_server_ip_config_and_save_writes_url_formattest covers the round-trip.
Validation
- Both GitHub Actions
check (stable, x86_64-unknown-linux-gnu)runs succeeded with no CI failure attributable to this PR. git diff --check origin/main...HEADpassed.prepare-reviewconfirmed the workspace HEAD matches314ed40cba70c8d570563db99d2127929fee6f66.- The container lacks
cargo, socargo fmt --check, clippy, and crate tests could not be run locally; CI already covers the fullcheckworkflow.
Review notes
- The PR modifies only the
ostoolcrate; no cross-crate breakage is expected. - Auth behaviour is gated behind
auth_modeand returnsNone(anonymous) when disabled, preserving the existing LAN workflow. - The
RefreshLockusesfs4file locks to serialise cross-process OAuth refresh; token rotation and invalidation-on-401 are handled correctly. - Legacy bare-IP configurations are auto-wrapped with
http://and IPv6 brackets, then migrated on next save. - The TUI preserves
auth_modebut does not expose an editable control; users should edit the config file or use the new CLI commands for auth setup. resolve_auth_endpointforcesAuthMode::Requiredwhen--serveris explicit for login/logout—this is intentional: providing a server URL during authentication means you intend to authenticate against it, and HTTPS is appropriately enforced.- No
unwrap()orexpect()calls exist in the new production paths beyond well-justified checks (static API paths, post-null-check expects, etc.).
Design & risk
The separation of auth from board::client is clean: TokenManager owns credential lifecycle, BoardServerClient consumes tokens through authorization_token() without knowing OAuth internals. The endpoint resolution cascade (CLI → project config → global config) follows existing patterns. Cross-origin WebSocket Bearer protection is implemented in resolve_ws_url.
Residual risk is the unexercised hardware/OAuth gateway integration path since no real board server or credential store was available for manual testing. The unit tests, CI, and structured error handling provide reasonable confidence.
No overlapping PRs were found in repository search.
Powered by deepseek-v4-pro
There was a problem hiding this comment.
Summary
This PR adds OAuth 2.0 Device Authorization Grant and PAT authentication to the ostool board client, migrates board endpoint configuration from a bare server_ip + port pair to a complete URL (server, optional port override, auth_mode), and propagates Bearer credentials through every board HTTP and serial WebSocket request. New top-level commands login, logout, and auth status are added; credentials are stored in the OS keyring with a 0600 file fallback, and a cross-process refresh lock prevents concurrent refresh-token rotation races.
Previous review findings — both resolved at this head
- Default port regression (earlier
CHANGES_REQUESTED):DEFAULT_BOARD_SERVERis nowhttp://localhost:2999, matching the established ostool-server default. Thedefault_config_resolves_to_the_board_server_default_porttest guards this. - Legacy
server_ipmigration (earlierCHANGES_REQUESTED):BoardGlobalConfigInputaccepts the persistedserver_ipfield during deserialization and rewrites it asserveron save;deny_unknown_fieldsapplies only to the input struct, not the output struct.load_migrates_base_server_ip_config_and_save_writes_url_formatcovers the round-trip. The old inline comments are therefore resolved.
Validation
- Both GitHub Actions
check (stable, x86_64-unknown-linux-gnu)runs on314ed40succeeded (fmt, clippy, build, test, publish dry-run) — no CI failure is attributable to this PR. prepare-reviewconfirmed the workspace HEAD matches314ed40cba70c8d570563db99d2127929fee6f66;git diff --check origin/main...HEADpassed.- This container has no
cargo/rustc, so I could not re-run fmt/clippy/tests locally; CI already covers the fullcheckworkflow.
Review notes
- The change is confined to the
ostoolcrate; no cross-crate contract is broken. - Auth behaviour is gated behind
auth_mode;authorization_token()returnsNone(anonymous) when disabled, preserving the existing LAN workflow. - Security posture is sound: redirects are disabled on both the auth client and board client (
Policy::none()),no_proxy()is set,auth_mode = requiredenforces HTTPS, cross-origin absolute WebSocket URLs are rejected before Bearer credentials are attached, and the credential-file fallback is restricted to0600. - Token refresh acquires an
fs4advisory lock and re-reads storage so another process can satisfy the request, then rotates and overwrites credentials on success;invalid_grant/invalid_token/401clear local credentials while transient network errors preserve them for retry. - The single
unreachable!()in the refresh path is justified byAuthClient::refreshalways returningOAuthRefresh; no otherunwrap/expectexists in the new production paths. - README Chinese and English versions are consistent, as required by AGENTS.md.
- TUI preserves
auth_modebut does not expose an editable control; users configure auth via the config file or the new CLI commands, which is acceptable for this scope.
Risk
Residual risk is the unexercised real credential-store / OAuth-gateway / board-hardware integration path — no live board server or keyring backend was available for manual testing. The unit tests, CI, and structured error handling provide reasonable confidence for merge.
Powered by glm-5.2
ostool登录与令牌管理变更说明变更内容与边界
本 PR 为
ostool增加登录、凭据保存、Token 刷新和注销能力,并将 board 服务地址统一为 URL 配置。ostool-server;现有匿名 board API 保持可用。配置与命令行
局域网仍使用匿名访问,但配置格式统一为 URL 型
server:公网认证网关使用完整 HTTPS 地址:
规则:
server必须为包含http://或https://的完整 URL;port为可选覆盖项;未填写时保留 URL 的显式端口或使用协议默认端口;auth_mode = "required"时必须使用 HTTPS;.board.toml可设置server、port、auth_mode,并由命令行参数覆盖。dev,原有的server_ip被替换为 URL 型server,原来必填的port变为可选覆盖项;命令行--server也必须使用完整 URL。新增命令:
客户端调用的认证网关接口
采用 OAuth 2.0 Device Authorization Grant,全部请求为
application/x-www-form-urlencoded。POST/oauth/device/codeclient_id=ostool-cli、scope=board:operate offline_accessPOST/oauth/tokengrant_type=urn:ietf:params:oauth:grant-type:device_code、device_code、client_idPOST/oauth/tokengrant_type=refresh_token、refresh_token、client_idPOST/oauth/revoketoken、token_type_hint=refresh_token、client_idDevice Code 响应至少应包含:
{ "device_code": "...", "user_code": "ABCD-EFGH", "verification_uri": "https://board.example.com/activate", "expires_in": 600, "interval": 5 }Token 响应应包含:
{ "access_token": "...", "refresh_token": "...", "token_type": "Bearer", "expires_in": 900 }scope为可选字段。服务端应在刷新时返回新的 Refresh Token,以支持 Token rotation。token_type必须为Bearer,expires_in必须为正数。轮询期间,认证网关应按 OAuth 约定以非 2xx 响应返回
authorization_pending或slow_down。客户端分别继续等待或将轮询间隔增加 5 秒;成功的 2xx 响应必须包含完整的 Token 字段。凭据与生命周期
凭据按认证网关 URL 隔离保存,优先使用系统 credential store:
系统凭据库不可用时,回退到
$XDG_CONFIG_HOME/ostool/hosts.json(或~/.config/ostool/hosts.json),并限制文件权限为0600。保存的敏感数据仅包括 OAuth Refresh Token、缓存的 Access Token 或 PAT。用户密码不落盘。
Token 使用策略:
auth_mode = "disabled":不读取、不发送 Token;OSTOOL_BOARD_ACCESS_TOKEN:只用于当前进程,不保存、不刷新;多个
ostool进程共用 Refresh Token 时,使用$XDG_CACHE_HOME/ostool/auth-locks/下的 URL 哈希锁文件避免并发刷新。收到invalid_grant、invalid_token或 board API 的401时,删除本地凭据并提示重新登录;临时网络错误保留凭据供后续重试。Board 请求认证
在
auth_mode = "required"时,所有现有 board REST 请求、文件上传和串口 WebSocket 握手均附加:Authorization: Bearer <access-token>认证模式下禁用 HTTP 重定向,并拒绝向不同 scheme、host 或 port 的绝对 WebSocket URL 发送 Token。匿名局域网模式不携带 Authorization Header。
修改范围
ostool/src/auth/:OAuth 客户端、凭据存储、Token 生命周期管理;server、可选port与auth_mode;login、logout、auth statusCLI;尚未提供 OAuth 生命周期、REST Bearer Header、凭据文件回退和
401凭据失效的 mock 网关自动化测试;这些场景列在下方作为手工验收项。验收
本地编译与自动化测试
cargo fmt --check cargo build -p ostool cargo test -p ostool --quiet cargo clippy -p ostool --all-targets -- -D warnings编译后的 CLI 位于
target/debug/ostool:本地匿名 board 服务
启动本地
ostool-server后,使用以下匿名配置验证局域网访问:本地认证网关
登录鉴权测试需要一个本地 HTTPS 认证网关;它应在同一 Base URL 下提供
/oauth/device/code、/oauth/token、/oauth/revoke,并代理或实现/api/v1/...board API。开发证书必须被操作系统信任,客户端不提供跳过 TLS 校验的选项。可先用 PAT 验证 Bearer Header 是否被携带:
可再用 mock 网关手工验证 Device Code 轮询、刷新、撤销、Refresh Token rotation、凭据文件回退和
401失效处理。可将 OAuth Token 响应的expires_in设为很短的值,再执行board ls确认刷新请求;不依赖真实开发板或真实公网服务。