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
97#[derive(Error, Debug)]
98pub enum DiscoveryError {
99    #[error("Failed to discover OIDC provider metadata after {attempts} attempts: {message}")]
100    DiscoveryFailed { attempts: u32, message: String },
101    #[error("Invalid issuer URL: {0}")]
102    InvalidIssuerUrl(String),
103}
104
105impl actix_web::ResponseError for OidcError {
106    fn status_code(&self) -> actix_web::http::StatusCode {
107        match self {
108            OidcError::RequestErrorWithDetails { status_code, .. } => {
109                if *status_code > 0 {
110                    actix_web::http::StatusCode::from_u16(*status_code)
111                        .unwrap_or(actix_web::http::StatusCode::INTERNAL_SERVER_ERROR)
112                } else {
113                    actix_web::http::StatusCode::INTERNAL_SERVER_ERROR
114                }
115            },
116            OidcError::CibaAuthenticationPending => actix_web::http::StatusCode::BAD_REQUEST,
117            OidcError::CibaStatusBadRequest(_) => actix_web::http::StatusCode::BAD_REQUEST,
118            OidcError::CibaStatusCheckFailed(_) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
119            OidcError::TokenIntrospectionFailed(_) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
120            OidcError::TokenNotActive => actix_web::http::StatusCode::UNAUTHORIZED,
121            OidcError::TokenIdentificationFailed(_) => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
122            _ => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
123        }
124    }
125
126    fn error_response(&self) -> actix_web::HttpResponse {
127        match self {
128            OidcError::RequestErrorWithDetails { error, error_description, .. } => {
129                let error_body = serde_json::json!({
130                    "error": error,
131                    "error_description": error_description
132                });
133                actix_web::HttpResponse::build(self.status_code())
134                    .content_type("application/json")
135                    .body(error_body.to_string())
136            },
137            OidcError::CibaAuthenticationPending => {
138                let error_body = serde_json::json!({
139                    "error": "authorization_pending",
140                    "error_description": "Authorization request is still pending as the end-user hasn't yet completed the user interaction steps"
141                });
142                actix_web::HttpResponse::build(self.status_code())
143                    .content_type("application/json")
144                    .body(error_body.to_string())
145            },
146            _ => {
147                let error_body = serde_json::json!({
148                    "error": "server_error",
149                    "error_description": self.to_string()
150                });
151                actix_web::HttpResponse::build(self.status_code())
152                    .content_type("application/json")
153                    .body(error_body.to_string())
154            }
155        }
156    }
157}