Skip to main content

auths_sdk/domains/auth/
error.rs

1use auths_core::error::AuthsErrorInfo;
2use thiserror::Error;
3
4/// Errors from trust policy resolution during verification.
5///
6/// Usage:
7/// ```ignore
8/// match resolve_issuer_key(did, policy) {
9///     Err(TrustError::UnknownIdentity { did, policy }) => {
10///         eprintln!("Unknown identity under {} policy; run `auths trust pin --did {}`", policy, did)
11///     }
12///     Err(e) => return Err(e.into()),
13///     Ok(key) => { /* use key for verification */ }
14/// }
15/// ```
16#[derive(Debug, Error)]
17#[non_exhaustive]
18pub enum TrustError {
19    /// Identity is unknown and trust policy does not permit TOFU/resolution.
20    #[error("Unknown identity '{did}' and trust policy is '{policy}'")]
21    UnknownIdentity {
22        /// The unknown identity DID.
23        did: String,
24        /// The policy preventing resolution (e.g., "explicit").
25        policy: String,
26    },
27
28    /// Identity exists but no public key could be resolved.
29    #[error("Failed to resolve public key for identity {did}")]
30    KeyResolutionFailed {
31        /// The DID whose key could not be resolved.
32        did: String,
33    },
34
35    /// The provided roots.json or trust store is invalid.
36    #[error("Invalid trust store: {0}")]
37    InvalidTrustStore(String),
38
39    /// TOFU prompt was required but execution is non-interactive.
40    #[error("TOFU trust decision required but running in non-interactive mode")]
41    TofuRequiresInteraction,
42}
43
44/// Errors from MCP token exchange operations.
45///
46/// Usage:
47/// ```ignore
48/// match result {
49///     Err(McpAuthError::BridgeUnreachable(msg)) => { /* retry later */ }
50///     Err(McpAuthError::InsufficientCapabilities { .. }) => { /* request fewer caps */ }
51///     Err(e) => return Err(e.into()),
52///     Ok(token) => { /* use token */ }
53/// }
54/// ```
55#[derive(Debug, Error)]
56#[non_exhaustive]
57pub enum McpAuthError {
58    /// The OIDC bridge is unreachable.
59    #[error("bridge unreachable: {0}")]
60    BridgeUnreachable(String),
61
62    /// The bridge returned a non-success status.
63    #[error("token exchange failed (HTTP {status}): {body}")]
64    TokenExchangeFailed {
65        /// HTTP status code from the bridge.
66        status: u16,
67        /// Response body.
68        body: String,
69    },
70
71    /// The bridge response could not be parsed.
72    #[error("invalid response: {0}")]
73    InvalidResponse(String),
74
75    /// The bridge rejected the requested capabilities.
76    #[error("insufficient capabilities: requested {requested:?}")]
77    InsufficientCapabilities {
78        /// The capabilities that were requested.
79        requested: Vec<String>,
80        /// Detail from the bridge error response.
81        detail: String,
82    },
83}
84
85impl AuthsErrorInfo for TrustError {
86    fn error_code(&self) -> &'static str {
87        match self {
88            Self::UnknownIdentity { .. } => "AUTHS-E5551",
89            Self::KeyResolutionFailed { .. } => "AUTHS-E5552",
90            Self::InvalidTrustStore(_) => "AUTHS-E5553",
91            Self::TofuRequiresInteraction => "AUTHS-E5554",
92        }
93    }
94
95    fn suggestion(&self) -> Option<&'static str> {
96        match self {
97            Self::UnknownIdentity { .. } => {
98                Some("Run `auths trust pin --did <did>` or add the identity to .auths/roots.json")
99            }
100            Self::KeyResolutionFailed { .. } => {
101                Some("Verify the identity exists and has a valid public key registered")
102            }
103            Self::InvalidTrustStore(_) => Some(
104                "Check the format of your trust store (roots.json or ~/.auths/known_identities.json)",
105            ),
106            Self::TofuRequiresInteraction => {
107                Some("Run interactively (on a TTY) or use `auths verify --trust explicit`")
108            }
109        }
110    }
111}
112
113impl AuthsErrorInfo for McpAuthError {
114    fn error_code(&self) -> &'static str {
115        match self {
116            Self::BridgeUnreachable(_) => "AUTHS-E5501",
117            Self::TokenExchangeFailed { .. } => "AUTHS-E5502",
118            Self::InvalidResponse(_) => "AUTHS-E5503",
119            Self::InsufficientCapabilities { .. } => "AUTHS-E5504",
120        }
121    }
122
123    fn suggestion(&self) -> Option<&'static str> {
124        match self {
125            Self::BridgeUnreachable(_) => Some("Check network connectivity to the OIDC bridge"),
126            Self::TokenExchangeFailed { .. } => Some("Verify your credentials and try again"),
127            Self::InvalidResponse(_) => Some(
128                "The OIDC bridge returned an unexpected response; verify the bridge URL and try again",
129            ),
130            Self::InsufficientCapabilities { .. } => {
131                Some("Request fewer capabilities or contact your administrator")
132            }
133        }
134    }
135}