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}