freighter_auth/
error.rs

1use axum::http::StatusCode;
2use axum::response::{IntoResponse, Response};
3use sha2::{Digest, Sha256};
4use thiserror::Error;
5
6pub type AuthResult<T> = Result<T, AuthError>;
7
8#[derive(Error, Debug)]
9pub enum AuthError {
10    #[error("The credentials were missing, or were insufficient to perform the operation requested")]
11    Unauthorized,
12    #[error("The client is not allowed to perform the operation requested")]
13    Forbidden,
14    #[error("The credentials supplied were invalid")]
15    InvalidCredentials,
16    #[error("This operation is not implemented")]
17    Unimplemented,
18    #[error("The requested crate does not exist")]
19    CrateNotFound,
20    #[error("Internal error ({})", error_id(_0))]
21    ServiceError(#[from] anyhow::Error),
22}
23
24impl IntoResponse for AuthError {
25    fn into_response(self) -> Response {
26        let code = match &self {
27            AuthError::Forbidden => StatusCode::FORBIDDEN,
28            AuthError::Unauthorized => StatusCode::UNAUTHORIZED,
29            AuthError::CrateNotFound => StatusCode::NOT_FOUND,
30            AuthError::InvalidCredentials => StatusCode::UNAUTHORIZED,
31            AuthError::Unimplemented => StatusCode::NOT_IMPLEMENTED,
32            AuthError::ServiceError(error) => {
33                tracing::error!(?error, "Encountered service error in auth operation");
34
35                StatusCode::INTERNAL_SERVER_ERROR
36            }
37        };
38        (code, self.to_string()).into_response()
39    }
40}
41
42/// We can't disclose the acutal message, it could contian private info or attacker-injected strings.
43/// But it is useful to differentiate between different types of internal errors.
44fn error_id(err: &anyhow::Error) -> String {
45    let msg = err.to_string();
46    format!("{:.6x}", Sha256::digest(msg.as_bytes()))
47}