1use std::ops::Deref;
12
13use chrono::{DateTime, Duration, Utc};
14use indexmap::IndexMap;
15use language_tags::LanguageTag;
16use mas_iana::{
17 jose::{JsonWebEncryptionAlg, JsonWebEncryptionEnc, JsonWebSignatureAlg},
18 oauth::{OAuthAuthorizationEndpointResponseType, OAuthClientAuthenticationMethod},
19};
20use mas_jose::jwk::PublicJsonWebKeySet;
21use serde::{Deserialize, Serialize};
22use serde_with::{TimestampSeconds, serde_as, skip_serializing_none};
23use thiserror::Error;
24use url::Url;
25
26use crate::{
27 oidc::{ApplicationType, SubjectType},
28 requests::GrantType,
29 response_type::ResponseType,
30};
31
32mod client_metadata_serde;
33use client_metadata_serde::ClientMetadataSerdeHelper;
34
35pub const DEFAULT_RESPONSE_TYPES: [OAuthAuthorizationEndpointResponseType; 1] =
37 [OAuthAuthorizationEndpointResponseType::Code];
38
39pub const DEFAULT_GRANT_TYPES: &[GrantType] = &[GrantType::AuthorizationCode];
41
42pub const DEFAULT_APPLICATION_TYPE: ApplicationType = ApplicationType::Web;
44
45pub const DEFAULT_TOKEN_AUTH_METHOD: &OAuthClientAuthenticationMethod =
47 &OAuthClientAuthenticationMethod::ClientSecretBasic;
48
49pub const DEFAULT_SIGNING_ALGORITHM: &JsonWebSignatureAlg = &JsonWebSignatureAlg::Rs256;
51
52pub const DEFAULT_ENCRYPTION_ENC_ALGORITHM: &JsonWebEncryptionEnc =
54 &JsonWebEncryptionEnc::A128CbcHs256;
55
56#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct Localized<T> {
61 non_localized: T,
62 localized: IndexMap<LanguageTag, T>,
63}
64
65impl<T> Localized<T> {
66 pub fn new(non_localized: T, localized: impl IntoIterator<Item = (LanguageTag, T)>) -> Self {
69 Self {
70 non_localized,
71 localized: localized.into_iter().collect(),
72 }
73 }
74
75 #[allow(clippy::len_without_is_empty)]
77 pub fn len(&self) -> usize {
78 self.localized.len() + 1
79 }
80
81 pub fn non_localized(&self) -> &T {
83 &self.non_localized
84 }
85
86 pub fn to_non_localized(self) -> T {
88 self.non_localized
89 }
90
91 pub fn get(&self, language: Option<&LanguageTag>) -> Option<&T> {
93 match language {
94 Some(lang) => self.localized.get(lang),
95 None => Some(&self.non_localized),
96 }
97 }
98
99 pub fn iter(&self) -> impl Iterator<Item = (Option<&LanguageTag>, &T)> {
101 Some(&self.non_localized)
102 .into_iter()
103 .map(|val| (None, val))
104 .chain(self.localized.iter().map(|(lang, val)| (Some(lang), val)))
105 }
106}
107
108impl<T> From<(T, IndexMap<LanguageTag, T>)> for Localized<T> {
109 fn from(t: (T, IndexMap<LanguageTag, T>)) -> Self {
110 Localized {
111 non_localized: t.0,
112 localized: t.1,
113 }
114 }
115}
116
117#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
123#[serde(from = "ClientMetadataSerdeHelper", into = "ClientMetadataSerdeHelper")]
124pub struct ClientMetadata {
125 pub redirect_uris: Option<Vec<Url>>,
135
136 pub response_types: Option<Vec<ResponseType>>,
147
148 pub grant_types: Option<Vec<GrantType>>,
163
164 pub application_type: Option<ApplicationType>,
168
169 pub contacts: Option<Vec<String>>,
171
172 pub client_name: Option<Localized<String>>,
174
175 pub logo_uri: Option<Localized<Url>>,
177
178 pub client_uri: Option<Localized<Url>>,
180
181 pub policy_uri: Option<Localized<Url>>,
184
185 pub tos_uri: Option<Localized<Url>>,
188
189 pub jwks_uri: Option<Url>,
200
201 pub jwks: Option<PublicJsonWebKeySet>,
210
211 pub software_id: Option<String>,
218
219 pub software_version: Option<String>,
222
223 pub sector_identifier_uri: Option<Url>,
230
231 pub subject_type: Option<SubjectType>,
235
236 pub token_endpoint_auth_method: Option<OAuthClientAuthenticationMethod>,
245
246 pub token_endpoint_auth_signing_alg: Option<JsonWebSignatureAlg>,
258
259 pub id_token_signed_response_alg: Option<JsonWebSignatureAlg>,
270
271 pub id_token_encrypted_response_alg: Option<JsonWebEncryptionAlg>,
278
279 pub id_token_encrypted_response_enc: Option<JsonWebEncryptionEnc>,
287
288 pub userinfo_signed_response_alg: Option<JsonWebSignatureAlg>,
292
293 pub userinfo_encrypted_response_alg: Option<JsonWebEncryptionAlg>,
302
303 pub userinfo_encrypted_response_enc: Option<JsonWebEncryptionEnc>,
313
314 pub request_object_signing_alg: Option<JsonWebSignatureAlg>,
321
322 pub request_object_encryption_alg: Option<JsonWebEncryptionAlg>,
329
330 pub request_object_encryption_enc: Option<JsonWebEncryptionEnc>,
338
339 pub default_max_age: Option<Duration>,
347
348 pub require_auth_time: Option<bool>,
352
353 pub default_acr_values: Option<Vec<String>>,
355
356 pub initiate_login_uri: Option<Url>,
362
363 pub request_uris: Option<Vec<Url>>,
377
378 pub require_signed_request_object: Option<bool>,
385
386 pub require_pushed_authorization_requests: Option<bool>,
393
394 pub introspection_signed_response_alg: Option<JsonWebSignatureAlg>,
400
401 pub introspection_encrypted_response_alg: Option<JsonWebEncryptionAlg>,
413
414 pub introspection_encrypted_response_enc: Option<JsonWebEncryptionEnc>,
426
427 pub post_logout_redirect_uris: Option<Vec<Url>>,
432}
433
434impl ClientMetadata {
435 #[allow(clippy::too_many_lines)]
444 pub fn validate(self) -> Result<VerifiedClientMetadata, ClientMetadataVerificationError> {
445 let grant_types = self.grant_types();
446 let has_implicit = grant_types.contains(&GrantType::Implicit);
447 let has_authorization_code = grant_types.contains(&GrantType::AuthorizationCode);
448 let has_both = has_implicit && has_authorization_code;
449
450 if let Some(uris) = &self.redirect_uris {
451 if let Some(uri) = uris.iter().find(|uri| uri.fragment().is_some()) {
452 return Err(ClientMetadataVerificationError::RedirectUriWithFragment(
453 uri.clone(),
454 ));
455 }
456 } else if has_authorization_code || has_implicit {
457 return Err(ClientMetadataVerificationError::MissingRedirectUris);
459 }
460
461 let response_type_code = [OAuthAuthorizationEndpointResponseType::Code.into()];
462 let response_types = match &self.response_types {
463 Some(types) => &types[..],
464 None if has_authorization_code || has_implicit => &response_type_code[..],
466 None => &[],
467 };
468
469 for response_type in response_types {
470 let has_code = response_type.has_code();
471 let has_id_token = response_type.has_id_token();
472 let has_token = response_type.has_token();
473 let is_ok = has_code && has_both
474 || !has_code && has_implicit
475 || has_authorization_code && !has_id_token && !has_token
476 || !has_code && !has_id_token && !has_token;
477
478 if !is_ok {
479 return Err(ClientMetadataVerificationError::IncoherentResponseType(
480 response_type.clone(),
481 ));
482 }
483 }
484
485 if self.jwks_uri.is_some() && self.jwks.is_some() {
486 return Err(ClientMetadataVerificationError::JwksUriAndJwksMutuallyExclusive);
487 }
488
489 if let Some(url) = self
490 .sector_identifier_uri
491 .as_ref()
492 .filter(|url| url.scheme() != "https")
493 {
494 return Err(ClientMetadataVerificationError::UrlNonHttpsScheme(
495 "sector_identifier_uri",
496 url.clone(),
497 ));
498 }
499
500 if *self.token_endpoint_auth_method() == OAuthClientAuthenticationMethod::PrivateKeyJwt
501 && self.jwks_uri.is_none()
502 && self.jwks.is_none()
503 {
504 return Err(ClientMetadataVerificationError::MissingJwksForTokenMethod);
505 }
506
507 if let Some(alg) = &self.token_endpoint_auth_signing_alg {
508 if *alg == JsonWebSignatureAlg::None {
509 return Err(ClientMetadataVerificationError::UnauthorizedSigningAlgNone(
510 "token_endpoint",
511 ));
512 }
513 } else if matches!(
514 self.token_endpoint_auth_method(),
515 OAuthClientAuthenticationMethod::PrivateKeyJwt
516 | OAuthClientAuthenticationMethod::ClientSecretJwt
517 ) {
518 return Err(ClientMetadataVerificationError::MissingAuthSigningAlg(
519 "token_endpoint",
520 ));
521 }
522
523 if *self.id_token_signed_response_alg() == JsonWebSignatureAlg::None
524 && response_types.iter().any(ResponseType::has_id_token)
525 {
526 return Err(ClientMetadataVerificationError::IdTokenSigningAlgNone);
527 }
528
529 if self.id_token_encrypted_response_enc.is_some() {
530 self.id_token_encrypted_response_alg.as_ref().ok_or(
531 ClientMetadataVerificationError::MissingEncryptionAlg("id_token"),
532 )?;
533 }
534
535 if self.userinfo_encrypted_response_enc.is_some() {
536 self.userinfo_encrypted_response_alg.as_ref().ok_or(
537 ClientMetadataVerificationError::MissingEncryptionAlg("userinfo"),
538 )?;
539 }
540
541 if self.request_object_encryption_enc.is_some() {
542 self.request_object_encryption_alg.as_ref().ok_or(
543 ClientMetadataVerificationError::MissingEncryptionAlg("request_object"),
544 )?;
545 }
546
547 if let Some(url) = self
548 .initiate_login_uri
549 .as_ref()
550 .filter(|url| url.scheme() != "https")
551 {
552 return Err(ClientMetadataVerificationError::UrlNonHttpsScheme(
553 "initiate_login_uri",
554 url.clone(),
555 ));
556 }
557
558 if self.introspection_encrypted_response_enc.is_some() {
559 self.introspection_encrypted_response_alg.as_ref().ok_or(
560 ClientMetadataVerificationError::MissingEncryptionAlg("introspection"),
561 )?;
562 }
563
564 Ok(VerifiedClientMetadata { inner: self })
565 }
566
567 #[must_use]
570 pub fn sorted(mut self) -> Self {
571 if let Some(redirect_uris) = &mut self.redirect_uris {
573 redirect_uris.sort();
574 }
575 if let Some(response_types) = &mut self.response_types {
576 response_types.sort();
577 }
578 if let Some(grant_types) = &mut self.grant_types {
579 grant_types.sort();
580 }
581 if let Some(contacts) = &mut self.contacts {
582 contacts.sort();
583 }
584 if let Some(client_name) = &mut self.client_name {
585 client_name.sort();
586 }
587 if let Some(logo_uri) = &mut self.logo_uri {
588 logo_uri.sort();
589 }
590 if let Some(client_uri) = &mut self.client_uri {
591 client_uri.sort();
592 }
593 if let Some(policy_uri) = &mut self.policy_uri {
594 policy_uri.sort();
595 }
596 if let Some(tos_uri) = &mut self.tos_uri {
597 tos_uri.sort();
598 }
599 if let Some(default_acr_values) = &mut self.default_acr_values {
600 default_acr_values.sort();
601 }
602 if let Some(request_uris) = &mut self.request_uris {
603 request_uris.sort();
604 }
605 if let Some(post_logout_redirect_uris) = &mut self.post_logout_redirect_uris {
606 post_logout_redirect_uris.sort();
607 }
608
609 self
610 }
611
612 #[must_use]
623 pub fn response_types(&self) -> Vec<ResponseType> {
624 self.response_types.clone().unwrap_or_else(|| {
625 DEFAULT_RESPONSE_TYPES
626 .into_iter()
627 .map(ResponseType::from)
628 .collect()
629 })
630 }
631
632 #[must_use]
645 pub fn grant_types(&self) -> &[GrantType] {
646 self.grant_types.as_deref().unwrap_or(DEFAULT_GRANT_TYPES)
647 }
648
649 #[must_use]
653 pub fn application_type(&self) -> ApplicationType {
654 self.application_type
655 .clone()
656 .unwrap_or(DEFAULT_APPLICATION_TYPE)
657 }
658
659 #[must_use]
665 pub fn token_endpoint_auth_method(&self) -> &OAuthClientAuthenticationMethod {
666 self.token_endpoint_auth_method
667 .as_ref()
668 .unwrap_or(DEFAULT_TOKEN_AUTH_METHOD)
669 }
670
671 #[must_use]
682 pub fn id_token_signed_response_alg(&self) -> &JsonWebSignatureAlg {
683 self.id_token_signed_response_alg
684 .as_ref()
685 .unwrap_or(DEFAULT_SIGNING_ALGORITHM)
686 }
687
688 #[must_use]
697 pub fn id_token_encrypted_response(
698 &self,
699 ) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> {
700 self.id_token_encrypted_response_alg.as_ref().map(|alg| {
701 (
702 alg,
703 self.id_token_encrypted_response_enc
704 .as_ref()
705 .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
706 )
707 })
708 }
709
710 #[must_use]
719 pub fn userinfo_encrypted_response(
720 &self,
721 ) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> {
722 self.userinfo_encrypted_response_alg.as_ref().map(|alg| {
723 (
724 alg,
725 self.userinfo_encrypted_response_enc
726 .as_ref()
727 .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
728 )
729 })
730 }
731
732 #[must_use]
741 pub fn request_object_encryption(
742 &self,
743 ) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> {
744 self.request_object_encryption_alg.as_ref().map(|alg| {
745 (
746 alg,
747 self.request_object_encryption_enc
748 .as_ref()
749 .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
750 )
751 })
752 }
753
754 #[must_use]
758 pub fn require_auth_time(&self) -> bool {
759 self.require_auth_time.unwrap_or_default()
760 }
761
762 #[must_use]
769 pub fn require_signed_request_object(&self) -> bool {
770 self.require_signed_request_object.unwrap_or_default()
771 }
772
773 #[must_use]
780 pub fn require_pushed_authorization_requests(&self) -> bool {
781 self.require_pushed_authorization_requests
782 .unwrap_or_default()
783 }
784
785 #[must_use]
795 pub fn introspection_encrypted_response(
796 &self,
797 ) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> {
798 self.introspection_encrypted_response_alg
799 .as_ref()
800 .map(|alg| {
801 (
802 alg,
803 self.introspection_encrypted_response_enc
804 .as_ref()
805 .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
806 )
807 })
808 }
809}
810
811#[derive(Serialize, Debug, PartialEq, Eq, Clone)]
820#[serde(into = "ClientMetadataSerdeHelper")]
821pub struct VerifiedClientMetadata {
822 inner: ClientMetadata,
823}
824
825impl VerifiedClientMetadata {
826 #[must_use]
834 pub fn redirect_uris(&self) -> &[Url] {
835 match &self.redirect_uris {
836 Some(v) => v,
837 None => &[],
838 }
839 }
840}
841
842impl Deref for VerifiedClientMetadata {
843 type Target = ClientMetadata;
844
845 fn deref(&self) -> &Self::Target {
846 &self.inner
847 }
848}
849
850#[derive(Debug, Error)]
852pub enum ClientMetadataVerificationError {
853 #[error("redirect URIs are missing")]
855 MissingRedirectUris,
856
857 #[error("redirect URI with fragment: {0}")]
859 RedirectUriWithFragment(Url),
860
861 #[error("'{0}' response type not compatible with grant types")]
863 IncoherentResponseType(ResponseType),
864
865 #[error("jwks_uri and jwks are mutually exclusive")]
868 JwksUriAndJwksMutuallyExclusive,
869
870 #[error("{0}'s URL doesn't use a https scheme: {1}")]
872 UrlNonHttpsScheme(&'static str, Url),
873
874 #[error("missing JWK Set for token auth method")]
876 MissingJwksForTokenMethod,
877
878 #[error("none signing alg unauthorized for {0}")]
880 UnauthorizedSigningAlgNone(&'static str),
881
882 #[error("{0} missing auth signing algorithm")]
886 MissingAuthSigningAlg(&'static str),
887
888 #[error("ID Token signing alg is none")]
891 IdTokenSigningAlgNone,
892
893 #[error("{0} missing encryption alg value")]
895 MissingEncryptionAlg(&'static str),
896}
897
898#[serde_as]
900#[skip_serializing_none]
901#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
902pub struct ClientRegistrationResponse {
903 pub client_id: String,
905
906 #[serde(default)]
908 pub client_secret: Option<String>,
909
910 #[serde(default)]
912 #[serde_as(as = "Option<TimestampSeconds<i64>>")]
913 pub client_id_issued_at: Option<DateTime<Utc>>,
914
915 #[serde(default)]
919 #[serde_as(as = "Option<TimestampSeconds<i64>>")]
920 pub client_secret_expires_at: Option<DateTime<Utc>>,
921}
922
923#[cfg(test)]
924mod tests {
925 use assert_matches::assert_matches;
926 use mas_iana::{
927 jose::{JsonWebEncryptionAlg, JsonWebEncryptionEnc, JsonWebSignatureAlg},
928 oauth::{OAuthAuthorizationEndpointResponseType, OAuthClientAuthenticationMethod},
929 };
930 use mas_jose::jwk::PublicJsonWebKeySet;
931 use url::Url;
932
933 use super::{ClientMetadata, ClientMetadataVerificationError};
934 use crate::{requests::GrantType, response_type::ResponseType};
935
936 fn valid_client_metadata() -> ClientMetadata {
937 ClientMetadata {
938 redirect_uris: Some(vec![Url::parse("http://localhost/oidc").unwrap()]),
939 ..Default::default()
940 }
941 }
942
943 fn jwks() -> PublicJsonWebKeySet {
944 serde_json::from_value(serde_json::json!({
945 "keys": [
946 {
947 "alg": "RS256",
948 "kty": "RSA",
949 "n": "tCwhHOxX_ylh5kVwfVqW7QIBTIsPjkjCjVCppDrynuF_3msEdtEaG64eJUz84ODFNMCC0BQ57G7wrKQVWkdSDxWUEqGk2BixBiHJRWZdofz1WOBTdPVicvHW5Zl_aIt7uXWMdOp_SODw-O2y2f05EqbFWFnR2-1y9K8KbiOp82CD72ny1Jbb_3PxTs2Z0F4ECAtTzpDteaJtjeeueRjr7040JAjQ-5fpL5D1g8x14LJyVIo-FL_y94NPFbMp7UCi69CIfVHXFO8WYFz949og-47mWRrID5lS4zpx-QLuvNhUb_lSqmylUdQB3HpRdOcYdj3xwy4MHJuu7tTaf0AmCQ",
950 "use": "sig",
951 "kid": "d98f49bc6ca4581eae8dfadd494fce10ea23aab0",
952 "e": "AQAB"
953 }
954 ]
955 })).unwrap()
956 }
957
958 #[test]
959 fn validate_required_metadata() {
960 let metadata = valid_client_metadata();
961 metadata.validate().unwrap();
962 }
963
964 #[test]
965 fn validate_redirect_uris() {
966 let mut metadata = ClientMetadata::default();
967
968 assert_matches!(
970 metadata.clone().validate(),
971 Err(ClientMetadataVerificationError::MissingRedirectUris)
972 );
973
974 let wrong_uri = Url::parse("http://localhost/#fragment").unwrap();
976 metadata.redirect_uris = Some(vec![
977 Url::parse("http://localhost/").unwrap(),
978 wrong_uri.clone(),
979 ]);
980 let uri = assert_matches!(
981 metadata.clone().validate(),
982 Err(ClientMetadataVerificationError::RedirectUriWithFragment(uri)) => uri
983 );
984 assert_eq!(uri, wrong_uri);
985
986 metadata.redirect_uris = Some(vec![
988 Url::parse("http://localhost/").unwrap(),
989 Url::parse("http://localhost/oidc").unwrap(),
990 Url::parse("http://localhost/?oidc").unwrap(),
991 Url::parse("http://localhost/my-client?oidc").unwrap(),
992 ]);
993 metadata.validate().unwrap();
994 }
995
996 #[test]
997 #[allow(clippy::too_many_lines)]
998 fn validate_response_types() {
999 let mut metadata = valid_client_metadata();
1000
1001 metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::Code.into()]);
1004 metadata.clone().validate().unwrap();
1005
1006 let response_type: ResponseType =
1008 OAuthAuthorizationEndpointResponseType::CodeIdToken.into();
1009 metadata.response_types = Some(vec![response_type.clone()]);
1010 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1011 assert_eq!(res, response_type);
1012
1013 let response_type: ResponseType =
1015 OAuthAuthorizationEndpointResponseType::CodeIdTokenToken.into();
1016 metadata.response_types = Some(vec![response_type.clone()]);
1017 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1018 assert_eq!(res, response_type);
1019
1020 let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::CodeToken.into();
1022 metadata.response_types = Some(vec![response_type.clone()]);
1023 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1024 assert_eq!(res, response_type);
1025
1026 let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::IdToken.into();
1028 metadata.response_types = Some(vec![response_type.clone()]);
1029 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1030 assert_eq!(res, response_type);
1031
1032 let response_type: ResponseType =
1034 OAuthAuthorizationEndpointResponseType::IdTokenToken.into();
1035 metadata.response_types = Some(vec![response_type.clone()]);
1036 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1037 assert_eq!(res, response_type);
1038
1039 let response_type: ResponseType =
1041 OAuthAuthorizationEndpointResponseType::IdTokenToken.into();
1042 metadata.response_types = Some(vec![response_type.clone()]);
1043 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1044 assert_eq!(res, response_type);
1045
1046 metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::None.into()]);
1048 metadata.clone().validate().unwrap();
1049
1050 metadata.grant_types = Some(vec![GrantType::Implicit]);
1052 let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::Code.into();
1054 metadata.response_types = Some(vec![response_type.clone()]);
1055 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1056 assert_eq!(res, response_type);
1057
1058 let response_type: ResponseType =
1060 OAuthAuthorizationEndpointResponseType::CodeIdToken.into();
1061 metadata.response_types = Some(vec![response_type.clone()]);
1062 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1063 assert_eq!(res, response_type);
1064
1065 let response_type: ResponseType =
1067 OAuthAuthorizationEndpointResponseType::CodeIdTokenToken.into();
1068 metadata.response_types = Some(vec![response_type.clone()]);
1069 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1070 assert_eq!(res, response_type);
1071
1072 let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::CodeToken.into();
1074 metadata.response_types = Some(vec![response_type.clone()]);
1075 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1076 assert_eq!(res, response_type);
1077
1078 metadata.response_types =
1080 Some(vec![OAuthAuthorizationEndpointResponseType::IdToken.into()]);
1081 metadata.clone().validate().unwrap();
1082
1083 metadata.response_types = Some(vec![
1085 OAuthAuthorizationEndpointResponseType::IdTokenToken.into(),
1086 ]);
1087 metadata.clone().validate().unwrap();
1088
1089 metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::Token.into()]);
1091 metadata.clone().validate().unwrap();
1092
1093 metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::None.into()]);
1095 metadata.clone().validate().unwrap();
1096
1097 metadata.grant_types = Some(vec![GrantType::AuthorizationCode, GrantType::Implicit]);
1099 metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::Code.into()]);
1101 metadata.clone().validate().unwrap();
1102
1103 metadata.response_types = Some(vec![
1105 OAuthAuthorizationEndpointResponseType::CodeIdToken.into(),
1106 ]);
1107 metadata.clone().validate().unwrap();
1108
1109 metadata.response_types = Some(vec![
1111 OAuthAuthorizationEndpointResponseType::CodeIdTokenToken.into(),
1112 ]);
1113 metadata.clone().validate().unwrap();
1114
1115 metadata.response_types = Some(vec![
1117 OAuthAuthorizationEndpointResponseType::CodeToken.into(),
1118 ]);
1119 metadata.clone().validate().unwrap();
1120
1121 metadata.response_types =
1123 Some(vec![OAuthAuthorizationEndpointResponseType::IdToken.into()]);
1124 metadata.clone().validate().unwrap();
1125
1126 metadata.response_types = Some(vec![
1128 OAuthAuthorizationEndpointResponseType::IdTokenToken.into(),
1129 ]);
1130 metadata.clone().validate().unwrap();
1131
1132 metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::Token.into()]);
1134 metadata.clone().validate().unwrap();
1135
1136 metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::None.into()]);
1138 metadata.clone().validate().unwrap();
1139
1140 metadata.grant_types = Some(vec![GrantType::RefreshToken, GrantType::ClientCredentials]);
1142 let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::Code.into();
1144 metadata.response_types = Some(vec![response_type.clone()]);
1145 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1146 assert_eq!(res, response_type);
1147
1148 let response_type: ResponseType =
1150 OAuthAuthorizationEndpointResponseType::CodeIdToken.into();
1151 metadata.response_types = Some(vec![response_type.clone()]);
1152 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1153 assert_eq!(res, response_type);
1154
1155 let response_type: ResponseType =
1157 OAuthAuthorizationEndpointResponseType::CodeIdTokenToken.into();
1158 metadata.response_types = Some(vec![response_type.clone()]);
1159 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1160 assert_eq!(res, response_type);
1161
1162 let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::CodeToken.into();
1164 metadata.response_types = Some(vec![response_type.clone()]);
1165 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1166 assert_eq!(res, response_type);
1167
1168 let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::IdToken.into();
1170 metadata.response_types = Some(vec![response_type.clone()]);
1171 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1172 assert_eq!(res, response_type);
1173
1174 let response_type: ResponseType =
1176 OAuthAuthorizationEndpointResponseType::IdTokenToken.into();
1177 metadata.response_types = Some(vec![response_type.clone()]);
1178 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1179 assert_eq!(res, response_type);
1180
1181 let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::Token.into();
1183 metadata.response_types = Some(vec![response_type.clone()]);
1184 let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1185 assert_eq!(res, response_type);
1186
1187 metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::None.into()]);
1189 metadata.validate().unwrap();
1190 }
1191
1192 #[test]
1193 fn validate_jwks() {
1194 let mut metadata = valid_client_metadata();
1195
1196 metadata.jwks_uri = Some(Url::parse("http://localhost/jwks").unwrap());
1198 metadata.clone().validate().unwrap();
1199
1200 metadata.jwks = Some(jwks());
1202 assert_matches!(
1203 metadata.clone().validate(),
1204 Err(ClientMetadataVerificationError::JwksUriAndJwksMutuallyExclusive)
1205 );
1206
1207 metadata.jwks_uri = None;
1209 metadata.validate().unwrap();
1210 }
1211
1212 #[test]
1213 fn validate_sector_identifier_uri() {
1214 let mut metadata = valid_client_metadata();
1215
1216 let identifier_uri = Url::parse("http://localhost/").unwrap();
1218 metadata.sector_identifier_uri = Some(identifier_uri.clone());
1219 let (field, url) = assert_matches!(
1220 metadata.clone().validate(),
1221 Err(ClientMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1222 );
1223 assert_eq!(field, "sector_identifier_uri");
1224 assert_eq!(url, identifier_uri);
1225
1226 metadata.sector_identifier_uri = Some(Url::parse("https://localhost/").unwrap());
1228 metadata.validate().unwrap();
1229 }
1230
1231 #[test]
1232 fn validate_token_endpoint_auth_method() {
1233 let mut metadata = valid_client_metadata();
1234
1235 metadata.token_endpoint_auth_signing_alg = Some(JsonWebSignatureAlg::None);
1237 let field = assert_matches!(
1238 metadata.clone().validate(),
1239 Err(ClientMetadataVerificationError::UnauthorizedSigningAlgNone(field)) => field
1240 );
1241 assert_eq!(field, "token_endpoint");
1242
1243 metadata.token_endpoint_auth_method = Some(OAuthClientAuthenticationMethod::PrivateKeyJwt);
1245 metadata.token_endpoint_auth_signing_alg = Some(JsonWebSignatureAlg::Rs256);
1246
1247 assert_matches!(
1249 metadata.clone().validate(),
1250 Err(ClientMetadataVerificationError::MissingJwksForTokenMethod)
1251 );
1252
1253 metadata.jwks_uri = Some(Url::parse("https://localhost/jwks").unwrap());
1255 metadata.clone().validate().unwrap();
1256
1257 metadata.jwks_uri = None;
1259 metadata.jwks = Some(jwks());
1260 metadata.clone().validate().unwrap();
1261
1262 metadata.token_endpoint_auth_signing_alg = None;
1264 let field = assert_matches!(
1265 metadata.clone().validate(),
1266 Err(ClientMetadataVerificationError::MissingAuthSigningAlg(field)) => field
1267 );
1268 assert_eq!(field, "token_endpoint");
1269
1270 metadata.token_endpoint_auth_method =
1272 Some(OAuthClientAuthenticationMethod::ClientSecretJwt);
1273 metadata.jwks = None;
1274
1275 let field = assert_matches!(
1277 metadata.clone().validate(),
1278 Err(ClientMetadataVerificationError::MissingAuthSigningAlg(field)) => field
1279 );
1280 assert_eq!(field, "token_endpoint");
1281
1282 metadata.token_endpoint_auth_signing_alg = Some(JsonWebSignatureAlg::Rs256);
1284 metadata.validate().unwrap();
1285 }
1286
1287 #[test]
1288 fn validate_id_token_signed_response_alg() {
1289 let mut metadata = valid_client_metadata();
1290 metadata.id_token_signed_response_alg = Some(JsonWebSignatureAlg::None);
1291 metadata.grant_types = Some(vec![GrantType::AuthorizationCode, GrantType::Implicit]);
1292
1293 metadata.response_types = Some(vec![
1295 OAuthAuthorizationEndpointResponseType::CodeIdToken.into(),
1296 ]);
1297 assert_matches!(
1298 metadata.clone().validate(),
1299 Err(ClientMetadataVerificationError::IdTokenSigningAlgNone)
1300 );
1301
1302 metadata.response_types = Some(vec![
1304 OAuthAuthorizationEndpointResponseType::CodeIdTokenToken.into(),
1305 ]);
1306 assert_matches!(
1307 metadata.clone().validate(),
1308 Err(ClientMetadataVerificationError::IdTokenSigningAlgNone)
1309 );
1310
1311 metadata.response_types =
1313 Some(vec![OAuthAuthorizationEndpointResponseType::IdToken.into()]);
1314 assert_matches!(
1315 metadata.clone().validate(),
1316 Err(ClientMetadataVerificationError::IdTokenSigningAlgNone)
1317 );
1318
1319 metadata.response_types = Some(vec![
1321 OAuthAuthorizationEndpointResponseType::IdTokenToken.into(),
1322 ]);
1323 assert_matches!(
1324 metadata.clone().validate(),
1325 Err(ClientMetadataVerificationError::IdTokenSigningAlgNone)
1326 );
1327
1328 metadata.response_types = Some(vec![
1330 OAuthAuthorizationEndpointResponseType::Code.into(),
1331 OAuthAuthorizationEndpointResponseType::CodeToken.into(),
1332 OAuthAuthorizationEndpointResponseType::Token.into(),
1333 OAuthAuthorizationEndpointResponseType::None.into(),
1334 ]);
1335 metadata.validate().unwrap();
1336 }
1337
1338 #[test]
1339 fn validate_id_token_encrypted_response() {
1340 let mut metadata = valid_client_metadata();
1341 metadata.id_token_encrypted_response_enc = Some(JsonWebEncryptionEnc::A128CbcHs256);
1342
1343 let field = assert_matches!(
1345 metadata.clone().validate(),
1346 Err(ClientMetadataVerificationError::MissingEncryptionAlg(field)) => field
1347 );
1348 assert_eq!(field, "id_token");
1349
1350 metadata.id_token_encrypted_response_alg = Some(JsonWebEncryptionAlg::RsaOaep);
1352 metadata.validate().unwrap();
1353 }
1354
1355 #[test]
1356 fn validate_userinfo_encrypted_response() {
1357 let mut metadata = valid_client_metadata();
1358 metadata.userinfo_encrypted_response_enc = Some(JsonWebEncryptionEnc::A128CbcHs256);
1359
1360 let field = assert_matches!(
1362 metadata.clone().validate(),
1363 Err(ClientMetadataVerificationError::MissingEncryptionAlg(field)) => field
1364 );
1365 assert_eq!(field, "userinfo");
1366
1367 metadata.userinfo_encrypted_response_alg = Some(JsonWebEncryptionAlg::RsaOaep);
1369 metadata.validate().unwrap();
1370 }
1371
1372 #[test]
1373 fn validate_request_object_encryption() {
1374 let mut metadata = valid_client_metadata();
1375 metadata.request_object_encryption_enc = Some(JsonWebEncryptionEnc::A128CbcHs256);
1376
1377 let field = assert_matches!(
1379 metadata.clone().validate(),
1380 Err(ClientMetadataVerificationError::MissingEncryptionAlg(field)) => field
1381 );
1382 assert_eq!(field, "request_object");
1383
1384 metadata.request_object_encryption_alg = Some(JsonWebEncryptionAlg::RsaOaep);
1386 metadata.validate().unwrap();
1387 }
1388
1389 #[test]
1390 fn validate_initiate_login_uri() {
1391 let mut metadata = valid_client_metadata();
1392
1393 let initiate_uri = Url::parse("http://localhost/").unwrap();
1395 metadata.initiate_login_uri = Some(initiate_uri.clone());
1396 let (field, url) = assert_matches!(
1397 metadata.clone().validate(),
1398 Err(ClientMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1399 );
1400 assert_eq!(field, "initiate_login_uri");
1401 assert_eq!(url, initiate_uri);
1402
1403 metadata.initiate_login_uri = Some(Url::parse("https://localhost/").unwrap());
1405 metadata.validate().unwrap();
1406 }
1407
1408 #[test]
1409 fn validate_introspection_encrypted_response() {
1410 let mut metadata = valid_client_metadata();
1411 metadata.introspection_encrypted_response_enc = Some(JsonWebEncryptionEnc::A128CbcHs256);
1412
1413 let field = assert_matches!(
1415 metadata.clone().validate(),
1416 Err(ClientMetadataVerificationError::MissingEncryptionAlg(field)) => field
1417 );
1418 assert_eq!(field, "introspection");
1419
1420 metadata.introspection_encrypted_response_alg = Some(JsonWebEncryptionAlg::RsaOaep);
1422 metadata.validate().unwrap();
1423 }
1424}