1use std::collections::{BTreeMap, BTreeSet};
4use std::fmt::Display;
5
6use base64::{engine::general_purpose::STANDARD, Engine as _};
7use serde::{Deserialize, Serialize};
8use serde_with::base64::{Base64, UrlSafe};
9use serde_with::formats::SpaceSeparator;
10use serde_with::{
11 formats, rust::deserialize_ignore_any, serde_as, skip_serializing_none, StringWithSeparator,
12};
13use url::Url;
14use uuid::Uuid;
15
16pub const OAUTH2_DEVICE_CODE_EXPIRY_SECONDS: u64 = 300;
18pub const OAUTH2_DEVICE_CODE_INTERVAL_SECONDS: u64 = 5;
20pub const OAUTH2_TOKEN_TYPE_ACCESS_TOKEN: &str = "urn:ietf:params:oauth:token-type:access_token";
22
23#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
24pub enum CodeChallengeMethod {
25 S256,
29}
30
31#[serde_as]
32#[derive(Serialize, Deserialize, Debug, Clone)]
33pub struct PkceRequest {
34 #[serde_as(as = "Base64<UrlSafe, formats::Unpadded>")]
35 pub code_challenge: Vec<u8>,
36 pub code_challenge_method: CodeChallengeMethod,
37}
38
39#[serde_as]
42#[skip_serializing_none]
43#[derive(Serialize, Deserialize, Debug, Clone)]
44pub struct AuthorisationRequest {
45 pub response_type: ResponseType,
47 pub response_mode: Option<ResponseMode>,
56 pub client_id: String,
57 pub state: Option<String>,
58 #[serde(flatten)]
59 pub pkce_request: Option<PkceRequest>,
60 pub redirect_uri: Url,
61 #[serde_as(as = "StringWithSeparator::<SpaceSeparator, String>")]
62 pub scope: BTreeSet<String>,
63 pub nonce: Option<String>,
65 #[serde(flatten)]
67 pub oidc_ext: AuthorisationRequestOidc,
68 pub max_age: Option<i64>,
70 #[serde_as(as = "StringWithSeparator::<SpaceSeparator, Prompt>")]
71 #[serde(default)]
72 pub prompt: Vec<Prompt>,
73
74 #[serde_as(as = "StringWithSeparator::<SpaceSeparator, String>")]
75 #[serde(default)]
76 pub ui_locales: Vec<String>,
77
78 #[serde(flatten)]
79 pub unknown_keys: BTreeMap<String, serde_json::value::Value>,
80}
81
82impl AuthorisationRequest {
83 pub const fn get_response_mode(&self) -> Option<ResponseMode> {
91 match (self.response_mode, self.response_type) {
92 (None, ResponseType::IdToken) => Some(ResponseMode::Fragment),
96 (Some(ResponseMode::Query), ResponseType::IdToken) => None,
97
98 (None, ResponseType::Code) => Some(ResponseMode::Query),
100 (None, ResponseType::Token) => Some(ResponseMode::Fragment),
102
103 (Some(ResponseMode::Query), ResponseType::Token) => None,
108
109 (Some(m), _) => Some(m),
111 }
112 }
113}
114
115#[skip_serializing_none]
118#[derive(Serialize, Deserialize, Debug, Clone, Default)]
119pub struct AuthorisationRequestOidc {
120 pub display: Option<String>,
121 pub prompt: Option<String>,
122 pub ui_locales: Option<()>,
123 pub claims_locales: Option<()>,
124 pub id_token_hint: Option<String>,
125 pub login_hint: Option<String>,
126 pub acr: Option<String>,
127}
128
129#[derive(Serialize, Deserialize, Debug, Clone)]
133pub enum AuthorisationResponse {
134 ConsentRequested {
135 client_name: String,
137 scopes: BTreeSet<String>,
139 pii_scopes: BTreeSet<String>,
141 consent_token: String,
145 },
146 Permitted,
147}
148
149#[serde_as]
150#[skip_serializing_none]
151#[derive(Serialize, Deserialize, Debug)]
152#[serde(tag = "grant_type", rename_all = "snake_case")]
153pub enum GrantTypeReq {
154 AuthorizationCode {
155 code: String,
157 redirect_uri: Url,
159 code_verifier: Option<String>,
160 },
161 ClientCredentials {
162 #[serde_as(as = "Option<StringWithSeparator::<SpaceSeparator, String>>")]
163 scope: Option<BTreeSet<String>>,
164 },
165 RefreshToken {
166 refresh_token: String,
167 #[serde_as(as = "Option<StringWithSeparator::<SpaceSeparator, String>>")]
168 scope: Option<BTreeSet<String>>,
169 },
170 #[serde(rename = "urn:ietf:params:oauth:grant-type:token-exchange")]
171 TokenExchange {
172 subject_token: String,
173 subject_token_type: String,
174 requested_token_type: Option<String>,
175 audience: Option<String>,
176 resource: Option<String>,
177 actor_token: Option<String>,
178 actor_token_type: Option<String>,
179 #[serde_as(as = "Option<StringWithSeparator::<SpaceSeparator, String>>")]
180 scope: Option<BTreeSet<String>>,
181 },
182 #[serde(rename = "urn:ietf:params:oauth:grant-type:device_code")]
184 DeviceCode {
185 device_code: String,
186 scope: Option<BTreeSet<String>>,
188 },
189}
190
191#[skip_serializing_none]
193#[derive(Serialize, Deserialize, Debug)]
194pub struct AccessTokenRequest {
195 #[serde(flatten)]
196 pub grant_type: GrantTypeReq,
197 #[serde(flatten)]
200 pub client_post_auth: ClientPostAuth,
201}
202
203impl From<GrantTypeReq> for AccessTokenRequest {
204 fn from(req: GrantTypeReq) -> AccessTokenRequest {
205 AccessTokenRequest {
206 grant_type: req,
207 client_post_auth: ClientPostAuth::default(),
208 }
209 }
210}
211
212#[derive(Serialize, Debug, Clone, Deserialize)]
213#[skip_serializing_none]
214pub struct OAuth2RFC9068Token<V>
215where
216 V: Clone,
217{
218 pub iss: String,
220 pub sub: Uuid,
222 pub aud: String,
224 pub exp: i64,
226 pub nbf: i64,
228 pub iat: i64,
230 pub jti: Uuid,
232 pub client_id: String,
233 #[serde(flatten)]
234 pub extensions: V,
235}
236
237#[serde_as]
239#[skip_serializing_none]
240#[derive(Serialize, Deserialize, Debug, Clone)]
241pub struct OAuth2RFC9068TokenExtensions {
242 pub auth_time: Option<i64>,
243 pub acr: Option<String>,
244 pub amr: Option<Vec<String>>,
245
246 #[serde_as(as = "StringWithSeparator::<SpaceSeparator, String>")]
247 pub scope: BTreeSet<String>,
248
249 pub nonce: Option<String>,
250
251 pub session_id: Uuid,
252 pub parent_session_id: Option<Uuid>,
253}
254
255#[derive(Serialize, Deserialize, Debug, PartialEq)]
256pub enum IssuedTokenType {
257 AccessToken,
258 RefreshToken,
259 IdToken,
260 Saml1,
261 Saml2,
262}
263
264#[serde_as]
266#[skip_serializing_none]
267#[derive(Serialize, Deserialize, Debug)]
268pub struct AccessTokenResponse {
269 pub access_token: String,
270 pub token_type: AccessTokenType,
271 pub issued_token_type: Option<IssuedTokenType>,
273 pub expires_in: u32,
275 pub refresh_token: Option<String>,
276 #[serde_as(as = "StringWithSeparator::<SpaceSeparator, String>")]
279 pub scope: BTreeSet<String>,
280 pub id_token: Option<String>,
282}
283
284#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
286#[serde(try_from = "&str")]
287pub enum AccessTokenType {
288 Bearer,
289 PoP,
290 #[serde(rename = "N_A")]
291 NA,
292 DPoP,
293}
294
295impl TryFrom<&str> for AccessTokenType {
296 type Error = String;
297
298 fn try_from(s: &str) -> Result<Self, Self::Error> {
299 match s.to_lowercase().as_str() {
300 "bearer" => Ok(AccessTokenType::Bearer),
301 "pop" => Ok(AccessTokenType::PoP),
302 "n_a" => Ok(AccessTokenType::NA),
303 "dpop" => Ok(AccessTokenType::DPoP),
304 _ => Err(format!("Unknown AccessTokenType: {s}")),
305 }
306 }
307}
308
309#[skip_serializing_none]
312#[derive(Serialize, Deserialize, Debug)]
313pub struct TokenRevokeRequest {
314 pub token: String,
315 pub token_type_hint: Option<String>,
318
319 #[serde(flatten)]
320 pub client_post_auth: ClientPostAuth,
321}
322
323#[skip_serializing_none]
324#[derive(Serialize, Deserialize, Debug, Default)]
325pub struct ClientPostAuth {
327 pub client_id: Option<String>,
328 pub client_secret: Option<String>,
329}
330
331impl From<(String, Option<String>)> for ClientPostAuth {
332 fn from((client_id, client_secret): (String, Option<String>)) -> Self {
333 ClientPostAuth {
334 client_id: Some(client_id),
335 client_secret,
336 }
337 }
338}
339
340impl From<(&str, Option<&str>)> for ClientPostAuth {
341 fn from((client_id, client_secret): (&str, Option<&str>)) -> Self {
342 ClientPostAuth {
343 client_id: Some(client_id.to_string()),
344 client_secret: client_secret.map(|s| s.to_string()),
345 }
346 }
347}
348
349#[skip_serializing_none]
350#[derive(Serialize, Deserialize, Debug, Default)]
351pub struct ClientAuth {
353 pub client_id: String,
354 pub client_secret: Option<String>,
355}
356
357impl From<(&str, Option<&str>)> for ClientAuth {
358 fn from((client_id, client_secret): (&str, Option<&str>)) -> Self {
359 ClientAuth {
360 client_id: client_id.to_string(),
361 client_secret: client_secret.map(|s| s.to_string()),
362 }
363 }
364}
365
366#[skip_serializing_none]
368#[derive(Serialize, Deserialize, Debug)]
369pub struct AccessTokenIntrospectRequest {
370 pub token: String,
371 pub token_type_hint: Option<String>,
374
375 #[serde(flatten)]
378 pub client_post_auth: ClientPostAuth,
379}
380
381#[serde_as]
384#[skip_serializing_none]
385#[derive(Serialize, Deserialize, Debug)]
386pub struct AccessTokenIntrospectResponse {
387 pub active: bool,
388 #[serde_as(as = "StringWithSeparator::<SpaceSeparator, String>")]
389 pub scope: BTreeSet<String>,
390 pub client_id: Option<String>,
391 pub username: Option<String>,
392 pub token_type: Option<AccessTokenType>,
393 pub exp: Option<i64>,
394 pub iat: Option<i64>,
395 pub nbf: Option<i64>,
396 pub sub: Option<String>,
397 pub aud: Option<String>,
398 pub iss: Option<String>,
399 pub jti: Uuid,
401}
402
403impl AccessTokenIntrospectResponse {
404 pub fn inactive(session_id: Uuid) -> Self {
405 AccessTokenIntrospectResponse {
406 active: false,
407 scope: BTreeSet::default(),
408 client_id: None,
409 username: None,
410 token_type: None,
411 exp: None,
412 iat: None,
413 nbf: None,
414 sub: None,
415 aud: None,
416 iss: None,
417 jti: session_id,
418 }
419 }
420}
421
422#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq, Eq)]
423#[serde(rename_all = "snake_case")]
424pub enum ResponseType {
425 Code,
428 Token,
431 IdToken,
433}
434
435#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq, Eq)]
436#[serde(rename_all = "snake_case")]
437pub enum ResponseMode {
438 Query,
439 Fragment,
440 FormPost,
441 #[serde(other, deserialize_with = "deserialize_ignore_any")]
442 Invalid,
443}
444
445fn response_modes_supported_default() -> Vec<ResponseMode> {
446 vec![ResponseMode::Query, ResponseMode::Fragment]
447}
448
449#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Hash)]
450#[serde(rename_all = "snake_case")]
451pub enum Prompt {
452 None,
461 Login,
462 Consent,
463 SelectAccount,
464 #[serde(untagged)]
465 Invalid(String),
466}
467
468impl Display for Prompt {
469 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
470 let s = match self {
471 Prompt::None => "none",
472 Prompt::Login => "login",
473 Prompt::Consent => "consent",
474 Prompt::SelectAccount => "select_account",
475 Prompt::Invalid(str) => &format!("invalid({})", str),
476 };
477 write!(f, "{s}")
478 }
479}
480
481impl std::str::FromStr for Prompt {
482 type Err = std::convert::Infallible;
483
484 fn from_str(s: &str) -> Result<Self, Self::Err> {
485 Ok(match s {
486 "none" => Prompt::None,
487 "login" => Prompt::Login,
488 "consent" => Prompt::Consent,
489 "select_account" => Prompt::SelectAccount,
490 other => Prompt::Invalid(other.to_string()),
491 })
492 }
493}
494
495#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
496#[serde(rename_all = "snake_case")]
497pub enum GrantType {
498 #[serde(rename = "authorization_code")]
499 AuthorisationCode,
500 Implicit,
501 #[serde(rename = "urn:ietf:params:oauth:grant-type:token-exchange")]
502 TokenExchange,
503}
504
505fn grant_types_supported_default() -> Vec<GrantType> {
506 vec![
507 GrantType::AuthorisationCode,
508 GrantType::Implicit,
509 GrantType::TokenExchange,
510 ]
511}
512
513#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
514#[serde(rename_all = "snake_case")]
515pub enum SubjectType {
516 Pairwise,
517 Public,
518}
519
520#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
521pub enum PkceAlg {
522 S256,
523}
524
525#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
526#[serde(rename_all = "UPPERCASE")]
527pub enum IdTokenSignAlg {
529 ES256,
531 RS256,
532}
533
534#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
535#[serde(rename_all = "snake_case")]
536pub enum EndpointAuthMethod {
537 None,
538 ClientSecretPost,
539 ClientSecretBasic,
540 ClientSecretJwt,
541 PrivateKeyJwt,
542}
543
544fn token_endpoint_auth_methods_supported_default() -> Vec<EndpointAuthMethod> {
545 vec![EndpointAuthMethod::ClientSecretBasic]
546}
547
548#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
549#[serde(rename_all = "snake_case")]
550pub enum DisplayValue {
551 Page,
552 Popup,
553 Touch,
554 Wap,
555}
556
557#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
558#[serde(rename_all = "snake_case")]
559pub enum ClaimType {
561 Normal,
562 Aggregated,
563 Distributed,
564}
565
566fn claim_types_supported_default() -> Vec<ClaimType> {
567 vec![ClaimType::Normal]
568}
569
570fn claims_parameter_supported_default() -> bool {
571 false
572}
573
574fn request_parameter_supported_default() -> bool {
575 false
576}
577
578fn request_uri_parameter_supported_default() -> bool {
579 false
580}
581
582fn require_request_uri_parameter_supported_default() -> bool {
583 false
584}
585
586#[derive(Serialize, Deserialize, Debug)]
587pub struct OidcWebfingerRel {
588 pub rel: String,
589 pub href: String,
590}
591
592#[skip_serializing_none]
595#[derive(Serialize, Deserialize, Debug)]
596pub struct OidcWebfingerResponse {
597 pub subject: String,
598 pub links: Vec<OidcWebfingerRel>,
599}
600
601#[skip_serializing_none]
604#[derive(Serialize, Deserialize, Debug)]
605pub struct OidcDiscoveryResponse {
606 pub issuer: Url,
607 pub authorization_endpoint: Url,
608 pub token_endpoint: Url,
609 pub userinfo_endpoint: Option<Url>,
610 pub jwks_uri: Url,
611 pub registration_endpoint: Option<Url>,
612 pub scopes_supported: Option<Vec<String>>,
613 pub response_types_supported: Vec<ResponseType>,
615 #[serde(default = "response_modes_supported_default")]
617 pub response_modes_supported: Vec<ResponseMode>,
618 #[serde(default = "grant_types_supported_default")]
620 pub grant_types_supported: Vec<GrantType>,
621 pub acr_values_supported: Option<Vec<String>>,
622 pub subject_types_supported: Vec<SubjectType>,
624 pub id_token_signing_alg_values_supported: Vec<IdTokenSignAlg>,
625 pub id_token_encryption_alg_values_supported: Option<Vec<String>>,
626 pub id_token_encryption_enc_values_supported: Option<Vec<String>>,
627 pub userinfo_signing_alg_values_supported: Option<Vec<String>>,
628 pub userinfo_encryption_alg_values_supported: Option<Vec<String>>,
629 pub userinfo_encryption_enc_values_supported: Option<Vec<String>>,
630 pub request_object_signing_alg_values_supported: Option<Vec<String>>,
631 pub request_object_encryption_alg_values_supported: Option<Vec<String>>,
632 pub request_object_encryption_enc_values_supported: Option<Vec<String>>,
633 #[serde(default = "token_endpoint_auth_methods_supported_default")]
635 pub token_endpoint_auth_methods_supported: Vec<EndpointAuthMethod>,
636 pub token_endpoint_auth_signing_alg_values_supported: Option<Vec<String>>,
637 pub display_values_supported: Option<Vec<DisplayValue>>,
639 #[serde(default = "claim_types_supported_default")]
641 pub claim_types_supported: Vec<ClaimType>,
642 pub claims_supported: Option<Vec<String>>,
643 pub service_documentation: Option<Url>,
644 pub claims_locales_supported: Option<Vec<String>>,
645 pub ui_locales_supported: Option<Vec<String>>,
646 #[serde(default = "claims_parameter_supported_default")]
648 pub claims_parameter_supported: bool,
649
650 pub op_policy_uri: Option<Url>,
651 pub op_tos_uri: Option<Url>,
652
653 #[serde(default = "request_parameter_supported_default")]
655 pub request_parameter_supported: bool,
656 #[serde(default = "request_uri_parameter_supported_default")]
657 pub request_uri_parameter_supported: bool,
658 #[serde(default = "require_request_uri_parameter_supported_default")]
659 pub require_request_uri_registration: bool,
660
661 pub code_challenge_methods_supported: Vec<PkceAlg>,
662
663 pub revocation_endpoint: Option<Url>,
671 pub revocation_endpoint_auth_methods_supported: Vec<EndpointAuthMethod>,
672
673 pub introspection_endpoint: Option<Url>,
675 pub introspection_endpoint_auth_methods_supported: Vec<EndpointAuthMethod>,
676 pub introspection_endpoint_auth_signing_alg_values_supported: Option<Vec<IdTokenSignAlg>>,
677
678 pub device_authorization_endpoint: Option<Url>,
680}
681
682#[skip_serializing_none]
684#[derive(Serialize, Deserialize, Debug)]
685pub struct Oauth2Rfc8414MetadataResponse {
686 pub issuer: Url,
687 pub authorization_endpoint: Url,
688 pub token_endpoint: Url,
689
690 pub jwks_uri: Option<Url>,
691
692 pub registration_endpoint: Option<Url>,
694
695 pub scopes_supported: Option<Vec<String>>,
696
697 pub response_types_supported: Vec<ResponseType>,
699 #[serde(default = "response_modes_supported_default")]
700 pub response_modes_supported: Vec<ResponseMode>,
701 #[serde(default = "grant_types_supported_default")]
702 pub grant_types_supported: Vec<GrantType>,
703
704 #[serde(default = "token_endpoint_auth_methods_supported_default")]
705 pub token_endpoint_auth_methods_supported: Vec<EndpointAuthMethod>,
706
707 pub token_endpoint_auth_signing_alg_values_supported: Option<Vec<IdTokenSignAlg>>,
708
709 pub service_documentation: Option<Url>,
710 pub ui_locales_supported: Option<Vec<String>>,
711
712 pub op_policy_uri: Option<Url>,
713 pub op_tos_uri: Option<Url>,
714
715 pub revocation_endpoint: Option<Url>,
717 pub revocation_endpoint_auth_methods_supported: Vec<EndpointAuthMethod>,
718
719 pub introspection_endpoint: Option<Url>,
721 pub introspection_endpoint_auth_methods_supported: Vec<EndpointAuthMethod>,
722 pub introspection_endpoint_auth_signing_alg_values_supported: Option<Vec<IdTokenSignAlg>>,
723
724 pub code_challenge_methods_supported: Vec<PkceAlg>,
726}
727
728#[skip_serializing_none]
729#[derive(Serialize, Deserialize, Debug, Default)]
730pub struct ErrorResponse {
731 pub error: String,
732 pub error_description: Option<String>,
733 pub error_uri: Option<Url>,
734}
735
736#[derive(Debug, Serialize, Deserialize)]
737pub struct DeviceAuthorizationResponse {
739 device_code: String,
741 user_code: String,
743 verification_uri: Url,
744 verification_uri_complete: Url,
745 expires_in: u64,
746 interval: u64,
747}
748
749impl DeviceAuthorizationResponse {
750 pub fn new(verification_uri: Url, device_code: [u8; 16], user_code: String) -> Self {
751 let mut verification_uri_complete = verification_uri.clone();
752 verification_uri_complete
753 .query_pairs_mut()
754 .append_pair("user_code", &user_code);
755
756 let device_code = STANDARD.encode(device_code);
757
758 Self {
759 verification_uri_complete,
760 device_code,
761 user_code,
762 verification_uri,
763 expires_in: OAUTH2_DEVICE_CODE_EXPIRY_SECONDS,
764 interval: OAUTH2_DEVICE_CODE_INTERVAL_SECONDS,
765 }
766 }
767}
768
769#[cfg(test)]
770mod tests {
771 use super::{AccessTokenRequest, GrantTypeReq, OAUTH2_TOKEN_TYPE_ACCESS_TOKEN};
772 use std::collections::BTreeSet;
773 use url::Url;
774
775 #[test]
776 fn test_oauth2_access_token_req() {
777 let atr: AccessTokenRequest = GrantTypeReq::AuthorizationCode {
778 code: "demo code".to_string(),
779 redirect_uri: Url::parse("http://[::1]").unwrap(),
780 code_verifier: None,
781 }
782 .into();
783
784 println!("{:?}", serde_json::to_string(&atr).expect("JSON failure"));
785 }
786
787 #[test]
788 fn test_oauth2_access_token_type_serde() {
789 for testcase in ["bearer", "Bearer", "BeArEr"] {
790 let at: super::AccessTokenType =
791 serde_json::from_str(&format!("\"{testcase}\"")).expect("Failed to parse");
792 assert_eq!(at, super::AccessTokenType::Bearer);
793 }
794
795 for testcase in ["dpop", "dPoP", "DPOP", "DPoP"] {
796 let at: super::AccessTokenType =
797 serde_json::from_str(&format!("\"{testcase}\"")).expect("Failed to parse");
798 assert_eq!(at, super::AccessTokenType::DPoP);
799 }
800
801 {
802 let testcase = "cheese";
803 let at = serde_json::from_str::<super::AccessTokenType>(&format!("\"{testcase}\""));
804 assert!(at.is_err())
805 }
806 }
807
808 #[test]
809 fn test_token_exchange_grant_serialization() {
810 let scopes: BTreeSet<String> = ["groups", "openid"]
811 .into_iter()
812 .map(str::to_string)
813 .collect();
814
815 let atr = AccessTokenRequest {
816 grant_type: GrantTypeReq::TokenExchange {
817 subject_token: "subject".to_string(),
818 subject_token_type: OAUTH2_TOKEN_TYPE_ACCESS_TOKEN.to_string(),
819 requested_token_type: None,
820 audience: Some("test_resource_server".to_string()),
821 resource: None,
822 actor_token: None,
823 actor_token_type: None,
824 scope: Some(scopes.clone()),
825 },
826 client_post_auth: Default::default(),
827 };
828
829 let json = serde_json::to_string(&atr).expect("JSON failure");
830 let de: AccessTokenRequest = serde_json::from_str(&json).expect("Roundtrip failure");
831
832 match de.grant_type {
833 GrantTypeReq::TokenExchange {
834 subject_token,
835 subject_token_type,
836 requested_token_type,
837 audience,
838 actor_token,
839 actor_token_type,
840 scope: descope,
841 ..
842 } => {
843 assert_eq!(subject_token, "subject");
844 assert_eq!(subject_token_type, OAUTH2_TOKEN_TYPE_ACCESS_TOKEN);
845 assert_eq!(requested_token_type, None);
846 assert_eq!(audience.as_deref(), Some("test_resource_server"));
847 assert_eq!(actor_token, None);
848 assert_eq!(actor_token_type, None);
849 assert_eq!(descope, Some(scopes));
850 }
851 _ => panic!("Wrong grant type"),
852 }
853 }
854
855 #[test]
856 fn test_authorisation_request_prompt_single_value() {
857 let qs = "response_type=code\
858 &client_id=test_client\
859 &redirect_uri=http%3A%2F%2Flocalhost\
860 &scope=openid\
861 &prompt=login";
862
863 let req: super::AuthorisationRequest =
864 serde_urlencoded::from_str(qs).expect("Failed to deserialize");
865
866 assert_eq!(req.prompt.len(), 1);
867 assert!(req.prompt.contains(&super::Prompt::Login));
868 }
869
870 #[test]
871 fn test_authorisation_request_prompt_multiple_values() {
872 let qs = "response_type=code\
873 &client_id=test_client\
874 &redirect_uri=http%3A%2F%2Flocalhost\
875 &scope=openid\
876 &prompt=login%20consent";
877
878 let req: super::AuthorisationRequest =
879 serde_urlencoded::from_str(qs).expect("Failed to deserialize");
880
881 assert_eq!(req.prompt.len(), 2);
882 assert!(req.prompt.contains(&super::Prompt::Login));
883 assert!(req.prompt.contains(&super::Prompt::Consent));
884 }
885
886 #[test]
887 fn test_authorisation_request_prompt_none() {
888 let qs = "response_type=code\
889 &client_id=test_client\
890 &redirect_uri=http%3A%2F%2Flocalhost\
891 &scope=openid\
892 &prompt=none";
893
894 let req: super::AuthorisationRequest =
895 serde_urlencoded::from_str(qs).expect("Failed to deserialize");
896
897 assert_eq!(req.prompt.len(), 1);
898 assert!(req.prompt.contains(&super::Prompt::None));
899 }
900
901 #[test]
902 fn test_authorisation_request_prompt_absent() {
903 let qs = "response_type=code\
904 &client_id=test_client\
905 &redirect_uri=http%3A%2F%2Flocalhost\
906 &scope=openid";
907
908 let req: super::AuthorisationRequest =
909 serde_urlencoded::from_str(qs).expect("Failed to deserialize");
910
911 assert!(req.prompt.is_empty());
912 }
913
914 #[test]
915 fn test_authorisation_request_prompt_invalid_value() {
916 let qs = "response_type=code\
917 &client_id=test_client\
918 &redirect_uri=http%3A%2F%2Flocalhost\
919 &scope=openid\
920 &prompt=bogus";
921
922 let req: super::AuthorisationRequest =
923 serde_urlencoded::from_str(qs).expect("Failed to deserialize");
924
925 assert_eq!(req.prompt.len(), 1);
926 assert!(req
927 .prompt
928 .contains(&super::Prompt::Invalid("bogus".to_string())));
929 }
930
931 #[test]
932 fn test_authorisation_request_prompt_select_account() {
933 let qs = "response_type=code\
934 &client_id=test_client\
935 &redirect_uri=http%3A%2F%2Flocalhost\
936 &scope=openid\
937 &prompt=select_account";
938
939 let req: super::AuthorisationRequest =
940 serde_urlencoded::from_str(qs).expect("Failed to deserialize");
941
942 assert_eq!(req.prompt.len(), 1);
943 assert!(req.prompt.contains(&super::Prompt::SelectAccount));
944 }
945
946 #[test]
947 fn test_authorisation_request_ui_locales() {
948 let qs = "response_type=code\
949 &client_id=test_client\
950 &redirect_uri=http%3A%2F%2Flocalhost\
951 &scope=openid\
952 &ui_locales=en-US";
953
954 let req: super::AuthorisationRequest =
955 serde_urlencoded::from_str(qs).expect("Failed to deserialize");
956
957 assert_eq!(req.ui_locales.len(), 1);
958 assert!(req.ui_locales.contains(&"en-US".to_string()));
959
960 let qs = "response_type=code\
961 &client_id=test_client\
962 &redirect_uri=http%3A%2F%2Flocalhost\
963 &scope=openid\
964 &ui_locales=en-US%20fr-FR";
965
966 let req: super::AuthorisationRequest =
967 serde_urlencoded::from_str(qs).expect("Failed to deserialize");
968 assert_eq!(req.ui_locales.len(), 2);
969 assert!(req.ui_locales.contains(&"fr-FR".to_string()));
970
971 let qs = "response_type=code\
972 &client_id=test_client\
973 &redirect_uri=http%3A%2F%2Flocalhost\
974 &scope=openid";
975
976 let req: super::AuthorisationRequest =
977 serde_urlencoded::from_str(qs).expect("Failed to deserialize");
978 assert_eq!(req.ui_locales.len(), 0);
979 assert!(req.ui_locales.is_empty());
980 }
981
982 #[test]
983 fn test_authorisation_request_prompt_all_valid_values() {
984 let qs = "response_type=code\
985 &client_id=test_client\
986 &redirect_uri=http%3A%2F%2Flocalhost\
987 &scope=openid\
988 &prompt=login+consent+select_account";
989
990 let req: super::AuthorisationRequest =
991 serde_urlencoded::from_str(qs).expect("Failed to deserialize");
992
993 assert_eq!(req.prompt.len(), 3);
994 assert!(req.prompt.contains(&super::Prompt::Login));
995 assert!(req.prompt.contains(&super::Prompt::Consent));
996 assert!(req.prompt.contains(&super::Prompt::SelectAccount));
997 }
998}