Skip to main content

swarmhive_api_types/
device.rs

1//! RFC 8628 Device Authorization Grant 的 HTTP DTO(`swarmhive login` 用)。
2//!
3//! `swarmhive-cli` 序列化请求 / 反序列化响应;`swarmhive-server` 反序列化请求 /
4//! 序列化响应。token 端点的错误体走 **RFC 8628 wire 格式**(`{ "error": <code> }`),
5//! **不**走仓库通用的 RFC 9457 problem+json——让标准 device-flow 客户端可互操作。
6
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9use utoipa::ToSchema;
10
11use crate::api_token::ApiTokenKind;
12
13/// RFC 8628 §3.4 规定的 device-code grant_type URN。
14pub const DEVICE_GRANT_TYPE: &str = "urn:ietf:params:oauth:grant-type:device_code";
15
16/// `POST /api/v1/auth/device/code` 请求体。
17#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
18pub struct DeviceCodeRequest {
19    /// OAuth public client 标识,固定 `"swarmhive-cli"`。
20    pub client_id: String,
21    /// 预留 scope(MVP 忽略,铸全权 PAT)。
22    #[serde(default)]
23    pub scope: Option<String>,
24    /// 待铸 PAT 的友好名(如 `<host>-<ts>`);省略时 server 兜底。
25    #[serde(default)]
26    pub token_name: Option<String>,
27}
28
29/// `POST /api/v1/auth/device/code` 成功响应(RFC 8628 §3.2)。
30#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
31pub struct DeviceCodeResponse {
32    /// 高熵设备码,CLI 轮询时回传。明文只此一次。
33    pub device_code: String,
34    /// 用户可读码,形如 `WDJB-MJHT`。
35    pub user_code: String,
36    /// 用户应打开的页面(`{base_url}/device`)。
37    pub verification_uri: String,
38    /// 预填 user_code 的便捷链接(`{base_url}/device?user_code=...`)。
39    pub verification_uri_complete: String,
40    /// device_code 有效秒数(~900)。
41    pub expires_in: i64,
42    /// 轮询建议间隔秒数(5)。
43    pub interval: i64,
44}
45
46/// `POST /api/v1/auth/device/token` 请求体(RFC 8628 §3.4)。
47#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
48pub struct DeviceTokenRequest {
49    /// 必须等于 [`DEVICE_GRANT_TYPE`]。
50    pub grant_type: String,
51    pub device_code: String,
52    pub client_id: String,
53}
54
55/// `POST /api/v1/auth/device/token` 成功响应。字段形状与 token 创建响应一致,铸的是 PAT。
56#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
57pub struct DeviceTokenResponse {
58    /// 明文 PAT,格式 `swhv_pat_<43>`。只此一次。
59    pub token: String,
60    pub name: String,
61    pub kind: ApiTokenKind,
62    pub created_at: DateTime<Utc>,
63}
64
65/// RFC 8628 §3.5 / RFC 6749 §5.2 的 token 端点错误码。`400 { "error": <code> }`。
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
67#[serde(rename_all = "snake_case")]
68pub enum DeviceTokenError {
69    /// 用户尚未批准,继续轮询。
70    AuthorizationPending,
71    /// 轮询太快,客户端应 `interval += 5`。
72    SlowDown,
73    /// 用户拒绝,停止轮询。
74    AccessDenied,
75    /// device_code 已过期,重新 `login`。
76    ExpiredToken,
77    /// device_code 未知 / 已消费 / 抢占失败。
78    InvalidGrant,
79    /// grant_type 非 device-code URN。
80    UnsupportedGrantType,
81    /// client_id 缺失 / 不匹配。
82    InvalidRequest,
83}
84
85/// token 端点错误响应体(RFC 8628 wire 格式,非 problem+json)。
86#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
87pub struct DeviceTokenErrorResponse {
88    pub error: DeviceTokenError,
89}
90
91/// approve / deny 请求体。
92#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
93pub struct DeviceVerifyRequest {
94    pub user_code: String,
95}
96
97/// `GET /api/v1/auth/device/lookup` 响应:给批准页展示「谁在请求」。不含任何 secret。
98#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
99pub struct DeviceAuthorizationView {
100    pub user_code: String,
101    pub client_id: String,
102    /// 内嵌 host,如 `swarmhive @ macbook.local`。
103    pub client_name: Option<String>,
104    pub created_at: DateTime<Utc>,
105    pub expires_at: DateTime<Utc>,
106}