Skip to main content

idprova_mcp/
error.rs

1//! Error types for MCP auth operations.
2
3use thiserror::Error;
4
5/// Errors that can occur during MCP authentication and authorization.
6#[derive(Debug, Error)]
7pub enum McpAuthError {
8    /// No DAT token was provided in the request.
9    #[error("missing DAT token: {0}")]
10    MissingToken(String),
11
12    /// The DAT token is malformed or has an invalid signature.
13    #[error("invalid DAT: {0}")]
14    InvalidDat(String),
15
16    /// The DAT does not grant the required scope.
17    #[error("insufficient scope: {0}")]
18    InsufficientScope(String),
19
20    /// DAT verification failed (expired, wrong key, constraint violated, etc.).
21    #[error("verification failed: {0}")]
22    VerificationFailed(String),
23}
24
25impl From<idprova_core::IdprovaError> for McpAuthError {
26    fn from(e: idprova_core::IdprovaError) -> Self {
27        match &e {
28            idprova_core::IdprovaError::ScopeNotPermitted(_) => {
29                McpAuthError::InsufficientScope(e.to_string())
30            }
31            idprova_core::IdprovaError::InvalidDat(_) => McpAuthError::InvalidDat(e.to_string()),
32            idprova_core::IdprovaError::DatExpired | idprova_core::IdprovaError::DatNotYetValid => {
33                McpAuthError::VerificationFailed(e.to_string())
34            }
35            _ => McpAuthError::VerificationFailed(e.to_string()),
36        }
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn test_error_display_missing_token() {
46        let e = McpAuthError::MissingToken("no token".into());
47        assert!(e.to_string().contains("missing DAT token"));
48    }
49
50    #[test]
51    fn test_error_display_invalid_dat() {
52        let e = McpAuthError::InvalidDat("bad format".into());
53        assert!(e.to_string().contains("invalid DAT"));
54    }
55
56    #[test]
57    fn test_error_display_insufficient_scope() {
58        let e = McpAuthError::InsufficientScope("need write".into());
59        assert!(e.to_string().contains("insufficient scope"));
60    }
61
62    #[test]
63    fn test_error_display_verification_failed() {
64        let e = McpAuthError::VerificationFailed("expired".into());
65        assert!(e.to_string().contains("verification failed"));
66    }
67
68    #[test]
69    fn test_from_idprova_scope_error() {
70        let core_err = idprova_core::IdprovaError::ScopeNotPermitted("write denied".into());
71        let mcp_err: McpAuthError = core_err.into();
72        assert!(matches!(mcp_err, McpAuthError::InsufficientScope(_)));
73    }
74
75    #[test]
76    fn test_from_idprova_invalid_dat() {
77        let core_err = idprova_core::IdprovaError::InvalidDat("malformed".into());
78        let mcp_err: McpAuthError = core_err.into();
79        assert!(matches!(mcp_err, McpAuthError::InvalidDat(_)));
80    }
81
82    #[test]
83    fn test_from_idprova_expired() {
84        let core_err = idprova_core::IdprovaError::DatExpired;
85        let mcp_err: McpAuthError = core_err.into();
86        assert!(matches!(mcp_err, McpAuthError::VerificationFailed(_)));
87    }
88}