Skip to main content

solid_pod_rs_idp/
error.rs

1//! Crate-wide error type for the IdP.
2
3use thiserror::Error;
4
5use solid_pod_rs::PodError;
6
7/// Errors surfaced by the provider surface.
8#[derive(Debug, Error)]
9pub enum ProviderError {
10    /// OIDC spec error: something about the request was malformed.
11    #[error("invalid request: {0}")]
12    InvalidRequest(String),
13
14    /// OIDC spec error: the caller is not allowed to perform this
15    /// action.
16    #[error("invalid grant: {0}")]
17    InvalidGrant(String),
18
19    /// OIDC spec error: unknown client.
20    #[error("invalid client: {0}")]
21    InvalidClient(String),
22
23    /// Missing / malformed DPoP proof.
24    #[error("invalid DPoP: {0}")]
25    InvalidDpop(String),
26
27    /// Client Identifier Document fetch / validation failed.
28    #[error("client document: {0}")]
29    ClientDocument(String),
30
31    /// Password does not meet the minimum length requirement.
32    /// JSS commit `1feead2` enforces >= 8 characters at registration.
33    #[error("password must be at least {min_length} characters")]
34    PasswordTooShort {
35        /// The minimum length that was not met.
36        min_length: usize,
37    },
38
39    /// Rate limit tripped.
40    #[error("rate limited (retry after {retry_after_secs}s)")]
41    RateLimited {
42        /// Seconds the client should wait.
43        retry_after_secs: u64,
44    },
45
46    /// User store failure (DB down, etc.).
47    #[error("user store: {0}")]
48    UserStore(String),
49
50    /// Internal crypto / JWT error.
51    #[error("crypto: {0}")]
52    Crypto(String),
53
54    /// Session lookup / expiry error.
55    #[error("session: {0}")]
56    Session(String),
57
58    /// Propagation from core crate.
59    #[error(transparent)]
60    Core(#[from] PodError),
61
62    /// I/O (file-backed stores, etc.).
63    #[error(transparent)]
64    Io(#[from] std::io::Error),
65
66    /// Generic "something unexpected happened".
67    #[error("internal: {0}")]
68    Internal(String),
69}
70
71impl ProviderError {
72    /// Stable short code for wire responses (`error` field of an
73    /// OAuth2 error response, RFC 6749 §5.2).
74    pub fn code(&self) -> &'static str {
75        match self {
76            Self::InvalidRequest(_) | Self::PasswordTooShort { .. } => "invalid_request",
77            Self::InvalidGrant(_) => "invalid_grant",
78            Self::InvalidClient(_) => "invalid_client",
79            Self::InvalidDpop(_) => "invalid_dpop_proof",
80            Self::ClientDocument(_) => "invalid_client",
81            Self::RateLimited { .. } => "rate_limited",
82            Self::UserStore(_) | Self::Session(_) | Self::Internal(_) | Self::Io(_) => {
83                "server_error"
84            }
85            Self::Crypto(_) => "server_error",
86            Self::Core(_) => "server_error",
87        }
88    }
89}