1use serde::{Deserialize, Serialize};
8
9pub const AUTH_STATE_KEY: &str = "fastmcp.auth";
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14pub struct AccessToken {
15 pub scheme: String,
17 pub token: String,
19}
20
21impl AccessToken {
22 #[must_use]
28 pub fn parse(value: &str) -> Option<Self> {
29 let trimmed = value.trim();
30 if trimmed.is_empty() {
31 return None;
32 }
33
34 if let Some((scheme, token)) = trimmed.split_once(' ') {
35 let scheme = scheme.trim();
36 let token = token.trim();
37 if scheme.is_empty() || token.is_empty() {
38 return None;
39 }
40 return Some(Self {
41 scheme: scheme.to_string(),
42 token: token.to_string(),
43 });
44 }
45
46 Some(Self {
47 scheme: "Bearer".to_string(),
48 token: trimmed.to_string(),
49 })
50 }
51}
52
53#[derive(Debug, Clone, Default, Serialize, Deserialize)]
55pub struct AuthContext {
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub subject: Option<String>,
59 #[serde(default, skip_serializing_if = "Vec::is_empty")]
61 pub scopes: Vec<String>,
62 #[serde(skip_serializing_if = "Option::is_none")]
64 pub token: Option<AccessToken>,
65 #[serde(skip_serializing_if = "Option::is_none")]
67 pub claims: Option<serde_json::Value>,
68}
69
70impl AuthContext {
71 #[must_use]
73 pub fn anonymous() -> Self {
74 Self::default()
75 }
76
77 #[must_use]
79 pub fn with_subject(subject: impl Into<String>) -> Self {
80 Self {
81 subject: Some(subject.into()),
82 ..Self::default()
83 }
84 }
85}