ones_oidc/
errors.rs

1use thiserror::Error;
2
3#[derive(Error, Debug)]
4pub enum OidcRequirementsError {
5    #[error("Provider metadata does not contain a token endpoint")]
6    MissingTokenEndpoint,
7}
8
9#[derive(Error, Debug)]
10pub enum EncodingDecodingErrors {
11    #[error("Encoding error: {0}")]
12    EncodingError(#[from] serde_json::Error),
13    #[error("JSON WebToken Decoding error: {0}")]
14    JsonWebTokenDecodingError(#[from] jsonwebtoken::errors::Error),
15}
16
17#[derive(Error, Debug)]
18pub enum DeviceError {
19    #[error("Device related request error: {0}")]
20    TokenRequest(#[from] reqwest::Error),
21    #[error("Device related oidc requirements error: {0}")]
22    TokenRequestRequirements(#[from] OidcRequirementsError),
23    #[error("Invalid signature request response")]
24    InvalidSignatureResponse,
25    #[error("Invalid response: {0}")]
26    InvalidResponse(String),
27    #[error("Device encoding error: {0}")]
28    EncodingError(#[from] serde_json::Error),
29    #[error("Device reading config error: {0}")]
30    ConfigReadError(#[from] std::io::Error),
31    #[error("Device config parsing error: {0}")]
32    ConfigParseError(#[from] serde_yml::Error),
33    #[error("Invalid client ID format: {0}")]
34    InvalidClientId(#[from] uuid::Error),
35    #[error("JWT encoding failed: {0}")]
36    JwtEncodingError(#[from] jsonwebtoken::errors::Error),
37}
38
39#[derive(Error, Debug)]
40pub enum OidcError {
41    #[error("OIDC related request error: {0}")]
42    RequestError(#[from] reqwest::Error),
43    #[error("Request error: {status_code} - {error}: {error_description}")]
44    RequestErrorWithDetails {
45        status_code: u16,
46        error: String,
47        error_description: String,
48    },
49    #[error("JSON WebToken Decoding error: {0}")]
50    JsonWebTokenDecodingError(#[from] jsonwebtoken::errors::Error),
51    #[error("CIBA status check failed: {0}")]
52    CibaStatusCheckFailed(String),
53    #[error("CIBA status bad request: {0}")]
54    CibaStatusBadRequest(String),
55    #[error("CIBA authentication pending")]
56    CibaAuthenticationPending,
57    #[error("Token introspection failed: {0}")]
58    TokenIntrospectionFailed(String),
59    #[error("Token is not active")]
60    TokenNotActive,
61    #[error("Token identification failed: {0}")]
62    TokenIdentificationFailed(String),
63    #[error("OIDC requirements error: {0}")]
64    OIDCRequestRequirements(#[from] OidcRequirementsError),
65    #[error("OIDC Device error: {0}")]
66    DeviceError(#[from] DeviceError),
67    #[error("Invalid UUID format: {0}")]
68    InvalidUuid(#[from] uuid::Error),
69    #[error("JWK set is empty or invalid")]
70    InvalidJwkSet,
71    #[error("Invalid JWK format: {0}")]
72    InvalidJwk(String),
73    #[error("Missing required claim: {0}")]
74    MissingClaim(String),
75    #[error("QR authentication session is invalid")]
76    InvalidQrSession,
77}
78
79#[derive(Error, Debug)]
80pub enum WellKnownApplicationsError {
81    #[error("Well known applications request error: {0}")]
82    RequestError(#[from] reqwest::Error),
83    #[error("Well known applications decoding error: {0}")]
84    DecodingError(#[from] serde_json::Error),
85    #[error("Well known application not found")]
86    NotFound,
87}
88
89#[derive(Error, Debug)]
90pub enum UtilsError {
91    #[error("IO error: {0}")]
92    IoError(#[from] std::io::Error),
93    #[error("Invalid private key format: {0}")]
94    InvalidPrivateKey(String),
95}
96
97impl actix_web::ResponseError for OidcError {
98    fn status_code(&self) -> actix_web::http::StatusCode {
99        match self {
100            OidcError::RequestErrorWithDetails { status_code, .. } => {
101                if *status_code > 0 {
102                    actix_web::http::StatusCode::from_u16(*status_code)
103                        .unwrap_or(actix_web::http::StatusCode::INTERNAL_SERVER_ERROR)
104                } else {
105                    actix_web::http::StatusCode::INTERNAL_SERVER_ERROR
106                }
107            },
108            OidcError::CibaAuthenticationPending => actix_web::http::StatusCode::BAD_REQUEST,
109            OidcError::CibaStatusBadRequest(_) => actix_web::http::StatusCode::BAD_REQUEST,
110            OidcError::CibaStatusCheckFailed(_) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
111            OidcError::TokenIntrospectionFailed(_) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
112            OidcError::TokenNotActive => actix_web::http::StatusCode::UNAUTHORIZED,
113            OidcError::TokenIdentificationFailed(_) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
114            _ => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
115        }
116    }
117
118    fn error_response(&self) -> actix_web::HttpResponse {
119        match self {
120            OidcError::RequestErrorWithDetails { error, error_description, .. } => {
121                let error_body = serde_json::json!({
122                    "error": error,
123                    "error_description": error_description
124                });
125                actix_web::HttpResponse::build(self.status_code())
126                    .content_type("application/json")
127                    .body(error_body.to_string())
128            },
129            OidcError::CibaAuthenticationPending => {
130                let error_body = serde_json::json!({
131                    "error": "authorization_pending",
132                    "error_description": "Authorization request is still pending as the end-user hasn't yet completed the user interaction steps"
133                });
134                actix_web::HttpResponse::build(self.status_code())
135                    .content_type("application/json")
136                    .body(error_body.to_string())
137            },
138            _ => {
139                let error_body = serde_json::json!({
140                    "error": "server_error",
141                    "error_description": self.to_string()
142                });
143                actix_web::HttpResponse::build(self.status_code())
144                    .content_type("application/json")
145                    .body(error_body.to_string())
146            }
147        }
148    }
149}