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}