Skip to main content

agentic_connect/types/
auth.rs

1//! Authentication types — Invention 2: Adaptive Authentication.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6/// Supported authentication methods.
7#[derive(Debug, Clone, Serialize, Deserialize)]
8#[serde(tag = "type", rename_all = "snake_case")]
9pub enum AuthMethod {
10    /// No authentication required.
11    None,
12
13    /// HTTP Basic authentication.
14    Basic { username: String, password: String },
15
16    /// Bearer token (API key, JWT, etc).
17    Bearer { token: String },
18
19    /// API key in header or query parameter.
20    ApiKey {
21        key: String,
22        header_name: Option<String>,
23        query_param: Option<String>,
24    },
25
26    /// OAuth 2.0 with token refresh.
27    OAuth2 {
28        client_id: String,
29        client_secret: Option<String>,
30        access_token: Option<String>,
31        refresh_token: Option<String>,
32        token_url: String,
33        scopes: Vec<String>,
34        expires_at: Option<DateTime<Utc>>,
35        grant_type: OAuth2GrantType,
36    },
37
38    /// SSH key authentication.
39    SshKey {
40        username: String,
41        private_key_path: String,
42        passphrase: Option<String>,
43    },
44
45    /// SSH password authentication.
46    SshPassword { username: String, password: String },
47
48    /// Mutual TLS (client certificate).
49    MutualTls {
50        cert_path: String,
51        key_path: String,
52        ca_path: Option<String>,
53    },
54}
55
56/// OAuth 2.0 grant types.
57#[derive(Debug, Clone, Serialize, Deserialize)]
58#[serde(rename_all = "snake_case")]
59pub enum OAuth2GrantType {
60    AuthorizationCode,
61    ClientCredentials,
62    RefreshToken,
63    DeviceCode,
64}
65
66impl AuthMethod {
67    /// Whether this auth method has an expirable token.
68    pub fn has_expiry(&self) -> bool {
69        matches!(self, AuthMethod::OAuth2 { .. })
70    }
71
72    /// Whether the token is expired (if applicable).
73    pub fn is_expired(&self) -> bool {
74        match self {
75            AuthMethod::OAuth2 { expires_at, .. } => {
76                expires_at.map_or(false, |exp| exp < Utc::now())
77            }
78            _ => false,
79        }
80    }
81
82    /// Whether this auth method needs a refresh.
83    pub fn needs_refresh(&self) -> bool {
84        match self {
85            AuthMethod::OAuth2 {
86                expires_at,
87                refresh_token,
88                ..
89            } => {
90                refresh_token.is_some()
91                    && expires_at.map_or(false, |exp| {
92                        // Refresh 5 minutes before expiry
93                        exp - chrono::Duration::minutes(5) < Utc::now()
94                    })
95            }
96            _ => false,
97        }
98    }
99
100    /// Display name for the auth method type.
101    pub fn method_name(&self) -> &'static str {
102        match self {
103            AuthMethod::None => "none",
104            AuthMethod::Basic { .. } => "basic",
105            AuthMethod::Bearer { .. } => "bearer",
106            AuthMethod::ApiKey { .. } => "api_key",
107            AuthMethod::OAuth2 { .. } => "oauth2",
108            AuthMethod::SshKey { .. } => "ssh_key",
109            AuthMethod::SshPassword { .. } => "ssh_password",
110            AuthMethod::MutualTls { .. } => "mtls",
111        }
112    }
113}
114
115/// Stored credential in the vault.
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct StoredCredential {
118    pub id: uuid::Uuid,
119    pub name: String,
120    pub auth: AuthMethod,
121    pub created_at: DateTime<Utc>,
122    pub last_rotated: Option<DateTime<Utc>>,
123    pub tags: Vec<String>,
124}