axum_jwt_sessions/
error.rs

1use axum::{
2    http::StatusCode,
3    response::{IntoResponse, Response},
4};
5use serde::Serialize;
6use thiserror::Error;
7
8/// Error response returned to clients
9#[derive(Debug, Serialize)]
10#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
11pub struct ErrorResponse {
12    /// Error type identifier
13    pub error: &'static str,
14    /// Human-readable error message
15    pub message: &'static str,
16}
17
18#[derive(Error, Debug)]
19pub enum AuthError {
20    #[error("Invalid token")]
21    InvalidToken,
22
23    #[error("Token expired")]
24    TokenExpired,
25
26    #[error("Missing authorization header")]
27    MissingAuthHeader,
28
29    #[error("Invalid authorization header format")]
30    InvalidAuthHeaderFormat,
31
32    #[error("Session not found")]
33    SessionNotFound,
34
35    #[error("Storage error: {0}")]
36    StorageError(String),
37
38    #[error("Token generation failed: {0}")]
39    TokenGenerationError(String),
40
41    #[error("Invalid refresh token")]
42    InvalidRefreshToken,
43
44    #[error("Refresh token required")]
45    RefreshTokenRequired,
46}
47
48impl IntoResponse for AuthError {
49    fn into_response(self) -> Response {
50        let (status, message) = match self {
51            AuthError::InvalidToken => (StatusCode::UNAUTHORIZED, "Invalid token"),
52            AuthError::TokenExpired => (StatusCode::UNAUTHORIZED, "Token expired"),
53            AuthError::MissingAuthHeader => {
54                (StatusCode::UNAUTHORIZED, "Missing authorization header")
55            }
56            AuthError::InvalidAuthHeaderFormat => (
57                StatusCode::BAD_REQUEST,
58                "Invalid authorization header format",
59            ),
60            AuthError::SessionNotFound => (StatusCode::UNAUTHORIZED, "Session not found"),
61            AuthError::StorageError(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Storage error"),
62            AuthError::TokenGenerationError(_) => {
63                (StatusCode::INTERNAL_SERVER_ERROR, "Token generation failed")
64            }
65            AuthError::InvalidRefreshToken => (StatusCode::UNAUTHORIZED, "Invalid refresh token"),
66            AuthError::RefreshTokenRequired => (
67                StatusCode::FORBIDDEN,
68                "Refresh token required for this endpoint",
69            ),
70        };
71
72        let error_response = ErrorResponse {
73            error: match self {
74                AuthError::InvalidToken => "invalid_token",
75                AuthError::TokenExpired => "token_expired",
76                AuthError::MissingAuthHeader => "missing_authorization_header",
77                AuthError::InvalidAuthHeaderFormat => "invalid_authorization_header",
78                AuthError::SessionNotFound => "session_not_found",
79                AuthError::StorageError(_) => "storage_error",
80                AuthError::TokenGenerationError(_) => "token_generation_error",
81                AuthError::InvalidRefreshToken => "invalid_refresh_token",
82                AuthError::RefreshTokenRequired => "refresh_token_required",
83            },
84            message,
85        };
86
87        (status, axum::Json(error_response)).into_response()
88    }
89}
90
91pub type Result<T> = std::result::Result<T, AuthError>;