Skip to main content

trellis_auth/
models.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::time::Duration;
4use tokio::sync::oneshot;
5
6use crate::{AuthenticatedUser, SentinelCredsRecord};
7use trellis_client::SessionAuth;
8
9/// Persisted admin session details for the CLI.
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct AdminSessionState {
12    /// Base URL for the Trellis auth service.
13    pub auth_url: String,
14    /// Comma-separated NATS server list returned by Trellis.
15    pub nats_servers: String,
16    /// Session-key seed used to sign subsequent Trellis requests.
17    pub session_seed: String,
18    /// Public session key derived from `session_seed`.
19    pub session_key: String,
20    /// Current binding token for the admin session.
21    pub binding_token: String,
22    /// Sentinel JWT used for NATS authentication.
23    pub sentinel_jwt: String,
24    /// Sentinel seed used for NATS authentication.
25    pub sentinel_seed: String,
26    /// RFC3339 expiry timestamp for the current binding token.
27    pub expires: String,
28}
29
30/// A successfully bound user session.
31#[derive(Debug, Clone, Deserialize, Serialize)]
32pub struct BoundSession {
33    /// Bind token used to establish NATS connections for the session.
34    #[serde(rename = "bindingToken")]
35    pub binding_token: String,
36    /// Inbox prefix authorized for the bound session.
37    #[serde(rename = "inboxPrefix")]
38    pub inbox_prefix: String,
39    /// RFC3339 expiry timestamp for this binding.
40    pub expires: String,
41    /// Sentinel credentials returned alongside the binding.
42    pub sentinel: SentinelCredsRecord,
43}
44
45#[derive(Debug, Clone, Deserialize)]
46pub(crate) struct BindResponseBound {
47    #[serde(rename = "bindingToken")]
48    pub binding_token: String,
49    #[serde(rename = "inboxPrefix")]
50    pub inbox_prefix: String,
51    pub expires: String,
52    pub sentinel: SentinelCredsRecord,
53}
54
55#[derive(Debug, Clone, Deserialize)]
56#[serde(tag = "status", rename_all = "snake_case")]
57pub(crate) enum BindResponse {
58    Bound(BindResponseBound),
59    ApprovalRequired {
60        approval: Value,
61    },
62    ApprovalDenied {
63        approval: Value,
64    },
65    InsufficientCapabilities {
66        approval: Value,
67        #[serde(rename = "missingCapabilities")]
68        missing_capabilities: Vec<String>,
69    },
70}
71
72#[derive(Debug, Clone, Deserialize)]
73pub(crate) struct CallbackTokenRequest {
74    #[serde(rename = "flowId")]
75    pub flow_id: Option<String>,
76    #[serde(rename = "authError")]
77    pub auth_error: Option<String>,
78}
79
80#[derive(Debug)]
81pub(crate) enum CallbackOutcome {
82    FlowId(String),
83    AuthError(String),
84}
85
86/// An in-progress browser login flow waiting for the auth callback.
87pub struct BrowserLoginChallenge {
88    pub(crate) login_url: String,
89    pub(crate) session_seed: String,
90    pub(crate) auth: SessionAuth,
91    pub(crate) receiver: oneshot::Receiver<CallbackOutcome>,
92    pub(crate) server_handle: tokio::task::JoinHandle<()>,
93}
94
95/// Options for starting a browser-based admin login flow.
96pub struct StartBrowserLoginOpts<'a> {
97    /// Base URL for the Trellis auth service.
98    pub auth_url: &'a str,
99    /// Local callback address in `host:port` form.
100    pub listen: &'a str,
101    /// Contract JSON sent to `/auth/login` when starting the flow.
102    pub contract_json: &'a str,
103}
104
105/// Successful browser-login result after the admin user has been verified.
106pub struct AdminLoginOutcome {
107    /// Persistable admin session state for later CLI reuse.
108    pub state: AdminSessionState,
109    /// Authenticated user returned by `Auth.Me` after bind succeeds.
110    pub user: AuthenticatedUser,
111}
112
113/// Derived workload identity material used by the workload activation helpers.
114#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
115pub struct WorkloadIdentity {
116    #[serde(rename = "identitySeedBase64url")]
117    pub identity_seed_base64url: String,
118    #[serde(rename = "publicIdentityKey")]
119    pub public_identity_key: String,
120    #[serde(rename = "activationKeyBase64url")]
121    pub activation_key_base64url: String,
122}
123
124/// Encoded workload activation payload carried in the activation QR.
125#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
126pub struct WorkloadActivationPayload {
127    pub v: u8,
128    #[serde(rename = "publicIdentityKey")]
129    pub public_identity_key: String,
130    pub nonce: String,
131    #[serde(rename = "qrMac")]
132    pub qr_mac: String,
133}
134
135/// Signed pre-auth request sent to `/auth/workloads/activate/wait`.
136#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
137pub struct WorkloadActivationWaitRequest {
138    #[serde(rename = "publicIdentityKey")]
139    pub public_identity_key: String,
140    #[serde(rename = "contractDigest", skip_serializing_if = "Option::is_none")]
141    pub contract_digest: Option<String>,
142    pub nonce: String,
143    pub iat: u64,
144    pub sig: String,
145}
146
147/// Activated wait response returned by auth.
148#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
149pub struct WorkloadActivationActivatedResponse {
150    pub status: String,
151    #[serde(rename = "activatedAt")]
152    pub activated_at: String,
153    #[serde(rename = "confirmationCode", skip_serializing_if = "Option::is_none")]
154    pub confirmation_code: Option<String>,
155    #[serde(rename = "connectInfo")]
156    pub connect_info: serde_json::Value,
157}
158
159/// Rejected wait response returned by auth.
160#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
161pub struct WorkloadActivationRejectedResponse {
162    pub status: String,
163    #[serde(skip_serializing_if = "Option::is_none")]
164    pub reason: Option<String>,
165}
166
167/// Pending wait response returned by auth.
168#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
169pub struct WorkloadActivationPendingResponse {
170    pub status: String,
171}
172
173/// Union of possible wait responses returned by auth.
174#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
175#[serde(tag = "status", rename_all = "lowercase")]
176pub enum WaitForWorkloadActivationResponse {
177    Activated {
178        #[serde(rename = "activatedAt")]
179        activated_at: String,
180        #[serde(rename = "confirmationCode", skip_serializing_if = "Option::is_none")]
181        confirmation_code: Option<String>,
182        #[serde(rename = "connectInfo")]
183        connect_info: serde_json::Value,
184    },
185    Rejected {
186        #[serde(skip_serializing_if = "Option::is_none")]
187        reason: Option<String>,
188    },
189    Pending,
190}
191
192/// Polling options for waiting on an activated workload.
193pub struct WaitForWorkloadActivationOpts<'a> {
194    pub auth_url: &'a str,
195    pub public_identity_key: &'a str,
196    pub nonce: &'a str,
197    pub identity_seed_base64url: &'a str,
198    pub contract_digest: Option<&'a str>,
199    pub poll_interval: Duration,
200}