1use std::collections::HashMap;
6use std::time::{SystemTime, UNIX_EPOCH};
7
8use async_trait::async_trait;
9use oauth2::RefreshToken;
10use serde::{Deserialize, Serialize};
11
12use turbomcp_protocol::{Error as McpError, Result as McpResult};
13
14use super::config::AuthProviderType;
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
24#[deprecated(
25 since = "2.0.5",
26 note = "Use context::AuthContext instead. This type is legacy and will be removed in 3.0.0"
27)]
28pub struct AuthContext {
29 pub user_id: String,
31 pub user: UserInfo,
33 pub roles: Vec<String>,
35 pub permissions: Vec<String>,
37 pub request_id: String,
42 pub token: Option<TokenInfo>,
44 pub provider: String,
46 pub authenticated_at: SystemTime,
48 pub expires_at: Option<SystemTime>,
50 pub metadata: HashMap<String, serde_json::Value>,
52}
53
54#[allow(deprecated)]
55impl AuthContext {
56 pub fn to_unified(&self) -> crate::context::AuthContext {
58 crate::context::AuthContext {
59 sub: self.user_id.clone(),
60 iss: None, aud: None, exp: self
63 .expires_at
64 .and_then(|t| t.duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs())),
65 iat: self
66 .authenticated_at
67 .duration_since(UNIX_EPOCH)
68 .ok()
69 .map(|d| d.as_secs()),
70 nbf: None, jti: None, user: self.user.clone(),
73 roles: self.roles.clone(),
74 permissions: self.permissions.clone(),
75 scopes: Vec::new(), request_id: Some(self.request_id.clone()),
77 authenticated_at: self.authenticated_at,
78 expires_at: self.expires_at,
79 token: self.token.clone(),
80 provider: self.provider.clone(),
81 #[cfg(feature = "dpop")]
82 dpop_jkt: None, metadata: self.metadata.clone(),
84 }
85 }
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct UserInfo {
91 pub id: String,
93 pub username: String,
95 pub email: Option<String>,
97 pub display_name: Option<String>,
99 pub avatar_url: Option<String>,
101 pub metadata: HashMap<String, serde_json::Value>,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct TokenInfo {
108 pub access_token: String,
110 pub token_type: String,
112 pub refresh_token: Option<String>,
114 pub expires_in: Option<u64>,
116 pub scope: Option<String>,
118}
119
120#[async_trait]
122pub trait AuthProvider: Send + Sync + std::fmt::Debug {
123 fn name(&self) -> &str;
125
126 fn provider_type(&self) -> AuthProviderType;
128
129 async fn authenticate(
131 &self,
132 credentials: AuthCredentials,
133 ) -> McpResult<crate::context::AuthContext>;
134
135 async fn validate_token(&self, token: &str) -> McpResult<crate::context::AuthContext>;
137
138 async fn refresh_token(&self, refresh_token: &str) -> McpResult<TokenInfo>;
140
141 async fn revoke_token(&self, token: &str) -> McpResult<()>;
143
144 async fn get_user_info(&self, token: &str) -> McpResult<UserInfo>;
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
150pub enum AuthCredentials {
151 UsernamePassword {
153 username: String,
155 password: String,
157 },
158 ApiKey {
160 key: String,
162 },
163 OAuth2Code {
165 code: String,
167 state: String,
169 },
170 JwtToken {
172 token: String,
174 },
175 Custom {
177 data: HashMap<String, serde_json::Value>,
179 },
180}
181
182#[async_trait]
184pub trait TokenStorage: Send + Sync + std::fmt::Debug {
185 async fn store_access_token(&self, user_id: &str, token: &AccessToken) -> McpResult<()>;
187
188 async fn get_access_token(&self, user_id: &str) -> McpResult<Option<AccessToken>>;
190
191 async fn store_refresh_token(&self, user_id: &str, token: &RefreshToken) -> McpResult<()>;
193
194 async fn get_refresh_token(&self, user_id: &str) -> McpResult<Option<RefreshToken>>;
196
197 async fn revoke_tokens(&self, user_id: &str) -> McpResult<()>;
199
200 async fn list_users(&self) -> McpResult<Vec<String>>;
202}
203
204#[derive(Debug, Clone)]
206pub struct AccessToken {
207 pub(crate) token: String,
209 pub(crate) expires_at: Option<SystemTime>,
211 pub(crate) scopes: Vec<String>,
213 pub(crate) metadata: HashMap<String, serde_json::Value>,
215}
216
217impl AccessToken {
218 #[must_use]
220 pub fn new(
221 token: String,
222 expires_at: Option<SystemTime>,
223 scopes: Vec<String>,
224 metadata: HashMap<String, serde_json::Value>,
225 ) -> Self {
226 Self {
227 token,
228 expires_at,
229 scopes,
230 metadata,
231 }
232 }
233
234 #[must_use]
236 pub fn token(&self) -> &str {
237 &self.token
238 }
239
240 #[must_use]
242 pub fn expires_at(&self) -> Option<SystemTime> {
243 self.expires_at
244 }
245
246 #[must_use]
248 pub fn scopes(&self) -> &[String] {
249 &self.scopes
250 }
251
252 #[must_use]
254 pub fn metadata(&self) -> &HashMap<String, serde_json::Value> {
255 &self.metadata
256 }
257}
258
259#[async_trait]
261pub trait AuthMiddleware: Send + Sync {
262 async fn extract_token(&self, headers: &HashMap<String, String>) -> Option<String>;
264
265 async fn handle_auth_failure(&self, error: McpError) -> McpResult<()>;
267}
268
269#[derive(Debug, Clone)]
271pub struct DefaultAuthMiddleware;
272
273#[async_trait]
274impl AuthMiddleware for DefaultAuthMiddleware {
275 async fn extract_token(&self, headers: &HashMap<String, String>) -> Option<String> {
276 if let Some(auth_header) = headers
278 .get("authorization")
279 .or_else(|| headers.get("Authorization"))
280 {
281 if let Some(token) = auth_header.strip_prefix("Bearer ") {
282 return Some(token.to_string());
283 }
284 if let Some(token) = auth_header.strip_prefix("ApiKey ") {
285 return Some(token.to_string());
286 }
287 }
288
289 if let Some(api_key) = headers
291 .get("x-api-key")
292 .or_else(|| headers.get("X-API-Key"))
293 {
294 return Some(api_key.clone());
295 }
296
297 None
298 }
299
300 async fn handle_auth_failure(&self, error: McpError) -> McpResult<()> {
301 tracing::warn!("Authentication failed: {}", error);
302 Err(Box::new(error))
303 }
304}