Skip to main content

mcp_kit/auth/
identity.rs

1use std::collections::HashMap;
2
3/// The result of a successful authentication.
4///
5/// An `AuthenticatedIdentity` is stored in the [`Session`] after the transport
6/// layer validates the incoming credentials. Handlers that declare an [`Auth`]
7/// extractor receive a clone of this value.
8///
9/// [`Session`]: crate::server::session::Session
10/// [`Auth`]: crate::server::extract::Auth
11#[derive(Debug, Clone)]
12pub struct AuthenticatedIdentity {
13    /// Stable subject identifier — e.g. a username, client ID, or JWT `sub` claim.
14    pub subject: String,
15
16    /// Scopes / roles granted to this identity (e.g. `["tools:execute", "admin"]`).
17    pub scopes: Vec<String>,
18
19    /// Arbitrary key-value metadata attached during authentication.
20    /// Examples: OAuth2 claims, certificate CN, tenant ID.
21    pub metadata: HashMap<String, serde_json::Value>,
22}
23
24impl AuthenticatedIdentity {
25    /// Create a new identity with the given subject and no scopes or metadata.
26    pub fn new(subject: impl Into<String>) -> Self {
27        Self {
28            subject: subject.into(),
29            scopes: Vec::new(),
30            metadata: HashMap::new(),
31        }
32    }
33
34    /// Builder: attach scopes to this identity.
35    pub fn with_scopes(mut self, scopes: impl IntoIterator<Item = impl Into<String>>) -> Self {
36        self.scopes = scopes.into_iter().map(Into::into).collect();
37        self
38    }
39
40    /// Builder: attach a single metadata key-value pair.
41    pub fn with_meta(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
42        self.metadata.insert(key.into(), value);
43        self
44    }
45
46    /// Returns `true` if this identity has the given scope.
47    pub fn has_scope(&self, scope: &str) -> bool {
48        self.scopes.iter().any(|s| s == scope)
49    }
50
51    /// Returns `true` if this identity has **all** of the given scopes.
52    pub fn has_all_scopes(&self, scopes: &[&str]) -> bool {
53        scopes.iter().all(|s| self.has_scope(s))
54    }
55}