Skip to main content

axum_oidc_client/
errors.rs

1use axum::response::IntoResponse;
2
3#[cfg(feature = "redis")]
4use redis::RedisError;
5
6#[derive(Debug)]
7pub enum Error {
8    MissingCodeVerifier,
9    MissingPatameter(String),
10    NotValidUri(String),
11    Request(reqwest::Error),
12    InvalidCodeResponse(serde_html_form::de::Error),
13    InvalidTokenResponse(serde_json::Error),
14    InvalidResponse(String),
15    CacheError(String),
16    TokenRefreshFailed(String),
17    // HTTP Status Code specific errors
18    BadRequest(String),
19    Unauthorized(String),
20    Forbidden(String),
21    NotFound(String),
22    TooManyRequests(String),
23    InternalServerError(String),
24    BadGateway(String),
25    ServiceUnavailable(String),
26    UnknownStatusCode(u16, String),
27    // Configuration errors
28    AuthCacheNotConfigured,
29    OAuthConfigNotConfigured,
30    HttpClientNotConfigured,
31    // Session and authentication errors
32    SessionNotFound,
33    SessionExpired,
34    CacheAccessError(String),
35    SessionUpdateFailed(String),
36    TokenRefreshFailedAuth(String),
37}
38
39impl IntoResponse for Error {
40    fn into_response(self) -> axum::response::Response {
41        match self {
42            Error::InvalidResponse(res) => {
43                let message = format!("Invalid response {res}");
44                (axum::http::StatusCode::INTERNAL_SERVER_ERROR, message).into_response()
45            }
46
47            Error::MissingCodeVerifier => {
48                let message = "Missing code verifier";
49                (axum::http::StatusCode::BAD_REQUEST, message).into_response()
50            }
51            Error::MissingPatameter(param) => {
52                let message = format!("Missing parameter: {param}");
53                (axum::http::StatusCode::BAD_REQUEST, message).into_response()
54            }
55            Error::NotValidUri(uri) => {
56                let message = format!("Not a valid URI: {uri}");
57                (axum::http::StatusCode::BAD_REQUEST, message).into_response()
58            }
59            Error::Request(err) => {
60                let message = format!("Reqwest error: {err}");
61                (axum::http::StatusCode::INTERNAL_SERVER_ERROR, message).into_response()
62            }
63            Error::InvalidCodeResponse(err) => {
64                let message = format!("Invalid code response: {err}");
65                (axum::http::StatusCode::INTERNAL_SERVER_ERROR, message).into_response()
66            }
67            Error::InvalidTokenResponse(err) => {
68                let message = format!("Invalid token response: {err}");
69                (axum::http::StatusCode::INTERNAL_SERVER_ERROR, message).into_response()
70            }
71            Error::CacheError(err) => {
72                let message = format!("Cache error: {err}");
73                (axum::http::StatusCode::INTERNAL_SERVER_ERROR, message).into_response()
74            }
75            Error::TokenRefreshFailed(err) => {
76                let message = format!("Token refresh failed: {err}");
77                (axum::http::StatusCode::UNAUTHORIZED, message).into_response()
78            }
79            // HTTP Status Code specific errors
80            Error::BadRequest(msg) => (axum::http::StatusCode::BAD_REQUEST, msg).into_response(),
81            Error::Unauthorized(msg) => (axum::http::StatusCode::UNAUTHORIZED, msg).into_response(),
82            Error::Forbidden(msg) => (axum::http::StatusCode::FORBIDDEN, msg).into_response(),
83            Error::NotFound(msg) => (axum::http::StatusCode::NOT_FOUND, msg).into_response(),
84            Error::TooManyRequests(msg) => {
85                (axum::http::StatusCode::TOO_MANY_REQUESTS, msg).into_response()
86            }
87            Error::InternalServerError(msg) => {
88                (axum::http::StatusCode::INTERNAL_SERVER_ERROR, msg).into_response()
89            }
90            Error::BadGateway(msg) => (axum::http::StatusCode::BAD_GATEWAY, msg).into_response(),
91            Error::ServiceUnavailable(msg) => {
92                (axum::http::StatusCode::SERVICE_UNAVAILABLE, msg).into_response()
93            }
94            Error::UnknownStatusCode(code, msg) => {
95                let message = format!("HTTP {code}: {msg}");
96                (axum::http::StatusCode::INTERNAL_SERVER_ERROR, message).into_response()
97            }
98            // Configuration errors
99            Error::AuthCacheNotConfigured => {
100                let message = "AuthCache not configured. Make sure to add it to your app with Extension(cache).";
101                (axum::http::StatusCode::INTERNAL_SERVER_ERROR, message).into_response()
102            }
103            Error::OAuthConfigNotConfigured => {
104                let message = "OAuthConfiguration not configured. Make sure to add it to your app with Extension(config).";
105                (axum::http::StatusCode::INTERNAL_SERVER_ERROR, message).into_response()
106            }
107            Error::HttpClientNotConfigured => {
108                let message = "HTTP Client not configured. Make sure to use AuthLayer middleware.";
109                (axum::http::StatusCode::INTERNAL_SERVER_ERROR, message).into_response()
110            }
111            // Session and authentication errors
112            Error::SessionNotFound => {
113                let message = "No active session found. Please log in.";
114                (axum::http::StatusCode::UNAUTHORIZED, message).into_response()
115            }
116            Error::SessionExpired => {
117                let message = "Session expired or not found. Please log in again.";
118                (axum::http::StatusCode::UNAUTHORIZED, message).into_response()
119            }
120            Error::CacheAccessError(msg) => {
121                let message = format!("Cache error: {msg}");
122                (axum::http::StatusCode::INTERNAL_SERVER_ERROR, message).into_response()
123            }
124            Error::SessionUpdateFailed(msg) => {
125                let message = format!("Failed to update session in cache: {msg}");
126                (axum::http::StatusCode::INTERNAL_SERVER_ERROR, message).into_response()
127            }
128            Error::TokenRefreshFailedAuth(msg) => {
129                let message =
130                    format!("Token expired and refresh failed. Please log in again: {msg}");
131                (axum::http::StatusCode::UNAUTHORIZED, message).into_response()
132            }
133        }
134    }
135}
136
137impl Error {
138    /// Convert HTTP status code and response text to appropriate Error variant
139    pub fn from_status_code(status: axum::http::StatusCode, response_text: String) -> Self {
140        match status {
141            axum::http::StatusCode::BAD_REQUEST => Error::BadRequest(response_text),
142            axum::http::StatusCode::UNAUTHORIZED => Error::Unauthorized(response_text),
143            axum::http::StatusCode::FORBIDDEN => Error::Forbidden(response_text),
144            axum::http::StatusCode::NOT_FOUND => Error::NotFound(response_text),
145            axum::http::StatusCode::TOO_MANY_REQUESTS => Error::TooManyRequests(response_text),
146            axum::http::StatusCode::INTERNAL_SERVER_ERROR => {
147                Error::InternalServerError(response_text)
148            }
149            axum::http::StatusCode::BAD_GATEWAY => Error::BadGateway(response_text),
150            axum::http::StatusCode::SERVICE_UNAVAILABLE => Error::ServiceUnavailable(response_text),
151            _ => Error::UnknownStatusCode(status.as_u16(), response_text),
152        }
153    }
154}
155
156impl From<reqwest::Error> for Error {
157    fn from(err: reqwest::Error) -> Self {
158        Error::Request(err)
159    }
160}
161
162impl From<serde_json::Error> for Error {
163    fn from(err: serde_json::Error) -> Self {
164        Error::InvalidTokenResponse(err)
165    }
166}
167
168impl From<serde_html_form::de::Error> for Error {
169    fn from(err: serde_html_form::de::Error) -> Self {
170        Error::InvalidCodeResponse(err)
171    }
172}
173
174#[cfg(feature = "redis")]
175impl From<RedisError> for Error {
176    fn from(err: RedisError) -> Self {
177        Error::CacheError(err.to_string())
178    }
179}