1#[cfg(feature = "axum")]
2use axum::{extract::rejection::TypedHeaderRejection, response::IntoResponse, Json};
3
4use reqwest::StatusCode;
5use serde::Serialize;
6use thiserror::Error;
7
8pub type Result<T> = std::result::Result<T, Error>;
9
10#[derive(Debug, Error)]
11pub enum Error {
12 #[error("Failed to fetch JWKS")]
13 JWKSFetchError,
14
15 #[error("Reqwest error: {0}")]
16 ReqwestError(#[from] reqwest::Error),
17
18 #[error("Failed to decode token kid")]
19 TokenKidDecodeError,
20
21 #[error("{0}")]
22 ValidationError(alcoholic_jwt::ValidationError),
23
24 #[error("Failed to deserialize json: {0}")]
25 DeserializationError(serde_json::Error),
26
27 #[cfg(feature = "axum")]
28 #[error("Invalid authorization header")]
29 InvalidTokenHeader(TypedHeaderRejection),
30}
31
32impl Error {
33 pub fn status_code(&self) -> StatusCode {
34 match self {
35 Self::DeserializationError(_)
36 | Self::TokenKidDecodeError
37 | Self::ValidationError(_)
38 | Self::JWKSFetchError => StatusCode::FORBIDDEN,
39 _ => StatusCode::UNAUTHORIZED,
40 }
41 }
42
43 pub fn title(&self) -> &'static str {
44 match self {
45 Self::DeserializationError(_) => "Deserialization Error",
46 Self::TokenKidDecodeError => "Invalid token kid",
47 Self::ValidationError(_) => "Invalid Token",
48 Self::JWKSFetchError => "JWKS fetch error",
49 _ => "Internal Server Error",
50 }
51 }
52}
53
54#[cfg(feature = "axum")]
55impl IntoResponse for Error {
56 fn into_response(self) -> axum::response::Response {
57 (self.status_code(), Json(ErrorBody::from_error(self))).into_response()
58 }
59}
60
61#[derive(Debug, Serialize)]
62pub struct ErrorBody {
63 pub title: String,
64 pub message: String,
65 pub status_code: u16,
66}
67
68impl ErrorBody {
69 pub fn from_error(error: Error) -> Self {
70 Self {
71 title: error.title().to_string(),
72 message: error.to_string(),
73 status_code: error.status_code().as_u16(),
74 }
75 }
76}