prism_mcp_rs/auth/
errors.rs1use std::fmt;
6
7#[derive(Debug, Clone)]
9pub enum AuthError {
10 NoAuthServer(String),
12
13 PkceNotSupported,
15
16 InvalidToken(String),
18
19 TokenExpired,
21
22 InsufficientScope(String),
24
25 RegistrationFailed(String),
27
28 DiscoveryFailed(String),
30
31 AuthorizationDenied,
33
34 InvalidAuthorizationCode,
36
37 InvalidRefreshToken,
39
40 OAuthError {
42 error: String,
43 description: Option<String>,
44 uri: Option<String>,
45 },
46
47 HttpError(String),
49
50 ConfigError(String),
52
53 StateMismatch,
55
56 InvalidResource(String),
58}
59
60impl fmt::Display for AuthError {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 match self {
63 Self::NoAuthServer(msg) => write!(f, "No authorization server: {msg}"),
64 Self::PkceNotSupported => write!(
65 f,
66 "Authorization server does not support PKCE (required for MCP)"
67 ),
68 Self::InvalidToken(msg) => write!(f, "Invalid token: {msg}"),
69 Self::TokenExpired => write!(f, "Access token has expired"),
70 Self::InsufficientScope(scope) => write!(f, "Insufficient scope: {scope}"),
71 Self::RegistrationFailed(msg) => write!(f, "Client registration failed: {msg}"),
72 Self::DiscoveryFailed(msg) => write!(f, "Discovery failed: {msg}"),
73 Self::AuthorizationDenied => write!(f, "Authorization denied by user"),
74 Self::InvalidAuthorizationCode => write!(f, "Invalid or expired authorization code"),
75 Self::InvalidRefreshToken => write!(f, "Invalid or expired refresh token"),
76 Self::OAuthError {
77 error,
78 description,
79 uri,
80 } => {
81 write!(f, "OAuth error: {error}")?;
82 if let Some(desc) = description {
83 write!(f, " - {desc}")?;
84 }
85 if let Some(uri) = uri {
86 write!(f, " (see: {uri})")?;
87 }
88 Ok(())
89 }
90 Self::HttpError(msg) => write!(f, "HTTP error: {msg}"),
91 Self::ConfigError(msg) => write!(f, "Configuration error: {msg}"),
92 Self::StateMismatch => write!(f, "State parameter mismatch (possible CSRF attack)"),
93 Self::InvalidResource(msg) => write!(f, "Invalid resource indicator: {msg}"),
94 }
95 }
96}
97
98impl std::error::Error for AuthError {}
99
100impl From<AuthError> for crate::core::error::McpError {
102 fn from(err: AuthError) -> Self {
103 crate::core::error::McpError::Auth(err.to_string())
104 }
105}
106
107pub fn parse_oauth_error(params: &[(String, String)]) -> Option<AuthError> {
109 let error = params
110 .iter()
111 .find(|(k, _)| k == "error")
112 .map(|(_, v)| v.clone())?;
113
114 let description = params
115 .iter()
116 .find(|(k, _)| k == "error_description")
117 .map(|(_, v)| v.clone());
118
119 let uri = params
120 .iter()
121 .find(|(k, _)| k == "error_uri")
122 .map(|(_, v)| v.clone());
123
124 Some(AuthError::OAuthError {
125 error,
126 description,
127 uri,
128 })
129}
130
131pub fn is_recoverable_error(error: &AuthError) -> bool {
133 matches!(error, AuthError::TokenExpired | AuthError::InvalidToken(_))
134}