1use std::borrow::Cow;
10
11use serde::{Deserialize, Serialize};
12use serde_with::{DeserializeFromStr, SerializeDisplay};
13
14#[derive(Debug, Serialize, Deserialize, Clone)]
19pub struct ClientError {
20 pub error: ClientErrorCode,
22
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub error_description: Option<Cow<'static, str>>,
26}
27
28impl ClientError {
29 #[must_use]
31 pub const fn new(error: ClientErrorCode, error_description: &'static str) -> Self {
32 Self {
33 error,
34 error_description: Some(Cow::Borrowed(error_description)),
35 }
36 }
37
38 #[must_use]
40 pub fn with_description(mut self, description: String) -> Self {
41 self.error_description = Some(Cow::Owned(description));
42 self
43 }
44}
45
46impl From<ClientErrorCode> for ClientError {
47 fn from(error: ClientErrorCode) -> Self {
48 let desc = error.default_description();
49 Self::new(error, desc)
50 }
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, SerializeDisplay, DeserializeFromStr)]
55pub enum ClientErrorCode {
56 InvalidRequest,
64
65 InvalidClient,
72
73 InvalidGrant,
82
83 UnauthorizedClient,
90
91 UnsupportedGrantType,
98
99 AccessDenied,
105
106 UnsupportedResponseType,
113
114 InvalidScope,
121
122 ServerError,
129
130 TemporarilyUnavailable,
137
138 InteractionRequired,
145
146 LoginRequired,
152
153 AccountSelectionRequired,
160
161 ConsentRequired,
167
168 InvalidRequestUri,
175
176 InvalidRequestObject,
182
183 RequestNotSupported,
190
191 RequestUriNotSupported,
198
199 RegistrationNotSupported,
206
207 InvalidRedirectUri,
213
214 InvalidClientMetadata,
221
222 AuthorizationPending,
236
237 SlowDown,
246
247 ExpiredToken,
258
259 UnsupportedTokenType,
267
268 Unknown(String),
270}
271
272impl core::fmt::Display for ClientErrorCode {
273 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
274 match self {
275 ClientErrorCode::InvalidRequest => f.write_str("invalid_request"),
276 ClientErrorCode::InvalidClient => f.write_str("invalid_client"),
277 ClientErrorCode::InvalidGrant => f.write_str("invalid_grant"),
278 ClientErrorCode::UnauthorizedClient => f.write_str("unauthorized_client"),
279 ClientErrorCode::UnsupportedGrantType => f.write_str("unsupported_grant_type"),
280 ClientErrorCode::AccessDenied => f.write_str("access_denied"),
281 ClientErrorCode::UnsupportedResponseType => f.write_str("unsupported_response_type"),
282 ClientErrorCode::InvalidScope => f.write_str("invalid_scope"),
283 ClientErrorCode::ServerError => f.write_str("server_error"),
284 ClientErrorCode::TemporarilyUnavailable => f.write_str("temporarily_unavailable"),
285 ClientErrorCode::InteractionRequired => f.write_str("interaction_required"),
286 ClientErrorCode::LoginRequired => f.write_str("login_required"),
287 ClientErrorCode::AccountSelectionRequired => f.write_str("account_selection_required"),
288 ClientErrorCode::ConsentRequired => f.write_str("consent_required"),
289 ClientErrorCode::InvalidRequestUri => f.write_str("invalid_request_uri"),
290 ClientErrorCode::InvalidRequestObject => f.write_str("invalid_request_object"),
291 ClientErrorCode::RequestNotSupported => f.write_str("request_not_supported"),
292 ClientErrorCode::RequestUriNotSupported => f.write_str("request_uri_not_supported"),
293 ClientErrorCode::RegistrationNotSupported => f.write_str("registration_not_supported"),
294 ClientErrorCode::InvalidRedirectUri => f.write_str("invalid_redirect_uri"),
295 ClientErrorCode::InvalidClientMetadata => f.write_str("invalid_client_metadata"),
296 ClientErrorCode::AuthorizationPending => f.write_str("authorization_pending"),
297 ClientErrorCode::SlowDown => f.write_str("slow_down"),
298 ClientErrorCode::ExpiredToken => f.write_str("expired_token"),
299 ClientErrorCode::UnsupportedTokenType => f.write_str("unsupported_token_type"),
300 ClientErrorCode::Unknown(value) => f.write_str(value),
301 }
302 }
303}
304
305impl core::str::FromStr for ClientErrorCode {
306 type Err = core::convert::Infallible;
307
308 fn from_str(s: &str) -> Result<Self, Self::Err> {
309 match s {
310 "invalid_request" => Ok(ClientErrorCode::InvalidRequest),
311 "invalid_client" => Ok(ClientErrorCode::InvalidClient),
312 "invalid_grant" => Ok(ClientErrorCode::InvalidGrant),
313 "unauthorized_client" => Ok(ClientErrorCode::UnauthorizedClient),
314 "unsupported_grant_type" => Ok(ClientErrorCode::UnsupportedGrantType),
315 "access_denied" => Ok(ClientErrorCode::AccessDenied),
316 "unsupported_response_type" => Ok(ClientErrorCode::UnsupportedResponseType),
317 "invalid_scope" => Ok(ClientErrorCode::InvalidScope),
318 "server_error" => Ok(ClientErrorCode::ServerError),
319 "temporarily_unavailable" => Ok(ClientErrorCode::TemporarilyUnavailable),
320 "interaction_required" => Ok(ClientErrorCode::InteractionRequired),
321 "login_required" => Ok(ClientErrorCode::LoginRequired),
322 "account_selection_required" => Ok(ClientErrorCode::AccountSelectionRequired),
323 "consent_required" => Ok(ClientErrorCode::ConsentRequired),
324 "invalid_request_uri" => Ok(ClientErrorCode::InvalidRequestUri),
325 "invalid_request_object" => Ok(ClientErrorCode::InvalidRequestObject),
326 "request_not_supported" => Ok(ClientErrorCode::RequestNotSupported),
327 "request_uri_not_supported" => Ok(ClientErrorCode::RequestUriNotSupported),
328 "registration_not_supported" => Ok(ClientErrorCode::RegistrationNotSupported),
329 "invalid_redirect_uri" => Ok(ClientErrorCode::InvalidRedirectUri),
330 "invalid_client_metadata" => Ok(ClientErrorCode::InvalidClientMetadata),
331 "authorization_pending" => Ok(ClientErrorCode::AuthorizationPending),
332 "slow_down" => Ok(ClientErrorCode::SlowDown),
333 "expired_token" => Ok(ClientErrorCode::ExpiredToken),
334 "unsupported_token_type" => Ok(ClientErrorCode::UnsupportedTokenType),
335 _ => Ok(ClientErrorCode::Unknown(s.to_owned())),
336 }
337 }
338}
339
340impl ClientErrorCode {
341 #[must_use]
345 pub fn default_description(&self) -> &'static str {
346 match self {
347 ClientErrorCode::InvalidRequest => {
348 "The request is missing a required parameter, includes an \
349 invalid parameter value, includes a parameter more than once, \
350 or is otherwise malformed."
351 }
352 ClientErrorCode::InvalidClient => "Client authentication failed.",
353 ClientErrorCode::InvalidGrant => {
354 "The provided access grant is invalid, expired, or revoked."
355 }
356 ClientErrorCode::UnauthorizedClient => {
357 "The client is not authorized to request an access token using this method."
358 }
359 ClientErrorCode::UnsupportedGrantType => {
360 "The authorization grant type is not supported by the authorization server."
361 }
362 ClientErrorCode::AccessDenied => {
363 "The resource owner or authorization server denied the request."
364 }
365 ClientErrorCode::UnsupportedResponseType => {
366 "The authorization server does not support obtaining an access \
367 token using this method."
368 }
369 ClientErrorCode::InvalidScope => {
370 "The requested scope is invalid, unknown, or malformed."
371 }
372 ClientErrorCode::ServerError => {
373 "The authorization server encountered an unexpected condition \
374 that prevented it from fulfilling the request."
375 }
376 ClientErrorCode::TemporarilyUnavailable => {
377 "The authorization server is currently unable to handle the request \
378 due to a temporary overloading or maintenance of the server."
379 }
380 ClientErrorCode::InteractionRequired => {
381 "The Authorization Server requires End-User interaction of some form to proceed."
382 }
383 ClientErrorCode::LoginRequired => {
384 "The Authorization Server requires End-User authentication."
385 }
386 ClientErrorCode::AccountSelectionRequired => {
387 "The End-User is required to select a session at the Authorization Server."
388 }
389 ClientErrorCode::ConsentRequired => {
390 "The Authorization Server requires End-User consent."
391 }
392 ClientErrorCode::InvalidRequestUri => {
393 "The request_uri in the Authorization Request returns an error \
394 or contains invalid data."
395 }
396 ClientErrorCode::InvalidRequestObject => {
397 "The request parameter contains an invalid Request Object."
398 }
399 ClientErrorCode::RequestNotSupported => {
400 "The provider does not support use of the request parameter."
401 }
402 ClientErrorCode::RequestUriNotSupported => {
403 "The provider does not support use of the request_uri parameter."
404 }
405 ClientErrorCode::RegistrationNotSupported => {
406 "The provider does not support use of the registration parameter."
407 }
408 ClientErrorCode::InvalidRedirectUri => {
409 "The value of one or more redirection URIs is invalid."
410 }
411 ClientErrorCode::InvalidClientMetadata => {
412 "The value of one of the client metadata fields is invalid"
413 }
414 ClientErrorCode::AuthorizationPending => "The authorization request is still pending",
415 ClientErrorCode::SlowDown => {
416 "The interval must be increased by 5 seconds for this and all subsequent requests"
417 }
418 ClientErrorCode::ExpiredToken => {
419 "The \"device_code\" has expired, and the device authorization session has concluded"
420 }
421 ClientErrorCode::UnsupportedTokenType => {
422 "The authorization server does not support the revocation of the presented token type."
423 }
424 ClientErrorCode::Unknown(_) => "",
425 }
426 }
427}
428
429#[cfg(test)]
430mod tests {
431 use super::*;
432
433 #[test]
434 fn serialize_client_error_code() {
435 assert_eq!(
436 serde_json::to_string(&ClientErrorCode::InvalidRequest).unwrap(),
437 "\"invalid_request\""
438 );
439 assert_eq!(
440 serde_json::to_string(&ClientErrorCode::InvalidClient).unwrap(),
441 "\"invalid_client\""
442 );
443 assert_eq!(
444 serde_json::to_string(&ClientErrorCode::InvalidGrant).unwrap(),
445 "\"invalid_grant\""
446 );
447 assert_eq!(
448 serde_json::to_string(&ClientErrorCode::UnauthorizedClient).unwrap(),
449 "\"unauthorized_client\""
450 );
451 assert_eq!(
452 serde_json::to_string(&ClientErrorCode::UnsupportedGrantType).unwrap(),
453 "\"unsupported_grant_type\""
454 );
455 assert_eq!(
456 serde_json::to_string(&ClientErrorCode::AccessDenied).unwrap(),
457 "\"access_denied\""
458 );
459 assert_eq!(
460 serde_json::to_string(&ClientErrorCode::UnsupportedResponseType).unwrap(),
461 "\"unsupported_response_type\""
462 );
463 assert_eq!(
464 serde_json::to_string(&ClientErrorCode::InvalidScope).unwrap(),
465 "\"invalid_scope\""
466 );
467 assert_eq!(
468 serde_json::to_string(&ClientErrorCode::ServerError).unwrap(),
469 "\"server_error\""
470 );
471 assert_eq!(
472 serde_json::to_string(&ClientErrorCode::TemporarilyUnavailable).unwrap(),
473 "\"temporarily_unavailable\""
474 );
475 assert_eq!(
476 serde_json::to_string(&ClientErrorCode::InteractionRequired).unwrap(),
477 "\"interaction_required\""
478 );
479 assert_eq!(
480 serde_json::to_string(&ClientErrorCode::LoginRequired).unwrap(),
481 "\"login_required\""
482 );
483 assert_eq!(
484 serde_json::to_string(&ClientErrorCode::AccountSelectionRequired).unwrap(),
485 "\"account_selection_required\""
486 );
487 assert_eq!(
488 serde_json::to_string(&ClientErrorCode::ConsentRequired).unwrap(),
489 "\"consent_required\""
490 );
491 assert_eq!(
492 serde_json::to_string(&ClientErrorCode::InvalidRequestUri).unwrap(),
493 "\"invalid_request_uri\""
494 );
495 assert_eq!(
496 serde_json::to_string(&ClientErrorCode::InvalidRequestObject).unwrap(),
497 "\"invalid_request_object\""
498 );
499 assert_eq!(
500 serde_json::to_string(&ClientErrorCode::RequestNotSupported).unwrap(),
501 "\"request_not_supported\""
502 );
503 assert_eq!(
504 serde_json::to_string(&ClientErrorCode::RequestUriNotSupported).unwrap(),
505 "\"request_uri_not_supported\""
506 );
507 assert_eq!(
508 serde_json::to_string(&ClientErrorCode::RegistrationNotSupported).unwrap(),
509 "\"registration_not_supported\""
510 );
511 assert_eq!(
512 serde_json::to_string(&ClientErrorCode::InvalidRedirectUri).unwrap(),
513 "\"invalid_redirect_uri\""
514 );
515 assert_eq!(
516 serde_json::to_string(&ClientErrorCode::InvalidClientMetadata).unwrap(),
517 "\"invalid_client_metadata\""
518 );
519
520 assert_eq!(
521 serde_json::to_string(&ClientErrorCode::Unknown("unknown_error_code".to_owned()))
522 .unwrap(),
523 "\"unknown_error_code\""
524 );
525 }
526
527 #[test]
528 fn deserialize_client_error_code() {
529 assert_eq!(
530 serde_json::from_str::<ClientErrorCode>("\"invalid_request\"").unwrap(),
531 ClientErrorCode::InvalidRequest
532 );
533 assert_eq!(
534 serde_json::from_str::<ClientErrorCode>("\"invalid_client\"").unwrap(),
535 ClientErrorCode::InvalidClient
536 );
537 assert_eq!(
538 serde_json::from_str::<ClientErrorCode>("\"invalid_grant\"").unwrap(),
539 ClientErrorCode::InvalidGrant
540 );
541 assert_eq!(
542 serde_json::from_str::<ClientErrorCode>("\"unauthorized_client\"").unwrap(),
543 ClientErrorCode::UnauthorizedClient
544 );
545 assert_eq!(
546 serde_json::from_str::<ClientErrorCode>("\"unsupported_grant_type\"").unwrap(),
547 ClientErrorCode::UnsupportedGrantType
548 );
549 assert_eq!(
550 serde_json::from_str::<ClientErrorCode>("\"access_denied\"").unwrap(),
551 ClientErrorCode::AccessDenied
552 );
553 assert_eq!(
554 serde_json::from_str::<ClientErrorCode>("\"unsupported_response_type\"").unwrap(),
555 ClientErrorCode::UnsupportedResponseType
556 );
557 assert_eq!(
558 serde_json::from_str::<ClientErrorCode>("\"invalid_scope\"").unwrap(),
559 ClientErrorCode::InvalidScope
560 );
561 assert_eq!(
562 serde_json::from_str::<ClientErrorCode>("\"server_error\"").unwrap(),
563 ClientErrorCode::ServerError
564 );
565 assert_eq!(
566 serde_json::from_str::<ClientErrorCode>("\"temporarily_unavailable\"").unwrap(),
567 ClientErrorCode::TemporarilyUnavailable
568 );
569 assert_eq!(
570 serde_json::from_str::<ClientErrorCode>("\"interaction_required\"").unwrap(),
571 ClientErrorCode::InteractionRequired
572 );
573 assert_eq!(
574 serde_json::from_str::<ClientErrorCode>("\"login_required\"").unwrap(),
575 ClientErrorCode::LoginRequired
576 );
577 assert_eq!(
578 serde_json::from_str::<ClientErrorCode>("\"account_selection_required\"").unwrap(),
579 ClientErrorCode::AccountSelectionRequired
580 );
581 assert_eq!(
582 serde_json::from_str::<ClientErrorCode>("\"consent_required\"").unwrap(),
583 ClientErrorCode::ConsentRequired
584 );
585 assert_eq!(
586 serde_json::from_str::<ClientErrorCode>("\"invalid_request_uri\"").unwrap(),
587 ClientErrorCode::InvalidRequestUri
588 );
589 assert_eq!(
590 serde_json::from_str::<ClientErrorCode>("\"invalid_request_object\"").unwrap(),
591 ClientErrorCode::InvalidRequestObject
592 );
593 assert_eq!(
594 serde_json::from_str::<ClientErrorCode>("\"request_not_supported\"").unwrap(),
595 ClientErrorCode::RequestNotSupported
596 );
597 assert_eq!(
598 serde_json::from_str::<ClientErrorCode>("\"request_uri_not_supported\"").unwrap(),
599 ClientErrorCode::RequestUriNotSupported
600 );
601 assert_eq!(
602 serde_json::from_str::<ClientErrorCode>("\"registration_not_supported\"").unwrap(),
603 ClientErrorCode::RegistrationNotSupported
604 );
605 assert_eq!(
606 serde_json::from_str::<ClientErrorCode>("\"invalid_redirect_uri\"").unwrap(),
607 ClientErrorCode::InvalidRedirectUri
608 );
609 assert_eq!(
610 serde_json::from_str::<ClientErrorCode>("\"invalid_client_metadata\"").unwrap(),
611 ClientErrorCode::InvalidClientMetadata
612 );
613
614 assert_eq!(
615 serde_json::from_str::<ClientErrorCode>("\"unknown_error_code\"").unwrap(),
616 ClientErrorCode::Unknown("unknown_error_code".to_owned())
617 );
618 }
619}