1use std::{collections::HashSet, fmt, hash::Hash, num::NonZeroU32};
20
21use chrono::{DateTime, Duration, Utc};
22use language_tags::LanguageTag;
23use mas_iana::oauth::{OAuthAccessTokenType, OAuthTokenTypeHint};
24use serde::{Deserialize, Serialize};
25use serde_with::{
26 formats::SpaceSeparator, serde_as, skip_serializing_none, DeserializeFromStr, DisplayFromStr,
27 DurationSeconds, SerializeDisplay, StringWithSeparator, TimestampSeconds,
28};
29use url::Url;
30
31use crate::{response_type::ResponseType, scope::Scope};
32
33#[derive(
40 Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, SerializeDisplay, DeserializeFromStr,
41)]
42#[non_exhaustive]
43pub enum ResponseMode {
44 Query,
47
48 Fragment,
51
52 FormPost,
60
61 Unknown(String),
63}
64
65impl core::fmt::Display for ResponseMode {
66 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
67 match self {
68 ResponseMode::Query => f.write_str("query"),
69 ResponseMode::Fragment => f.write_str("fragment"),
70 ResponseMode::FormPost => f.write_str("form_post"),
71 ResponseMode::Unknown(s) => f.write_str(s),
72 }
73 }
74}
75
76impl core::str::FromStr for ResponseMode {
77 type Err = core::convert::Infallible;
78
79 fn from_str(s: &str) -> Result<Self, Self::Err> {
80 match s {
81 "query" => Ok(ResponseMode::Query),
82 "fragment" => Ok(ResponseMode::Fragment),
83 "form_post" => Ok(ResponseMode::FormPost),
84 s => Ok(ResponseMode::Unknown(s.to_owned())),
85 }
86 }
87}
88
89#[derive(
94 Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, SerializeDisplay, DeserializeFromStr,
95)]
96#[non_exhaustive]
97pub enum Display {
98 Page,
103
104 Popup,
107
108 Touch,
111
112 Wap,
115
116 Unknown(String),
118}
119
120impl core::fmt::Display for Display {
121 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
122 match self {
123 Display::Page => f.write_str("page"),
124 Display::Popup => f.write_str("popup"),
125 Display::Touch => f.write_str("touch"),
126 Display::Wap => f.write_str("wap"),
127 Display::Unknown(s) => f.write_str(s),
128 }
129 }
130}
131
132impl core::str::FromStr for Display {
133 type Err = core::convert::Infallible;
134
135 fn from_str(s: &str) -> Result<Self, Self::Err> {
136 match s {
137 "page" => Ok(Display::Page),
138 "popup" => Ok(Display::Popup),
139 "touch" => Ok(Display::Touch),
140 "wap" => Ok(Display::Wap),
141 s => Ok(Display::Unknown(s.to_owned())),
142 }
143 }
144}
145
146impl Default for Display {
147 fn default() -> Self {
148 Self::Page
149 }
150}
151
152#[derive(
157 Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, SerializeDisplay, DeserializeFromStr,
158)]
159#[non_exhaustive]
160pub enum Prompt {
161 None,
164
165 Login,
168
169 Consent,
172
173 SelectAccount,
180
181 Create,
186
187 Unknown(String),
189}
190
191impl core::fmt::Display for Prompt {
192 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
193 match self {
194 Prompt::None => f.write_str("none"),
195 Prompt::Login => f.write_str("login"),
196 Prompt::Consent => f.write_str("consent"),
197 Prompt::SelectAccount => f.write_str("select_account"),
198 Prompt::Create => f.write_str("create"),
199 Prompt::Unknown(s) => f.write_str(s),
200 }
201 }
202}
203
204impl core::str::FromStr for Prompt {
205 type Err = core::convert::Infallible;
206
207 fn from_str(s: &str) -> Result<Self, Self::Err> {
208 match s {
209 "none" => Ok(Prompt::None),
210 "login" => Ok(Prompt::Login),
211 "consent" => Ok(Prompt::Consent),
212 "select_account" => Ok(Prompt::SelectAccount),
213 "create" => Ok(Prompt::Create),
214 s => Ok(Prompt::Unknown(s.to_owned())),
215 }
216 }
217}
218
219#[skip_serializing_none]
223#[serde_as]
224#[derive(Serialize, Deserialize, Clone)]
225pub struct AuthorizationRequest {
226 pub response_type: ResponseType,
229
230 pub client_id: String,
232
233 pub redirect_uri: Option<Url>,
240
241 pub scope: Scope,
245
246 pub state: Option<String>,
249
250 pub response_mode: Option<ResponseMode>,
257
258 pub nonce: Option<String>,
261
262 pub display: Option<Display>,
265
266 #[serde_as(as = "Option<StringWithSeparator::<SpaceSeparator, Prompt>>")]
271 #[serde(default)]
272 pub prompt: Option<Vec<Prompt>>,
273
274 #[serde(default)]
277 #[serde_as(as = "Option<DisplayFromStr>")]
278 pub max_age: Option<NonZeroU32>,
279
280 #[serde_as(as = "Option<StringWithSeparator::<SpaceSeparator, LanguageTag>>")]
282 #[serde(default)]
283 pub ui_locales: Option<Vec<LanguageTag>>,
284
285 pub id_token_hint: Option<String>,
289
290 pub login_hint: Option<String>,
293
294 #[serde_as(as = "Option<StringWithSeparator::<SpaceSeparator, String>>")]
296 #[serde(default)]
297 pub acr_values: Option<HashSet<String>>,
298
299 pub request: Option<String>,
304
305 pub request_uri: Option<Url>,
311
312 pub registration: Option<String>,
317}
318
319impl AuthorizationRequest {
320 #[must_use]
322 pub fn new(response_type: ResponseType, client_id: String, scope: Scope) -> Self {
323 Self {
324 response_type,
325 client_id,
326 redirect_uri: None,
327 scope,
328 state: None,
329 response_mode: None,
330 nonce: None,
331 display: None,
332 prompt: None,
333 max_age: None,
334 ui_locales: None,
335 id_token_hint: None,
336 login_hint: None,
337 acr_values: None,
338 request: None,
339 request_uri: None,
340 registration: None,
341 }
342 }
343}
344
345impl fmt::Debug for AuthorizationRequest {
346 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347 f.debug_struct("AuthorizationRequest")
348 .field("response_type", &self.response_type)
349 .field("redirect_uri", &self.redirect_uri)
350 .field("scope", &self.scope)
351 .field("response_mode", &self.response_mode)
352 .field("display", &self.display)
353 .field("prompt", &self.prompt)
354 .field("max_age", &self.max_age)
355 .field("ui_locales", &self.ui_locales)
356 .field("login_hint", &self.login_hint)
357 .field("acr_values", &self.acr_values)
358 .field("request", &self.request)
359 .field("request_uri", &self.request_uri)
360 .field("registration", &self.registration)
361 .finish_non_exhaustive()
362 }
363}
364
365#[skip_serializing_none]
369#[serde_as]
370#[derive(Serialize, Deserialize, Default, Clone)]
371pub struct AuthorizationResponse {
372 pub code: Option<String>,
374
375 pub access_token: Option<String>,
377
378 pub token_type: Option<OAuthAccessTokenType>,
380
381 pub id_token: Option<String>,
383
384 #[serde_as(as = "Option<DurationSeconds<i64>>")]
386 pub expires_in: Option<Duration>,
387}
388
389impl fmt::Debug for AuthorizationResponse {
390 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391 f.debug_struct("AuthorizationResponse")
392 .field("token_type", &self.token_type)
393 .field("id_token", &self.id_token)
394 .field("expires_in", &self.expires_in)
395 .finish_non_exhaustive()
396 }
397}
398
399#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
403pub struct DeviceAuthorizationRequest {
404 pub scope: Option<Scope>,
406}
407
408pub const DEFAULT_DEVICE_AUTHORIZATION_INTERVAL: Duration = Duration::microseconds(5 * 1000 * 1000);
411
412#[serde_as]
416#[skip_serializing_none]
417#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
418pub struct DeviceAuthorizationResponse {
419 pub device_code: String,
421
422 pub user_code: String,
424
425 pub verification_uri: Url,
430
431 pub verification_uri_complete: Option<Url>,
435
436 #[serde_as(as = "DurationSeconds<i64>")]
438 pub expires_in: Duration,
439
440 #[serde_as(as = "Option<DurationSeconds<i64>>")]
445 pub interval: Option<Duration>,
446}
447
448impl DeviceAuthorizationResponse {
449 #[must_use]
454 pub fn interval(&self) -> Duration {
455 self.interval
456 .unwrap_or(DEFAULT_DEVICE_AUTHORIZATION_INTERVAL)
457 }
458}
459
460impl fmt::Debug for DeviceAuthorizationResponse {
461 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
462 f.debug_struct("DeviceAuthorizationResponse")
463 .field("verification_uri", &self.verification_uri)
464 .field("expires_in", &self.expires_in)
465 .field("interval", &self.interval)
466 .finish_non_exhaustive()
467 }
468}
469
470#[skip_serializing_none]
475#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
476pub struct AuthorizationCodeGrant {
477 pub code: String,
480
481 pub redirect_uri: Option<Url>,
486
487 pub code_verifier: Option<String>,
491}
492
493impl fmt::Debug for AuthorizationCodeGrant {
494 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
495 f.debug_struct("AuthorizationCodeGrant")
496 .field("redirect_uri", &self.redirect_uri)
497 .finish_non_exhaustive()
498 }
499}
500
501#[skip_serializing_none]
506#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
507pub struct RefreshTokenGrant {
508 pub refresh_token: String,
510
511 pub scope: Option<Scope>,
517}
518
519impl fmt::Debug for RefreshTokenGrant {
520 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
521 f.debug_struct("RefreshTokenGrant")
522 .field("scope", &self.scope)
523 .finish_non_exhaustive()
524 }
525}
526
527#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
532pub struct ClientCredentialsGrant {
533 pub scope: Option<Scope>,
535}
536
537#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
542pub struct DeviceCodeGrant {
543 pub device_code: String,
545}
546
547impl fmt::Debug for DeviceCodeGrant {
548 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
549 f.debug_struct("DeviceCodeGrant").finish_non_exhaustive()
550 }
551}
552
553#[derive(
555 Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, SerializeDisplay, DeserializeFromStr,
556)]
557pub enum GrantType {
558 AuthorizationCode,
560
561 RefreshToken,
563
564 Implicit,
566
567 ClientCredentials,
569
570 Password,
572
573 DeviceCode,
575
576 JwtBearer,
578
579 ClientInitiatedBackchannelAuthentication,
581
582 Unknown(String),
584}
585
586impl core::fmt::Display for GrantType {
587 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
588 match self {
589 GrantType::AuthorizationCode => f.write_str("authorization_code"),
590 GrantType::RefreshToken => f.write_str("refresh_token"),
591 GrantType::Implicit => f.write_str("implicit"),
592 GrantType::ClientCredentials => f.write_str("client_credentials"),
593 GrantType::Password => f.write_str("password"),
594 GrantType::DeviceCode => f.write_str("urn:ietf:params:oauth:grant-type:device_code"),
595 GrantType::JwtBearer => f.write_str("urn:ietf:params:oauth:grant-type:jwt-bearer"),
596 GrantType::ClientInitiatedBackchannelAuthentication => {
597 f.write_str("urn:openid:params:grant-type:ciba")
598 }
599 GrantType::Unknown(s) => f.write_str(s),
600 }
601 }
602}
603
604impl core::str::FromStr for GrantType {
605 type Err = core::convert::Infallible;
606
607 fn from_str(s: &str) -> Result<Self, Self::Err> {
608 match s {
609 "authorization_code" => Ok(GrantType::AuthorizationCode),
610 "refresh_token" => Ok(GrantType::RefreshToken),
611 "implicit" => Ok(GrantType::Implicit),
612 "client_credentials" => Ok(GrantType::ClientCredentials),
613 "password" => Ok(GrantType::Password),
614 "urn:ietf:params:oauth:grant-type:device_code" => Ok(GrantType::DeviceCode),
615 "urn:ietf:params:oauth:grant-type:jwt-bearer" => Ok(GrantType::JwtBearer),
616 "urn:openid:params:grant-type:ciba" => {
617 Ok(GrantType::ClientInitiatedBackchannelAuthentication)
618 }
619 s => Ok(GrantType::Unknown(s.to_owned())),
620 }
621 }
622}
623
624#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
628#[serde(tag = "grant_type", rename_all = "snake_case")]
629#[non_exhaustive]
630pub enum AccessTokenRequest {
631 AuthorizationCode(AuthorizationCodeGrant),
633
634 RefreshToken(RefreshTokenGrant),
636
637 ClientCredentials(ClientCredentialsGrant),
639
640 #[serde(rename = "urn:ietf:params:oauth:grant-type:device_code")]
642 DeviceCode(DeviceCodeGrant),
643
644 #[serde(skip_serializing, other)]
646 Unsupported,
647}
648
649#[serde_as]
653#[skip_serializing_none]
654#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
655pub struct AccessTokenResponse {
656 pub access_token: String,
658
659 pub refresh_token: Option<String>,
661
662 pub id_token: Option<String>,
665
666 pub token_type: OAuthAccessTokenType,
668
669 #[serde_as(as = "Option<DurationSeconds<i64>>")]
671 pub expires_in: Option<Duration>,
672
673 pub scope: Option<Scope>,
675}
676
677impl AccessTokenResponse {
678 #[must_use]
680 pub fn new(access_token: String) -> AccessTokenResponse {
681 AccessTokenResponse {
682 access_token,
683 refresh_token: None,
684 id_token: None,
685 token_type: OAuthAccessTokenType::Bearer,
686 expires_in: None,
687 scope: None,
688 }
689 }
690
691 #[must_use]
693 pub fn with_refresh_token(mut self, refresh_token: String) -> Self {
694 self.refresh_token = Some(refresh_token);
695 self
696 }
697
698 #[must_use]
700 pub fn with_id_token(mut self, id_token: String) -> Self {
701 self.id_token = Some(id_token);
702 self
703 }
704
705 #[must_use]
707 pub fn with_scope(mut self, scope: Scope) -> Self {
708 self.scope = Some(scope);
709 self
710 }
711
712 #[must_use]
714 pub fn with_expires_in(mut self, expires_in: Duration) -> Self {
715 self.expires_in = Some(expires_in);
716 self
717 }
718}
719
720impl fmt::Debug for AccessTokenResponse {
721 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
722 f.debug_struct("AccessTokenResponse")
723 .field("token_type", &self.token_type)
724 .field("expires_in", &self.expires_in)
725 .field("scope", &self.scope)
726 .finish_non_exhaustive()
727 }
728}
729
730#[skip_serializing_none]
734#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
735pub struct IntrospectionRequest {
736 pub token: String,
738
739 pub token_type_hint: Option<OAuthTokenTypeHint>,
741}
742
743impl fmt::Debug for IntrospectionRequest {
744 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
745 f.debug_struct("IntrospectionRequest")
746 .field("token_type_hint", &self.token_type_hint)
747 .finish_non_exhaustive()
748 }
749}
750
751#[serde_as]
755#[skip_serializing_none]
756#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
757pub struct IntrospectionResponse {
758 pub active: bool,
760
761 pub scope: Option<Scope>,
763
764 pub client_id: Option<String>,
766
767 pub username: Option<String>,
770
771 pub token_type: Option<OAuthTokenTypeHint>,
773
774 #[serde_as(as = "Option<TimestampSeconds>")]
776 pub exp: Option<DateTime<Utc>>,
777
778 #[serde_as(as = "Option<TimestampSeconds>")]
780 pub iat: Option<DateTime<Utc>>,
781
782 #[serde_as(as = "Option<TimestampSeconds>")]
784 pub nbf: Option<DateTime<Utc>>,
785
786 pub sub: Option<String>,
788
789 pub aud: Option<String>,
791
792 pub iss: Option<String>,
794
795 pub jti: Option<String>,
797}
798
799#[skip_serializing_none]
803#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
804pub struct RevocationRequest {
805 pub token: String,
807
808 pub token_type_hint: Option<OAuthTokenTypeHint>,
810}
811
812impl fmt::Debug for RevocationRequest {
813 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
814 f.debug_struct("RevocationRequest")
815 .field("token_type_hint", &self.token_type_hint)
816 .finish_non_exhaustive()
817 }
818}
819
820#[serde_as]
827#[skip_serializing_none]
828#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
829pub struct PushedAuthorizationResponse {
830 pub request_uri: String,
832
833 #[serde_as(as = "DurationSeconds<i64>")]
835 pub expires_in: Duration,
836}
837
838#[cfg(test)]
839mod tests {
840 use serde_json::json;
841
842 use super::*;
843 use crate::{scope::OPENID, test_utils::assert_serde_json};
844
845 #[test]
846 fn serde_refresh_token_grant() {
847 let expected = json!({
848 "grant_type": "refresh_token",
849 "refresh_token": "abcd",
850 "scope": "openid",
851 });
852
853 let scope: Option<Scope> = Some(vec![OPENID].into_iter().collect());
857
858 let req = AccessTokenRequest::RefreshToken(RefreshTokenGrant {
859 refresh_token: "abcd".into(),
860 scope,
861 });
862
863 assert_serde_json(&req, expected);
864 }
865
866 #[test]
867 fn serde_authorization_code_grant() {
868 let expected = json!({
869 "grant_type": "authorization_code",
870 "code": "abcd",
871 "redirect_uri": "https://example.com/redirect",
872 });
873
874 let req = AccessTokenRequest::AuthorizationCode(AuthorizationCodeGrant {
875 code: "abcd".into(),
876 redirect_uri: Some("https://example.com/redirect".parse().unwrap()),
877 code_verifier: None,
878 });
879
880 assert_serde_json(&req, expected);
881 }
882
883 #[test]
884 fn serialize_grant_type() {
885 assert_eq!(
886 serde_json::to_string(&GrantType::AuthorizationCode).unwrap(),
887 "\"authorization_code\""
888 );
889 assert_eq!(
890 serde_json::to_string(&GrantType::RefreshToken).unwrap(),
891 "\"refresh_token\""
892 );
893 assert_eq!(
894 serde_json::to_string(&GrantType::Implicit).unwrap(),
895 "\"implicit\""
896 );
897 assert_eq!(
898 serde_json::to_string(&GrantType::ClientCredentials).unwrap(),
899 "\"client_credentials\""
900 );
901 assert_eq!(
902 serde_json::to_string(&GrantType::Password).unwrap(),
903 "\"password\""
904 );
905 assert_eq!(
906 serde_json::to_string(&GrantType::DeviceCode).unwrap(),
907 "\"urn:ietf:params:oauth:grant-type:device_code\""
908 );
909 assert_eq!(
910 serde_json::to_string(&GrantType::ClientInitiatedBackchannelAuthentication).unwrap(),
911 "\"urn:openid:params:grant-type:ciba\""
912 );
913 }
914
915 #[test]
916 fn deserialize_grant_type() {
917 assert_eq!(
918 serde_json::from_str::<GrantType>("\"authorization_code\"").unwrap(),
919 GrantType::AuthorizationCode
920 );
921 assert_eq!(
922 serde_json::from_str::<GrantType>("\"refresh_token\"").unwrap(),
923 GrantType::RefreshToken
924 );
925 assert_eq!(
926 serde_json::from_str::<GrantType>("\"implicit\"").unwrap(),
927 GrantType::Implicit
928 );
929 assert_eq!(
930 serde_json::from_str::<GrantType>("\"client_credentials\"").unwrap(),
931 GrantType::ClientCredentials
932 );
933 assert_eq!(
934 serde_json::from_str::<GrantType>("\"password\"").unwrap(),
935 GrantType::Password
936 );
937 assert_eq!(
938 serde_json::from_str::<GrantType>("\"urn:ietf:params:oauth:grant-type:device_code\"")
939 .unwrap(),
940 GrantType::DeviceCode
941 );
942 assert_eq!(
943 serde_json::from_str::<GrantType>("\"urn:openid:params:grant-type:ciba\"").unwrap(),
944 GrantType::ClientInitiatedBackchannelAuthentication
945 );
946 }
947
948 #[test]
949 fn serialize_response_mode() {
950 assert_eq!(
951 serde_json::to_string(&ResponseMode::Query).unwrap(),
952 "\"query\""
953 );
954 assert_eq!(
955 serde_json::to_string(&ResponseMode::Fragment).unwrap(),
956 "\"fragment\""
957 );
958 assert_eq!(
959 serde_json::to_string(&ResponseMode::FormPost).unwrap(),
960 "\"form_post\""
961 );
962 }
963
964 #[test]
965 fn deserialize_response_mode() {
966 assert_eq!(
967 serde_json::from_str::<ResponseMode>("\"query\"").unwrap(),
968 ResponseMode::Query
969 );
970 assert_eq!(
971 serde_json::from_str::<ResponseMode>("\"fragment\"").unwrap(),
972 ResponseMode::Fragment
973 );
974 assert_eq!(
975 serde_json::from_str::<ResponseMode>("\"form_post\"").unwrap(),
976 ResponseMode::FormPost
977 );
978 }
979
980 #[test]
981 fn serialize_display() {
982 assert_eq!(serde_json::to_string(&Display::Page).unwrap(), "\"page\"");
983 assert_eq!(serde_json::to_string(&Display::Popup).unwrap(), "\"popup\"");
984 assert_eq!(serde_json::to_string(&Display::Touch).unwrap(), "\"touch\"");
985 assert_eq!(serde_json::to_string(&Display::Wap).unwrap(), "\"wap\"");
986 }
987
988 #[test]
989 fn deserialize_display() {
990 assert_eq!(
991 serde_json::from_str::<Display>("\"page\"").unwrap(),
992 Display::Page
993 );
994 assert_eq!(
995 serde_json::from_str::<Display>("\"popup\"").unwrap(),
996 Display::Popup
997 );
998 assert_eq!(
999 serde_json::from_str::<Display>("\"touch\"").unwrap(),
1000 Display::Touch
1001 );
1002 assert_eq!(
1003 serde_json::from_str::<Display>("\"wap\"").unwrap(),
1004 Display::Wap
1005 );
1006 }
1007
1008 #[test]
1009 fn serialize_prompt() {
1010 assert_eq!(serde_json::to_string(&Prompt::None).unwrap(), "\"none\"");
1011 assert_eq!(serde_json::to_string(&Prompt::Login).unwrap(), "\"login\"");
1012 assert_eq!(
1013 serde_json::to_string(&Prompt::Consent).unwrap(),
1014 "\"consent\""
1015 );
1016 assert_eq!(
1017 serde_json::to_string(&Prompt::SelectAccount).unwrap(),
1018 "\"select_account\""
1019 );
1020 assert_eq!(
1021 serde_json::to_string(&Prompt::Create).unwrap(),
1022 "\"create\""
1023 );
1024 }
1025
1026 #[test]
1027 fn deserialize_prompt() {
1028 assert_eq!(
1029 serde_json::from_str::<Prompt>("\"none\"").unwrap(),
1030 Prompt::None
1031 );
1032 assert_eq!(
1033 serde_json::from_str::<Prompt>("\"login\"").unwrap(),
1034 Prompt::Login
1035 );
1036 assert_eq!(
1037 serde_json::from_str::<Prompt>("\"consent\"").unwrap(),
1038 Prompt::Consent
1039 );
1040 assert_eq!(
1041 serde_json::from_str::<Prompt>("\"select_account\"").unwrap(),
1042 Prompt::SelectAccount
1043 );
1044 assert_eq!(
1045 serde_json::from_str::<Prompt>("\"create\"").unwrap(),
1046 Prompt::Create
1047 );
1048 }
1049}