Skip to content
Clone
2139a03
109 lines 3.4 KB Rust
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use export::data::{AppResponseData, NoData};
use thiserror::Error;
use cache::redis::RedisError;
use email::EmailError;
use jwt::JwtError;
use crate::pow::PowError;
/// Result alias used by every auth use case.
pub type AuthResult<T> = Result<T, AuthError>;
#[derive(Debug, Error)]
pub enum AuthError {
#[error("bad request: {0}")]
BadRequest(String),
#[error("invalid credentials")]
InvalidCredentials,
#[error("unauthorized: {0}")]
Unauthorized(String),
#[error("forbidden: {0}")]
Forbidden(String),
#[error("not found: {0}")]
NotFound(String),
#[error("conflict: {0}")]
Conflict(String),
#[error("rate limited: {0}")]
RateLimited(String),
#[error("pow failed: {0}")]
PowFailed(String),
#[error("internal error: {0}")]
Internal(String),
#[error("database error: {0}")]
Sqlx(#[from] db::conn::SqlxError),
#[error("redis error: {0}")]
Redis(#[from] RedisError),
#[error("pow error: {0}")]
Pow(#[from] PowError),
#[error("jwt error: {0}")]
Jwt(#[from] JwtError),
#[error("email error: {0}")]
Email(#[from] EmailError),
#[error("uuid: {0}")]
Uuid(#[from] uuid::Error),
}
impl From<axum::extract::rejection::JsonRejection> for AuthError {
fn from(re: axum::extract::rejection::JsonRejection) -> Self {
AuthError::BadRequest(re.body_text())
}
}
impl From<axum::extract::rejection::QueryRejection> for AuthError {
fn from(re: axum::extract::rejection::QueryRejection) -> Self {
AuthError::BadRequest(re.body_text())
}
}
impl AuthError {
fn status(&self) -> StatusCode {
match self {
AuthError::BadRequest(_) | AuthError::InvalidCredentials | AuthError::PowFailed(_) => {
StatusCode::BAD_REQUEST
}
AuthError::Forbidden(_) => StatusCode::FORBIDDEN,
AuthError::Conflict(_) => StatusCode::CONFLICT,
AuthError::Unauthorized(_) => StatusCode::UNAUTHORIZED,
AuthError::NotFound(_) => StatusCode::NOT_FOUND,
AuthError::RateLimited(_) => StatusCode::TOO_MANY_REQUESTS,
AuthError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
AuthError::Sqlx(_) => StatusCode::INTERNAL_SERVER_ERROR,
AuthError::Redis(_) => StatusCode::INTERNAL_SERVER_ERROR,
AuthError::Pow(_) => StatusCode::BAD_REQUEST,
AuthError::Jwt(_) => StatusCode::UNAUTHORIZED,
AuthError::Email(_) => StatusCode::INTERNAL_SERVER_ERROR,
AuthError::Uuid(_) => StatusCode::BAD_REQUEST,
}
}
fn is_user_facing(&self) -> bool {
matches!(
self,
AuthError::BadRequest(_)
| AuthError::InvalidCredentials
| AuthError::Unauthorized(_)
| AuthError::Forbidden(_)
| AuthError::NotFound(_)
| AuthError::Conflict(_)
| AuthError::RateLimited(_)
| AuthError::PowFailed(_)
)
}
}
impl IntoResponse for AuthError {
fn into_response(self) -> Response {
let status = self.status();
let msg = if self.is_user_facing() {
self.to_string()
} else {
tracing::error!(error = %self, "auth internal error");
"internal error".to_string()
};
AppResponseData::<NoData>::err_plain(status.as_u16() as i32, msg).into_response()
}
}