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