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}