1use std::{fmt, ops::Deref};
12
13use language_tags::LanguageTag;
14use mas_iana::{
15 jose::{JsonWebEncryptionAlg, JsonWebEncryptionEnc, JsonWebSignatureAlg},
16 oauth::{OAuthAccessTokenType, OAuthClientAuthenticationMethod, PkceCodeChallengeMethod},
17};
18use serde::{Deserialize, Serialize};
19use serde_with::{
20 DeserializeFromStr, SerializeDisplay, StringWithSeparator, formats::SpaceSeparator, serde_as,
21 skip_serializing_none,
22};
23use thiserror::Error;
24use url::Url;
25
26use crate::{
27 requests::{Display, GrantType, Prompt, ResponseMode},
28 response_type::ResponseType,
29};
30
31#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug)]
34pub enum AuthenticationMethodOrAccessTokenType {
35 AuthenticationMethod(OAuthClientAuthenticationMethod),
37
38 AccessTokenType(OAuthAccessTokenType),
40
41 Unknown(String),
47}
48
49impl core::fmt::Display for AuthenticationMethodOrAccessTokenType {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 match self {
52 Self::AuthenticationMethod(m) => m.fmt(f),
53 Self::AccessTokenType(t) => t.fmt(f),
54 Self::Unknown(s) => s.fmt(f),
55 }
56 }
57}
58
59impl core::str::FromStr for AuthenticationMethodOrAccessTokenType {
60 type Err = core::convert::Infallible;
61
62 fn from_str(s: &str) -> Result<Self, Self::Err> {
63 match OAuthClientAuthenticationMethod::from_str(s) {
64 Ok(OAuthClientAuthenticationMethod::Unknown(_)) | Err(_) => {}
65 Ok(m) => return Ok(m.into()),
66 }
67
68 match OAuthAccessTokenType::from_str(s) {
69 Ok(OAuthAccessTokenType::Unknown(_)) | Err(_) => {}
70 Ok(m) => return Ok(m.into()),
71 }
72
73 Ok(Self::Unknown(s.to_owned()))
74 }
75}
76
77impl AuthenticationMethodOrAccessTokenType {
78 #[must_use]
81 pub fn authentication_method(&self) -> Option<&OAuthClientAuthenticationMethod> {
82 match self {
83 Self::AuthenticationMethod(m) => Some(m),
84 _ => None,
85 }
86 }
87
88 #[must_use]
91 pub fn access_token_type(&self) -> Option<&OAuthAccessTokenType> {
92 match self {
93 Self::AccessTokenType(t) => Some(t),
94 _ => None,
95 }
96 }
97}
98
99impl From<OAuthClientAuthenticationMethod> for AuthenticationMethodOrAccessTokenType {
100 fn from(t: OAuthClientAuthenticationMethod) -> Self {
101 Self::AuthenticationMethod(t)
102 }
103}
104
105impl From<OAuthAccessTokenType> for AuthenticationMethodOrAccessTokenType {
106 fn from(t: OAuthAccessTokenType) -> Self {
107 Self::AccessTokenType(t)
108 }
109}
110
111#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug)]
113pub enum ApplicationType {
114 Web,
116
117 Native,
119
120 Unknown(String),
122}
123
124impl core::fmt::Display for ApplicationType {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 match self {
127 Self::Web => f.write_str("web"),
128 Self::Native => f.write_str("native"),
129 Self::Unknown(s) => f.write_str(s),
130 }
131 }
132}
133
134impl core::str::FromStr for ApplicationType {
135 type Err = core::convert::Infallible;
136
137 fn from_str(s: &str) -> Result<Self, Self::Err> {
138 match s {
139 "web" => Ok(Self::Web),
140 "native" => Ok(Self::Native),
141 s => Ok(Self::Unknown(s.to_owned())),
142 }
143 }
144}
145
146#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug)]
152pub enum SubjectType {
153 Public,
155
156 Pairwise,
160
161 Unknown(String),
163}
164
165impl core::fmt::Display for SubjectType {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 match self {
168 Self::Public => f.write_str("public"),
169 Self::Pairwise => f.write_str("pairwise"),
170 Self::Unknown(s) => f.write_str(s),
171 }
172 }
173}
174
175impl core::str::FromStr for SubjectType {
176 type Err = core::convert::Infallible;
177
178 fn from_str(s: &str) -> Result<Self, Self::Err> {
179 match s {
180 "public" => Ok(Self::Public),
181 "pairwise" => Ok(Self::Pairwise),
182 s => Ok(Self::Unknown(s.to_owned())),
183 }
184 }
185}
186
187#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug)]
189pub enum ClaimType {
190 Normal,
192
193 Aggregated,
196
197 Distributed,
200
201 Unknown(String),
203}
204
205impl core::fmt::Display for ClaimType {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 match self {
208 Self::Normal => f.write_str("normal"),
209 Self::Aggregated => f.write_str("aggregated"),
210 Self::Distributed => f.write_str("distributed"),
211 Self::Unknown(s) => f.write_str(s),
212 }
213 }
214}
215
216impl core::str::FromStr for ClaimType {
217 type Err = core::convert::Infallible;
218
219 fn from_str(s: &str) -> Result<Self, Self::Err> {
220 match s {
221 "normal" => Ok(Self::Normal),
222 "aggregated" => Ok(Self::Aggregated),
223 "distributed" => Ok(Self::Distributed),
224 s => Ok(Self::Unknown(s.to_owned())),
225 }
226 }
227}
228
229#[derive(
233 SerializeDisplay, DeserializeFromStr, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
234)]
235#[non_exhaustive]
236pub enum AccountManagementAction {
237 Profile,
241
242 SessionsList,
246
247 SessionView,
251
252 SessionEnd,
256
257 AccountDeactivate,
261
262 CrossSigningReset,
266
267 Unknown(String),
269}
270
271impl core::fmt::Display for AccountManagementAction {
272 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
273 match self {
274 Self::Profile => write!(f, "org.matrix.profile"),
275 Self::SessionsList => write!(f, "org.matrix.sessions_list"),
276 Self::SessionView => write!(f, "org.matrix.session_view"),
277 Self::SessionEnd => write!(f, "org.matrix.session_end"),
278 Self::AccountDeactivate => write!(f, "org.matrix.account_deactivate"),
279 Self::CrossSigningReset => write!(f, "org.matrix.cross_signing_reset"),
280 Self::Unknown(value) => write!(f, "{value}"),
281 }
282 }
283}
284
285impl core::str::FromStr for AccountManagementAction {
286 type Err = core::convert::Infallible;
287
288 fn from_str(s: &str) -> Result<Self, Self::Err> {
289 match s {
290 "org.matrix.profile" => Ok(Self::Profile),
291 "org.matrix.sessions_list" => Ok(Self::SessionsList),
292 "org.matrix.session_view" => Ok(Self::SessionView),
293 "org.matrix.session_end" => Ok(Self::SessionEnd),
294 "org.matrix.account_deactivate" => Ok(Self::AccountDeactivate),
295 "org.matrix.cross_signing_reset" => Ok(Self::CrossSigningReset),
296 value => Ok(Self::Unknown(value.to_owned())),
297 }
298 }
299}
300
301pub static DEFAULT_RESPONSE_MODES_SUPPORTED: &[ResponseMode] =
303 &[ResponseMode::Query, ResponseMode::Fragment];
304
305pub static DEFAULT_GRANT_TYPES_SUPPORTED: &[GrantType] =
307 &[GrantType::AuthorizationCode, GrantType::Implicit];
308
309pub static DEFAULT_AUTH_METHODS_SUPPORTED: &[OAuthClientAuthenticationMethod] =
312 &[OAuthClientAuthenticationMethod::ClientSecretBasic];
313
314pub static DEFAULT_CLAIM_TYPES_SUPPORTED: &[ClaimType] = &[ClaimType::Normal];
316
317#[skip_serializing_none]
323#[derive(Debug, Serialize, Deserialize, Clone, Default)]
324pub struct ProviderMetadata {
325 pub issuer: Option<String>,
331
332 pub authorization_endpoint: Option<Url>,
339
340 pub token_endpoint: Option<Url>,
347
348 pub jwks_uri: Option<Url>,
354
355 pub registration_endpoint: Option<Url>,
362
363 pub scopes_supported: Option<Vec<String>>,
369
370 pub response_types_supported: Option<Vec<ResponseType>>,
377
378 pub response_modes_supported: Option<Vec<ResponseMode>>,
385
386 pub grant_types_supported: Option<Vec<GrantType>>,
393
394 pub token_endpoint_auth_methods_supported: Option<Vec<OAuthClientAuthenticationMethod>>,
399
400 pub token_endpoint_auth_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
410
411 pub service_documentation: Option<Url>,
414
415 pub ui_locales_supported: Option<Vec<LanguageTag>>,
420
421 pub op_policy_uri: Option<Url>,
425
426 pub op_tos_uri: Option<Url>,
429
430 pub revocation_endpoint: Option<Url>,
437
438 pub revocation_endpoint_auth_methods_supported: Option<Vec<OAuthClientAuthenticationMethod>>,
443
444 pub revocation_endpoint_auth_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
454
455 pub introspection_endpoint: Option<Url>,
461
462 pub introspection_endpoint_auth_methods_supported:
465 Option<Vec<AuthenticationMethodOrAccessTokenType>>,
466
467 pub introspection_endpoint_auth_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
477
478 pub code_challenge_methods_supported: Option<Vec<PkceCodeChallengeMethod>>,
483
484 pub userinfo_endpoint: Option<Url>,
488
489 pub acr_values_supported: Option<Vec<String>>,
492
493 pub subject_types_supported: Option<Vec<SubjectType>>,
498
499 pub id_token_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
504
505 pub id_token_encryption_alg_values_supported: Option<Vec<JsonWebEncryptionAlg>>,
508
509 pub id_token_encryption_enc_values_supported: Option<Vec<JsonWebEncryptionEnc>>,
512
513 pub userinfo_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
516
517 pub userinfo_encryption_alg_values_supported: Option<Vec<JsonWebEncryptionAlg>>,
520
521 pub userinfo_encryption_enc_values_supported: Option<Vec<JsonWebEncryptionEnc>>,
524
525 pub request_object_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
528
529 pub request_object_encryption_alg_values_supported: Option<Vec<JsonWebEncryptionAlg>>,
532
533 pub request_object_encryption_enc_values_supported: Option<Vec<JsonWebEncryptionEnc>>,
536
537 pub display_values_supported: Option<Vec<Display>>,
540
541 pub claim_types_supported: Option<Vec<ClaimType>>,
546
547 pub claims_supported: Option<Vec<String>>,
550
551 pub claims_locales_supported: Option<Vec<LanguageTag>>,
554
555 pub claims_parameter_supported: Option<bool>,
560
561 pub request_parameter_supported: Option<bool>,
566
567 pub request_uri_parameter_supported: Option<bool>,
572
573 pub require_request_uri_registration: Option<bool>,
578
579 pub require_signed_request_object: Option<bool>,
586
587 pub pushed_authorization_request_endpoint: Option<Url>,
592
593 pub require_pushed_authorization_requests: Option<bool>,
598
599 pub prompt_values_supported: Option<Vec<Prompt>>,
606
607 pub device_authorization_endpoint: Option<Url>,
611
612 pub end_session_endpoint: Option<Url>,
616
617 pub account_management_uri: Option<Url>,
622
623 pub account_management_actions_supported: Option<Vec<AccountManagementAction>>,
627}
628
629impl ProviderMetadata {
630 pub fn validate(
644 self,
645 issuer: &str,
646 ) -> Result<VerifiedProviderMetadata, ProviderMetadataVerificationError> {
647 let metadata = self.insecure_verify_metadata()?;
648
649 if metadata.issuer() != issuer {
650 return Err(ProviderMetadataVerificationError::IssuerUrlsDontMatch {
651 expected: issuer.to_owned(),
652 actual: metadata.issuer().to_owned(),
653 });
654 }
655
656 validate_url(
657 "issuer",
658 &metadata
659 .issuer()
660 .parse()
661 .map_err(|_| ProviderMetadataVerificationError::IssuerNotUrl)?,
662 ExtraUrlRestrictions::NoQueryOrFragment,
663 )?;
664
665 validate_url(
666 "authorization_endpoint",
667 metadata.authorization_endpoint(),
668 ExtraUrlRestrictions::NoFragment,
669 )?;
670
671 validate_url(
672 "token_endpoint",
673 metadata.token_endpoint(),
674 ExtraUrlRestrictions::NoFragment,
675 )?;
676
677 validate_url("jwks_uri", metadata.jwks_uri(), ExtraUrlRestrictions::None)?;
678
679 if let Some(url) = &metadata.registration_endpoint {
680 validate_url("registration_endpoint", url, ExtraUrlRestrictions::None)?;
681 }
682
683 if let Some(scopes) = &metadata.scopes_supported
684 && !scopes.iter().any(|s| s == "openid")
685 {
686 return Err(ProviderMetadataVerificationError::ScopesMissingOpenid);
687 }
688
689 validate_signing_alg_values_supported(
690 "token_endpoint",
691 metadata
692 .token_endpoint_auth_signing_alg_values_supported
693 .iter()
694 .flatten(),
695 )?;
696
697 if let Some(url) = &metadata.revocation_endpoint {
698 validate_url("revocation_endpoint", url, ExtraUrlRestrictions::NoFragment)?;
699 }
700
701 validate_signing_alg_values_supported(
702 "revocation_endpoint",
703 metadata
704 .revocation_endpoint_auth_signing_alg_values_supported
705 .iter()
706 .flatten(),
707 )?;
708
709 if let Some(url) = &metadata.introspection_endpoint {
710 validate_url("introspection_endpoint", url, ExtraUrlRestrictions::None)?;
711 }
712
713 validate_signing_alg_values_supported(
714 "introspection_endpoint",
715 metadata
716 .introspection_endpoint_auth_signing_alg_values_supported
717 .iter()
718 .flatten(),
719 )?;
720
721 if let Some(url) = &metadata.userinfo_endpoint {
722 validate_url("userinfo_endpoint", url, ExtraUrlRestrictions::None)?;
723 }
724
725 if let Some(url) = &metadata.pushed_authorization_request_endpoint {
726 validate_url(
727 "pushed_authorization_request_endpoint",
728 url,
729 ExtraUrlRestrictions::None,
730 )?;
731 }
732
733 if let Some(url) = &metadata.end_session_endpoint {
734 validate_url("end_session_endpoint", url, ExtraUrlRestrictions::None)?;
735 }
736
737 Ok(metadata)
738 }
739
740 pub fn insecure_verify_metadata(
762 self,
763 ) -> Result<VerifiedProviderMetadata, ProviderMetadataVerificationError> {
764 self.issuer
765 .as_ref()
766 .ok_or(ProviderMetadataVerificationError::MissingIssuer)?;
767
768 self.authorization_endpoint
769 .as_ref()
770 .ok_or(ProviderMetadataVerificationError::MissingAuthorizationEndpoint)?;
771
772 self.token_endpoint
773 .as_ref()
774 .ok_or(ProviderMetadataVerificationError::MissingTokenEndpoint)?;
775
776 self.jwks_uri
777 .as_ref()
778 .ok_or(ProviderMetadataVerificationError::MissingJwksUri)?;
779
780 self.response_types_supported
781 .as_ref()
782 .ok_or(ProviderMetadataVerificationError::MissingResponseTypesSupported)?;
783
784 self.subject_types_supported
785 .as_ref()
786 .ok_or(ProviderMetadataVerificationError::MissingSubjectTypesSupported)?;
787
788 self.id_token_signing_alg_values_supported
789 .as_ref()
790 .ok_or(ProviderMetadataVerificationError::MissingIdTokenSigningAlgValuesSupported)?;
791
792 Ok(VerifiedProviderMetadata { inner: self })
793 }
794
795 #[must_use]
800 pub fn response_modes_supported(&self) -> &[ResponseMode] {
801 self.response_modes_supported
802 .as_deref()
803 .unwrap_or(DEFAULT_RESPONSE_MODES_SUPPORTED)
804 }
805
806 #[must_use]
811 pub fn grant_types_supported(&self) -> &[GrantType] {
812 self.grant_types_supported
813 .as_deref()
814 .unwrap_or(DEFAULT_GRANT_TYPES_SUPPORTED)
815 }
816
817 #[must_use]
822 pub fn token_endpoint_auth_methods_supported(&self) -> &[OAuthClientAuthenticationMethod] {
823 self.token_endpoint_auth_methods_supported
824 .as_deref()
825 .unwrap_or(DEFAULT_AUTH_METHODS_SUPPORTED)
826 }
827
828 #[must_use]
833 pub fn revocation_endpoint_auth_methods_supported(&self) -> &[OAuthClientAuthenticationMethod] {
834 self.revocation_endpoint_auth_methods_supported
835 .as_deref()
836 .unwrap_or(DEFAULT_AUTH_METHODS_SUPPORTED)
837 }
838
839 #[must_use]
844 pub fn claim_types_supported(&self) -> &[ClaimType] {
845 self.claim_types_supported
846 .as_deref()
847 .unwrap_or(DEFAULT_CLAIM_TYPES_SUPPORTED)
848 }
849
850 #[must_use]
855 pub fn claims_parameter_supported(&self) -> bool {
856 self.claims_parameter_supported.unwrap_or(false)
857 }
858
859 #[must_use]
864 pub fn request_parameter_supported(&self) -> bool {
865 self.request_parameter_supported.unwrap_or(false)
866 }
867
868 #[must_use]
873 pub fn request_uri_parameter_supported(&self) -> bool {
874 self.request_uri_parameter_supported.unwrap_or(true)
875 }
876
877 #[must_use]
882 pub fn require_request_uri_registration(&self) -> bool {
883 self.require_request_uri_registration.unwrap_or(false)
884 }
885
886 #[must_use]
891 pub fn require_signed_request_object(&self) -> bool {
892 self.require_signed_request_object.unwrap_or(false)
893 }
894
895 #[must_use]
900 pub fn require_pushed_authorization_requests(&self) -> bool {
901 self.require_pushed_authorization_requests.unwrap_or(false)
902 }
903}
904
905#[derive(Debug, Clone)]
914pub struct VerifiedProviderMetadata {
915 inner: ProviderMetadata,
916}
917
918impl VerifiedProviderMetadata {
919 #[must_use]
921 pub fn issuer(&self) -> &str {
922 match &self.issuer {
923 Some(u) => u,
924 None => unreachable!(),
925 }
926 }
927
928 #[must_use]
930 pub fn authorization_endpoint(&self) -> &Url {
931 match &self.authorization_endpoint {
932 Some(u) => u,
933 None => unreachable!(),
934 }
935 }
936
937 #[must_use]
939 pub fn userinfo_endpoint(&self) -> &Url {
940 match &self.userinfo_endpoint {
941 Some(u) => u,
942 None => unreachable!(),
943 }
944 }
945
946 #[must_use]
948 pub fn token_endpoint(&self) -> &Url {
949 match &self.token_endpoint {
950 Some(u) => u,
951 None => unreachable!(),
952 }
953 }
954
955 #[must_use]
957 pub fn jwks_uri(&self) -> &Url {
958 match &self.jwks_uri {
959 Some(u) => u,
960 None => unreachable!(),
961 }
962 }
963
964 #[must_use]
967 pub fn response_types_supported(&self) -> &[ResponseType] {
968 match &self.response_types_supported {
969 Some(u) => u,
970 None => unreachable!(),
971 }
972 }
973
974 #[must_use]
977 pub fn subject_types_supported(&self) -> &[SubjectType] {
978 match &self.subject_types_supported {
979 Some(u) => u,
980 None => unreachable!(),
981 }
982 }
983
984 #[must_use]
987 pub fn id_token_signing_alg_values_supported(&self) -> &[JsonWebSignatureAlg] {
988 match &self.id_token_signing_alg_values_supported {
989 Some(u) => u,
990 None => unreachable!(),
991 }
992 }
993}
994
995impl Deref for VerifiedProviderMetadata {
996 type Target = ProviderMetadata;
997
998 fn deref(&self) -> &Self::Target {
999 &self.inner
1000 }
1001}
1002
1003#[derive(Debug, Error)]
1005pub enum ProviderMetadataVerificationError {
1006 #[error("issuer is missing")]
1008 MissingIssuer,
1009
1010 #[error("issuer is not a valid URL")]
1012 IssuerNotUrl,
1013
1014 #[error("authorization endpoint is missing")]
1016 MissingAuthorizationEndpoint,
1017
1018 #[error("token endpoint is missing")]
1020 MissingTokenEndpoint,
1021
1022 #[error("JWK Set URI is missing")]
1024 MissingJwksUri,
1025
1026 #[error("supported response types are missing")]
1028 MissingResponseTypesSupported,
1029
1030 #[error("supported subject types are missing")]
1032 MissingSubjectTypesSupported,
1033
1034 #[error("supported ID token signing algorithm values are missing")]
1036 MissingIdTokenSigningAlgValuesSupported,
1037
1038 #[error("{0}'s URL doesn't use a https scheme: {1}")]
1040 UrlNonHttpsScheme(&'static str, Url),
1041
1042 #[error("{0}'s URL contains a query: {1}")]
1044 UrlWithQuery(&'static str, Url),
1045
1046 #[error("{0}'s URL contains a fragment: {1}")]
1048 UrlWithFragment(&'static str, Url),
1049
1050 #[error("issuer URLs don't match: expected {expected:?}, got {actual:?}")]
1052 IssuerUrlsDontMatch {
1053 expected: String,
1055 actual: String,
1057 },
1058
1059 #[error("missing openid scope")]
1061 ScopesMissingOpenid,
1062
1063 #[error("missing `code` response type")]
1065 ResponseTypesMissingCode,
1066
1067 #[error("missing `id_token` response type")]
1069 ResponseTypesMissingIdToken,
1070
1071 #[error("missing `id_token token` response type")]
1073 ResponseTypesMissingIdTokenToken,
1074
1075 #[error("missing `authorization_code` grant type")]
1077 GrantTypesMissingAuthorizationCode,
1078
1079 #[error("missing `implicit` grant type")]
1081 GrantTypesMissingImplicit,
1082
1083 #[error("{0} signing algorithm values contain `none`")]
1086 SigningAlgValuesWithNone(&'static str),
1087}
1088
1089#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1091enum ExtraUrlRestrictions {
1092 None,
1094
1095 NoFragment,
1097
1098 NoQueryOrFragment,
1100}
1101
1102impl ExtraUrlRestrictions {
1103 fn can_have_fragment(self) -> bool {
1104 self == Self::None
1105 }
1106
1107 fn can_have_query(self) -> bool {
1108 self != Self::NoQueryOrFragment
1109 }
1110}
1111
1112fn validate_url(
1116 field: &'static str,
1117 url: &Url,
1118 restrictions: ExtraUrlRestrictions,
1119) -> Result<(), ProviderMetadataVerificationError> {
1120 if url.scheme() != "https" {
1121 return Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(
1122 field,
1123 url.clone(),
1124 ));
1125 }
1126
1127 if !restrictions.can_have_query() && url.query().is_some() {
1128 return Err(ProviderMetadataVerificationError::UrlWithQuery(
1129 field,
1130 url.clone(),
1131 ));
1132 }
1133
1134 if !restrictions.can_have_fragment() && url.fragment().is_some() {
1135 return Err(ProviderMetadataVerificationError::UrlWithFragment(
1136 field,
1137 url.clone(),
1138 ));
1139 }
1140
1141 Ok(())
1142}
1143
1144fn validate_signing_alg_values_supported<'a>(
1152 endpoint: &'static str,
1153 values: impl Iterator<Item = &'a JsonWebSignatureAlg>,
1154) -> Result<(), ProviderMetadataVerificationError> {
1155 for value in values {
1156 if *value == JsonWebSignatureAlg::None {
1157 return Err(ProviderMetadataVerificationError::SigningAlgValuesWithNone(
1158 endpoint,
1159 ));
1160 }
1161 }
1162 Ok(())
1163}
1164
1165#[skip_serializing_none]
1169#[serde_as]
1170#[derive(Default, Serialize, Deserialize, Clone)]
1171pub struct RpInitiatedLogoutRequest {
1172 pub id_token_hint: Option<String>,
1177
1178 pub logout_hint: Option<String>,
1185
1186 pub client_id: Option<String>,
1195
1196 pub post_logout_redirect_uri: Option<Url>,
1202
1203 pub state: Option<String>,
1207
1208 #[serde_as(as = "Option<StringWithSeparator::<SpaceSeparator, LanguageTag>>")]
1211 #[serde(default)]
1212 pub ui_locales: Option<Vec<LanguageTag>>,
1213}
1214
1215impl fmt::Debug for RpInitiatedLogoutRequest {
1216 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1217 f.debug_struct("RpInitiatedLogoutRequest")
1218 .field("logout_hint", &self.logout_hint)
1219 .field("post_logout_redirect_uri", &self.post_logout_redirect_uri)
1220 .field("ui_locales", &self.ui_locales)
1221 .finish_non_exhaustive()
1222 }
1223}
1224
1225#[cfg(test)]
1226mod tests {
1227 use assert_matches::assert_matches;
1228 use mas_iana::{
1229 jose::JsonWebSignatureAlg,
1230 oauth::{OAuthAuthorizationEndpointResponseType, OAuthClientAuthenticationMethod},
1231 };
1232 use url::Url;
1233
1234 use super::*;
1235
1236 fn valid_provider_metadata() -> (ProviderMetadata, String) {
1237 let issuer = "https://localhost".to_owned();
1238 let metadata = ProviderMetadata {
1239 issuer: Some(issuer.clone()),
1240 authorization_endpoint: Some(Url::parse("https://localhost/auth").unwrap()),
1241 token_endpoint: Some(Url::parse("https://localhost/token").unwrap()),
1242 jwks_uri: Some(Url::parse("https://localhost/jwks").unwrap()),
1243 response_types_supported: Some(vec![
1244 OAuthAuthorizationEndpointResponseType::Code.into(),
1245 ]),
1246 subject_types_supported: Some(vec![SubjectType::Public]),
1247 id_token_signing_alg_values_supported: Some(vec![JsonWebSignatureAlg::Rs256]),
1248 ..Default::default()
1249 };
1250
1251 (metadata, issuer)
1252 }
1253
1254 #[test]
1255 fn validate_required_metadata() {
1256 let (metadata, issuer) = valid_provider_metadata();
1257 metadata.validate(&issuer).unwrap();
1258 }
1259
1260 #[test]
1261 fn validate_issuer() {
1262 let (mut metadata, issuer) = valid_provider_metadata();
1263
1264 metadata.issuer = None;
1266 assert_matches!(
1267 metadata.clone().validate(&issuer),
1268 Err(ProviderMetadataVerificationError::MissingIssuer)
1269 );
1270
1271 metadata.issuer = Some("not-an-url".to_owned());
1273 assert_matches!(
1274 metadata.clone().validate("not-an-url"),
1275 Err(ProviderMetadataVerificationError::IssuerNotUrl)
1276 );
1277
1278 metadata.issuer = Some("https://example.com/".to_owned());
1280 assert_matches!(
1281 metadata.clone().validate(&issuer),
1282 Err(ProviderMetadataVerificationError::IssuerUrlsDontMatch { .. })
1283 );
1284
1285 let issuer = "http://localhost/".to_owned();
1287 metadata.issuer = Some(issuer.clone());
1288 let (field, url) = assert_matches!(
1289 metadata.clone().validate(&issuer),
1290 Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1291 );
1292 assert_eq!(field, "issuer");
1293 assert_eq!(url.as_str(), issuer);
1294
1295 let issuer = "https://localhost/?query".to_owned();
1297 metadata.issuer = Some(issuer.clone());
1298 let (field, url) = assert_matches!(
1299 metadata.clone().validate(&issuer),
1300 Err(ProviderMetadataVerificationError::UrlWithQuery(field, url)) => (field, url)
1301 );
1302 assert_eq!(field, "issuer");
1303 assert_eq!(url.as_str(), issuer);
1304
1305 let issuer = "https://localhost/#fragment".to_owned();
1307 metadata.issuer = Some(issuer.clone());
1308 let (field, url) = assert_matches!(
1309 metadata.clone().validate(&issuer),
1310 Err(ProviderMetadataVerificationError::UrlWithFragment(field, url)) => (field, url)
1311 );
1312 assert_eq!(field, "issuer");
1313 assert_eq!(url.as_str(), issuer);
1314
1315 let issuer = "https://localhost/issuer1".to_owned();
1317 metadata.issuer = Some(issuer.clone());
1318 metadata.validate(&issuer).unwrap();
1319 }
1320
1321 #[test]
1322 fn validate_authorization_endpoint() {
1323 let (mut metadata, issuer) = valid_provider_metadata();
1324
1325 metadata.authorization_endpoint = None;
1327 assert_matches!(
1328 metadata.clone().validate(&issuer),
1329 Err(ProviderMetadataVerificationError::MissingAuthorizationEndpoint)
1330 );
1331
1332 let endpoint = Url::parse("http://localhost/auth").unwrap();
1334 metadata.authorization_endpoint = Some(endpoint.clone());
1335 let (field, url) = assert_matches!(
1336 metadata.clone().validate(&issuer),
1337 Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1338 );
1339 assert_eq!(field, "authorization_endpoint");
1340 assert_eq!(url, endpoint);
1341
1342 let endpoint = Url::parse("https://localhost/auth#fragment").unwrap();
1344 metadata.authorization_endpoint = Some(endpoint.clone());
1345 let (field, url) = assert_matches!(
1346 metadata.clone().validate(&issuer),
1347 Err(ProviderMetadataVerificationError::UrlWithFragment(field, url)) => (field, url)
1348 );
1349 assert_eq!(field, "authorization_endpoint");
1350 assert_eq!(url, endpoint);
1351
1352 metadata.authorization_endpoint = Some(Url::parse("https://localhost/auth?query").unwrap());
1354 metadata.validate(&issuer).unwrap();
1355 }
1356
1357 #[test]
1358 fn validate_token_endpoint() {
1359 let (mut metadata, issuer) = valid_provider_metadata();
1360
1361 metadata.token_endpoint = None;
1363 assert_matches!(
1364 metadata.clone().validate(&issuer),
1365 Err(ProviderMetadataVerificationError::MissingTokenEndpoint)
1366 );
1367
1368 let endpoint = Url::parse("http://localhost/token").unwrap();
1370 metadata.token_endpoint = Some(endpoint.clone());
1371 let (field, url) = assert_matches!(
1372 metadata.clone().validate(&issuer),
1373 Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1374 );
1375 assert_eq!(field, "token_endpoint");
1376 assert_eq!(url, endpoint);
1377
1378 let endpoint = Url::parse("https://localhost/token#fragment").unwrap();
1380 metadata.token_endpoint = Some(endpoint.clone());
1381 let (field, url) = assert_matches!(
1382 metadata.clone().validate(&issuer),
1383 Err(ProviderMetadataVerificationError::UrlWithFragment(field, url)) => (field, url)
1384 );
1385 assert_eq!(field, "token_endpoint");
1386 assert_eq!(url, endpoint);
1387
1388 metadata.token_endpoint = Some(Url::parse("https://localhost/token?query").unwrap());
1390 metadata.validate(&issuer).unwrap();
1391 }
1392
1393 #[test]
1394 fn validate_jwks_uri() {
1395 let (mut metadata, issuer) = valid_provider_metadata();
1396
1397 metadata.jwks_uri = None;
1399 assert_matches!(
1400 metadata.clone().validate(&issuer),
1401 Err(ProviderMetadataVerificationError::MissingJwksUri)
1402 );
1403
1404 let endpoint = Url::parse("http://localhost/jwks").unwrap();
1406 metadata.jwks_uri = Some(endpoint.clone());
1407 let (field, url) = assert_matches!(
1408 metadata.clone().validate(&issuer),
1409 Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1410 );
1411 assert_eq!(field, "jwks_uri");
1412 assert_eq!(url, endpoint);
1413
1414 metadata.jwks_uri = Some(Url::parse("https://localhost/token?query#fragment").unwrap());
1416 metadata.validate(&issuer).unwrap();
1417 }
1418
1419 #[test]
1420 fn validate_registration_endpoint() {
1421 let (mut metadata, issuer) = valid_provider_metadata();
1422
1423 let endpoint = Url::parse("http://localhost/registration").unwrap();
1425 metadata.registration_endpoint = Some(endpoint.clone());
1426 let (field, url) = assert_matches!(
1427 metadata.clone().validate(&issuer),
1428 Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1429 );
1430 assert_eq!(field, "registration_endpoint");
1431 assert_eq!(url, endpoint);
1432
1433 metadata.registration_endpoint = None;
1435 metadata.clone().validate(&issuer).unwrap();
1436
1437 metadata.registration_endpoint =
1439 Some(Url::parse("https://localhost/registration?query#fragment").unwrap());
1440 metadata.validate(&issuer).unwrap();
1441 }
1442
1443 #[test]
1444 fn validate_scopes_supported() {
1445 let (mut metadata, issuer) = valid_provider_metadata();
1446
1447 metadata.scopes_supported = Some(vec!["custom".to_owned()]);
1449 assert_matches!(
1450 metadata.clone().validate(&issuer),
1451 Err(ProviderMetadataVerificationError::ScopesMissingOpenid)
1452 );
1453
1454 metadata.scopes_supported = None;
1456 metadata.clone().validate(&issuer).unwrap();
1457
1458 metadata.scopes_supported = Some(vec!["openid".to_owned(), "custom".to_owned()]);
1460 metadata.validate(&issuer).unwrap();
1461 }
1462
1463 #[test]
1464 fn validate_response_types_supported() {
1465 let (mut metadata, issuer) = valid_provider_metadata();
1466
1467 metadata.response_types_supported = None;
1469 assert_matches!(
1470 metadata.clone().validate(&issuer),
1471 Err(ProviderMetadataVerificationError::MissingResponseTypesSupported)
1472 );
1473
1474 metadata.response_types_supported =
1476 Some(vec![OAuthAuthorizationEndpointResponseType::Code.into()]);
1477 metadata.validate(&issuer).unwrap();
1478 }
1479
1480 #[test]
1481 fn validate_token_endpoint_signing_alg_values_supported() {
1482 let (mut metadata, issuer) = valid_provider_metadata();
1483
1484 metadata.token_endpoint_auth_signing_alg_values_supported = None;
1486 metadata.token_endpoint_auth_methods_supported = None;
1487 metadata.clone().validate(&issuer).unwrap();
1488
1489 metadata.token_endpoint_auth_signing_alg_values_supported =
1491 Some(vec![JsonWebSignatureAlg::None]);
1492 let endpoint = assert_matches!(
1493 metadata.clone().validate(&issuer),
1494 Err(ProviderMetadataVerificationError::SigningAlgValuesWithNone(endpoint)) => endpoint
1495 );
1496 assert_eq!(endpoint, "token_endpoint");
1497
1498 metadata.token_endpoint_auth_signing_alg_values_supported =
1500 Some(vec![JsonWebSignatureAlg::Rs256, JsonWebSignatureAlg::EdDsa]);
1501 metadata.clone().validate(&issuer).unwrap();
1502
1503 metadata.token_endpoint_auth_methods_supported =
1505 Some(vec![OAuthClientAuthenticationMethod::ClientSecretJwt]);
1506 metadata.token_endpoint_auth_signing_alg_values_supported =
1507 Some(vec![JsonWebSignatureAlg::Rs256]);
1508 metadata.clone().validate(&issuer).unwrap();
1509
1510 metadata.token_endpoint_auth_methods_supported =
1512 Some(vec![OAuthClientAuthenticationMethod::PrivateKeyJwt]);
1513 metadata.token_endpoint_auth_signing_alg_values_supported =
1514 Some(vec![JsonWebSignatureAlg::Rs256]);
1515 metadata.clone().validate(&issuer).unwrap();
1516
1517 metadata.token_endpoint_auth_methods_supported =
1519 Some(vec![OAuthClientAuthenticationMethod::ClientSecretJwt]);
1520 metadata.token_endpoint_auth_signing_alg_values_supported = None;
1521 metadata.clone().validate(&issuer).unwrap();
1522
1523 metadata.token_endpoint_auth_methods_supported =
1525 Some(vec![OAuthClientAuthenticationMethod::PrivateKeyJwt]);
1526 metadata.token_endpoint_auth_signing_alg_values_supported = None;
1527 metadata.clone().validate(&issuer).unwrap();
1528
1529 metadata.token_endpoint_auth_methods_supported = Some(vec![
1531 OAuthClientAuthenticationMethod::ClientSecretBasic,
1532 OAuthClientAuthenticationMethod::ClientSecretPost,
1533 ]);
1534 metadata.token_endpoint_auth_signing_alg_values_supported = None;
1535 metadata.validate(&issuer).unwrap();
1536 }
1537
1538 #[test]
1539 fn validate_revocation_endpoint() {
1540 let (mut metadata, issuer) = valid_provider_metadata();
1541
1542 metadata.revocation_endpoint = None;
1544 metadata.clone().validate(&issuer).unwrap();
1545
1546 let endpoint = Url::parse("http://localhost/revocation").unwrap();
1548 metadata.revocation_endpoint = Some(endpoint.clone());
1549 let (field, url) = assert_matches!(
1550 metadata.clone().validate(&issuer),
1551 Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1552 );
1553 assert_eq!(field, "revocation_endpoint");
1554 assert_eq!(url, endpoint);
1555
1556 let endpoint = Url::parse("https://localhost/revocation#fragment").unwrap();
1558 metadata.revocation_endpoint = Some(endpoint.clone());
1559 let (field, url) = assert_matches!(
1560 metadata.clone().validate(&issuer),
1561 Err(ProviderMetadataVerificationError::UrlWithFragment(field, url)) => (field, url)
1562 );
1563 assert_eq!(field, "revocation_endpoint");
1564 assert_eq!(url, endpoint);
1565
1566 metadata.revocation_endpoint =
1568 Some(Url::parse("https://localhost/revocation?query").unwrap());
1569 metadata.validate(&issuer).unwrap();
1570 }
1571
1572 #[test]
1573 fn validate_revocation_endpoint_signing_alg_values_supported() {
1574 let (mut metadata, issuer) = valid_provider_metadata();
1575
1576 metadata.revocation_endpoint_auth_signing_alg_values_supported = None;
1581 metadata.revocation_endpoint_auth_methods_supported = None;
1582 metadata.clone().validate(&issuer).unwrap();
1583
1584 metadata.revocation_endpoint_auth_signing_alg_values_supported =
1586 Some(vec![JsonWebSignatureAlg::None]);
1587 let endpoint = assert_matches!(
1588 metadata.validate(&issuer),
1589 Err(ProviderMetadataVerificationError::SigningAlgValuesWithNone(endpoint)) => endpoint
1590 );
1591 assert_eq!(endpoint, "revocation_endpoint");
1592 }
1593
1594 #[test]
1595 fn validate_introspection_endpoint() {
1596 let (mut metadata, issuer) = valid_provider_metadata();
1597
1598 metadata.introspection_endpoint = None;
1600 metadata.clone().validate(&issuer).unwrap();
1601
1602 let endpoint = Url::parse("http://localhost/introspection").unwrap();
1604 metadata.introspection_endpoint = Some(endpoint.clone());
1605 let (field, url) = assert_matches!(
1606 metadata.clone().validate(&issuer),
1607 Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1608 );
1609 assert_eq!(field, "introspection_endpoint");
1610 assert_eq!(url, endpoint);
1611
1612 metadata.introspection_endpoint =
1614 Some(Url::parse("https://localhost/introspection?query#fragment").unwrap());
1615 metadata.validate(&issuer).unwrap();
1616 }
1617
1618 #[test]
1619 fn validate_introspection_endpoint_signing_alg_values_supported() {
1620 let (mut metadata, issuer) = valid_provider_metadata();
1621
1622 metadata.introspection_endpoint_auth_signing_alg_values_supported = None;
1627 metadata.introspection_endpoint_auth_methods_supported = None;
1628 metadata.clone().validate(&issuer).unwrap();
1629
1630 metadata.introspection_endpoint_auth_signing_alg_values_supported =
1632 Some(vec![JsonWebSignatureAlg::None]);
1633 let endpoint = assert_matches!(
1634 metadata.validate(&issuer),
1635 Err(ProviderMetadataVerificationError::SigningAlgValuesWithNone(endpoint)) => endpoint
1636 );
1637 assert_eq!(endpoint, "introspection_endpoint");
1638 }
1639
1640 #[test]
1641 fn validate_userinfo_endpoint() {
1642 let (mut metadata, issuer) = valid_provider_metadata();
1643
1644 metadata.userinfo_endpoint = None;
1646 metadata.clone().validate(&issuer).unwrap();
1647
1648 let endpoint = Url::parse("http://localhost/userinfo").unwrap();
1650 metadata.userinfo_endpoint = Some(endpoint.clone());
1651 let (field, url) = assert_matches!(
1652 metadata.clone().validate(&issuer),
1653 Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1654 );
1655 assert_eq!(field, "userinfo_endpoint");
1656 assert_eq!(url, endpoint);
1657
1658 metadata.userinfo_endpoint =
1660 Some(Url::parse("https://localhost/userinfo?query#fragment").unwrap());
1661 metadata.validate(&issuer).unwrap();
1662 }
1663
1664 #[test]
1665 fn validate_subject_types_supported() {
1666 let (mut metadata, issuer) = valid_provider_metadata();
1667
1668 metadata.subject_types_supported = None;
1670 assert_matches!(
1671 metadata.clone().validate(&issuer),
1672 Err(ProviderMetadataVerificationError::MissingSubjectTypesSupported)
1673 );
1674
1675 metadata.subject_types_supported = Some(vec![SubjectType::Public, SubjectType::Pairwise]);
1677 metadata.validate(&issuer).unwrap();
1678 }
1679
1680 #[test]
1681 fn validate_id_token_signing_alg_values_supported() {
1682 let (mut metadata, issuer) = valid_provider_metadata();
1683
1684 metadata.id_token_signing_alg_values_supported = None;
1686 assert_matches!(
1687 metadata.clone().validate(&issuer),
1688 Err(ProviderMetadataVerificationError::MissingIdTokenSigningAlgValuesSupported)
1689 );
1690
1691 metadata.id_token_signing_alg_values_supported =
1693 Some(vec![JsonWebSignatureAlg::Rs256, JsonWebSignatureAlg::EdDsa]);
1694 metadata.validate(&issuer).unwrap();
1695 }
1696
1697 #[test]
1698 fn validate_pushed_authorization_request_endpoint() {
1699 let (mut metadata, issuer) = valid_provider_metadata();
1700
1701 metadata.pushed_authorization_request_endpoint = None;
1703 metadata.clone().validate(&issuer).unwrap();
1704
1705 let endpoint = Url::parse("http://localhost/par").unwrap();
1707 metadata.pushed_authorization_request_endpoint = Some(endpoint.clone());
1708 let (field, url) = assert_matches!(
1709 metadata.clone().validate(&issuer),
1710 Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1711 );
1712 assert_eq!(field, "pushed_authorization_request_endpoint");
1713 assert_eq!(url, endpoint);
1714
1715 metadata.pushed_authorization_request_endpoint =
1717 Some(Url::parse("https://localhost/par?query#fragment").unwrap());
1718 metadata.validate(&issuer).unwrap();
1719 }
1720
1721 #[test]
1722 fn serialize_application_type() {
1723 assert_eq!(
1724 serde_json::to_string(&ApplicationType::Web).unwrap(),
1725 "\"web\""
1726 );
1727 assert_eq!(
1728 serde_json::to_string(&ApplicationType::Native).unwrap(),
1729 "\"native\""
1730 );
1731 }
1732
1733 #[test]
1734 fn deserialize_application_type() {
1735 assert_eq!(
1736 serde_json::from_str::<ApplicationType>("\"web\"").unwrap(),
1737 ApplicationType::Web
1738 );
1739 assert_eq!(
1740 serde_json::from_str::<ApplicationType>("\"native\"").unwrap(),
1741 ApplicationType::Native
1742 );
1743 }
1744
1745 #[test]
1746 fn serialize_subject_type() {
1747 assert_eq!(
1748 serde_json::to_string(&SubjectType::Public).unwrap(),
1749 "\"public\""
1750 );
1751 assert_eq!(
1752 serde_json::to_string(&SubjectType::Pairwise).unwrap(),
1753 "\"pairwise\""
1754 );
1755 }
1756
1757 #[test]
1758 fn deserialize_subject_type() {
1759 assert_eq!(
1760 serde_json::from_str::<SubjectType>("\"public\"").unwrap(),
1761 SubjectType::Public
1762 );
1763 assert_eq!(
1764 serde_json::from_str::<SubjectType>("\"pairwise\"").unwrap(),
1765 SubjectType::Pairwise
1766 );
1767 }
1768
1769 #[test]
1770 fn serialize_claim_type() {
1771 assert_eq!(
1772 serde_json::to_string(&ClaimType::Normal).unwrap(),
1773 "\"normal\""
1774 );
1775 assert_eq!(
1776 serde_json::to_string(&ClaimType::Aggregated).unwrap(),
1777 "\"aggregated\""
1778 );
1779 assert_eq!(
1780 serde_json::to_string(&ClaimType::Distributed).unwrap(),
1781 "\"distributed\""
1782 );
1783 }
1784
1785 #[test]
1786 fn deserialize_claim_type() {
1787 assert_eq!(
1788 serde_json::from_str::<ClaimType>("\"normal\"").unwrap(),
1789 ClaimType::Normal
1790 );
1791 assert_eq!(
1792 serde_json::from_str::<ClaimType>("\"aggregated\"").unwrap(),
1793 ClaimType::Aggregated
1794 );
1795 assert_eq!(
1796 serde_json::from_str::<ClaimType>("\"distributed\"").unwrap(),
1797 ClaimType::Distributed
1798 );
1799 }
1800
1801 #[test]
1802 fn deserialize_auth_method_or_token_type_type() {
1803 assert_eq!(
1804 serde_json::from_str::<AuthenticationMethodOrAccessTokenType>("\"none\"").unwrap(),
1805 AuthenticationMethodOrAccessTokenType::AuthenticationMethod(
1806 OAuthClientAuthenticationMethod::None
1807 )
1808 );
1809 assert_eq!(
1810 serde_json::from_str::<AuthenticationMethodOrAccessTokenType>("\"Bearer\"").unwrap(),
1811 AuthenticationMethodOrAccessTokenType::AccessTokenType(OAuthAccessTokenType::Bearer)
1812 );
1813 assert_eq!(
1814 serde_json::from_str::<AuthenticationMethodOrAccessTokenType>("\"unknown_value\"")
1815 .unwrap(),
1816 AuthenticationMethodOrAccessTokenType::Unknown("unknown_value".to_owned())
1817 );
1818 }
1819}