oauth2_types/
oidc.rs

1// Copyright 2024, 2025 New Vector Ltd.
2// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5// Please see LICENSE files in the repository root for full details.
6
7//! Types to interact with the [OpenID Connect] specification.
8//!
9//! [OpenID Connect]: https://openid.net/connect/
10
11use 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/// An enum for types that accept either an [`OAuthClientAuthenticationMethod`]
32/// or an [`OAuthAccessTokenType`].
33#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug)]
34pub enum AuthenticationMethodOrAccessTokenType {
35    /// An authentication method.
36    AuthenticationMethod(OAuthClientAuthenticationMethod),
37
38    /// An access token type.
39    AccessTokenType(OAuthAccessTokenType),
40
41    /// An unknown value.
42    ///
43    /// Note that this variant should only be used as the result parsing a
44    /// string of unknown type. To build a custom variant, first parse a
45    /// string with the wanted type then use `.into()`.
46    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    /// Get the authentication method of this
79    /// `AuthenticationMethodOrAccessTokenType`.
80    #[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    /// Get the access token type of this
89    /// `AuthenticationMethodOrAccessTokenType`.
90    #[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/// The kind of an application.
112#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug)]
113pub enum ApplicationType {
114    /// A web application.
115    Web,
116
117    /// A native application.
118    Native,
119
120    /// An unknown value.
121    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/// Subject Identifier types.
147///
148/// A Subject Identifier is a locally unique and never reassigned identifier
149/// within the Issuer for the End-User, which is intended to be consumed by the
150/// Client.
151#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug)]
152pub enum SubjectType {
153    /// This provides the same `sub` (subject) value to all Clients.
154    Public,
155
156    /// This provides a different `sub` value to each Client, so as not to
157    /// enable Clients to correlate the End-User's activities without
158    /// permission.
159    Pairwise,
160
161    /// An unknown value.
162    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/// Claim types.
188#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug)]
189pub enum ClaimType {
190    /// Claims that are directly asserted by the OpenID Provider.
191    Normal,
192
193    /// Claims that are asserted by a Claims Provider other than the OpenID
194    /// Provider but are returned by OpenID Provider.
195    Aggregated,
196
197    /// Claims that are asserted by a Claims Provider other than the OpenID
198    /// Provider but are returned as references by the OpenID Provider.
199    Distributed,
200
201    /// An unknown value.
202    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/// An account management action that a user can take.
230///
231/// Source: <https://github.com/matrix-org/matrix-spec-proposals/pull/2965>
232#[derive(
233    SerializeDisplay, DeserializeFromStr, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
234)]
235#[non_exhaustive]
236pub enum AccountManagementAction {
237    /// `org.matrix.profile`
238    ///
239    /// The user wishes to view their profile (name, avatar, contact details).
240    Profile,
241
242    /// `org.matrix.sessions_list`
243    ///
244    /// The user wishes to view a list of their sessions.
245    SessionsList,
246
247    /// `org.matrix.session_view`
248    ///
249    /// The user wishes to view the details of a specific session.
250    SessionView,
251
252    /// `org.matrix.session_end`
253    ///
254    /// The user wishes to end/log out of a specific session.
255    SessionEnd,
256
257    /// `org.matrix.account_deactivate`
258    ///
259    /// The user wishes to deactivate their account.
260    AccountDeactivate,
261
262    /// `org.matrix.cross_signing_reset`
263    ///
264    /// The user wishes to reset their cross-signing keys.
265    CrossSigningReset,
266
267    /// An unknown value.
268    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
301/// The default value of `response_modes_supported` if it is not set.
302pub static DEFAULT_RESPONSE_MODES_SUPPORTED: &[ResponseMode] =
303    &[ResponseMode::Query, ResponseMode::Fragment];
304
305/// The default value of `grant_types_supported` if it is not set.
306pub static DEFAULT_GRANT_TYPES_SUPPORTED: &[GrantType] =
307    &[GrantType::AuthorizationCode, GrantType::Implicit];
308
309/// The default value of `token_endpoint_auth_methods_supported` if it is not
310/// set.
311pub static DEFAULT_AUTH_METHODS_SUPPORTED: &[OAuthClientAuthenticationMethod] =
312    &[OAuthClientAuthenticationMethod::ClientSecretBasic];
313
314/// The default value of `claim_types_supported` if it is not set.
315pub static DEFAULT_CLAIM_TYPES_SUPPORTED: &[ClaimType] = &[ClaimType::Normal];
316
317/// Authorization server metadata, as described by the [IANA registry].
318///
319/// All the fields with a default value are accessible via methods.
320///
321/// [IANA registry]: https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml#authorization-server-metadata
322#[skip_serializing_none]
323#[derive(Debug, Serialize, Deserialize, Clone, Default)]
324pub struct ProviderMetadata {
325    /// Authorization server's issuer identifier URL.
326    ///
327    /// This field is required. The URL must use a `https` scheme, and must not
328    /// contain a query or fragment. It must match the one used to build the
329    /// well-known URI to query this metadata.
330    pub issuer: Option<String>,
331
332    /// URL of the authorization server's [authorization endpoint].
333    ///
334    /// This field is required. The URL must use a `https` scheme, and must not
335    /// contain a fragment.
336    ///
337    /// [authorization endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.1
338    pub authorization_endpoint: Option<Url>,
339
340    /// URL of the authorization server's [token endpoint].
341    ///
342    /// This field is required. The URL must use a `https` scheme, and must not
343    /// contain a fragment.
344    ///
345    /// [token endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.2
346    pub token_endpoint: Option<Url>,
347
348    /// URL of the authorization server's [JWK] Set document.
349    ///
350    /// This field is required. The URL must use a `https` scheme.
351    ///
352    /// [JWK]: https://www.rfc-editor.org/rfc/rfc7517.html
353    pub jwks_uri: Option<Url>,
354
355    /// URL of the authorization server's [OAuth 2.0 Dynamic Client
356    /// Registration] endpoint.
357    ///
358    /// If this field is present, the URL must use a `https` scheme.
359    ///
360    /// [OAuth 2.0 Dynamic Client Registration]: https://www.rfc-editor.org/rfc/rfc7591
361    pub registration_endpoint: Option<Url>,
362
363    /// JSON array containing a list of the OAuth 2.0 `scope` values that this
364    /// authorization server supports.
365    ///
366    /// If this field is present, it must contain at least the `openid` scope
367    /// value.
368    pub scopes_supported: Option<Vec<String>>,
369
370    /// JSON array containing a list of the [OAuth 2.0 `response_type` values]
371    /// that this authorization server supports.
372    ///
373    /// This field is required.
374    ///
375    /// [OAuth 2.0 `response_type` values]: https://www.rfc-editor.org/rfc/rfc7591#page-9
376    pub response_types_supported: Option<Vec<ResponseType>>,
377
378    /// JSON array containing a list of the [OAuth 2.0 `response_mode` values]
379    /// that this authorization server supports.
380    ///
381    /// Defaults to [`DEFAULT_RESPONSE_MODES_SUPPORTED`].
382    ///
383    /// [OAuth 2.0 `response_mode` values]: https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html
384    pub response_modes_supported: Option<Vec<ResponseMode>>,
385
386    /// JSON array containing a list of the [OAuth 2.0 `grant_type` values] that
387    /// this authorization server supports.
388    ///
389    /// Defaults to [`DEFAULT_GRANT_TYPES_SUPPORTED`].
390    ///
391    /// [OAuth 2.0 `grant_type` values]: https://www.rfc-editor.org/rfc/rfc7591#page-9
392    pub grant_types_supported: Option<Vec<GrantType>>,
393
394    /// JSON array containing a list of client authentication methods supported
395    /// by this token endpoint.
396    ///
397    /// Defaults to [`DEFAULT_AUTH_METHODS_SUPPORTED`].
398    pub token_endpoint_auth_methods_supported: Option<Vec<OAuthClientAuthenticationMethod>>,
399
400    /// JSON array containing a list of the JWS signing algorithms supported
401    /// by the token endpoint for the signature on the JWT used to
402    /// authenticate the client at the token endpoint.
403    ///
404    /// If this field is present, it must not contain
405    /// [`JsonWebSignatureAlg::None`]. This field is required if
406    /// `token_endpoint_auth_methods_supported` contains
407    /// [`OAuthClientAuthenticationMethod::PrivateKeyJwt`] or
408    /// [`OAuthClientAuthenticationMethod::ClientSecretJwt`].
409    pub token_endpoint_auth_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
410
411    /// URL of a page containing human-readable information that developers
412    /// might want or need to know when using the authorization server.
413    pub service_documentation: Option<Url>,
414
415    /// Languages and scripts supported for the user interface, represented as a
416    /// JSON array of language tag values from BCP 47.
417    ///
418    /// If omitted, the set of supported languages and scripts is unspecified.
419    pub ui_locales_supported: Option<Vec<LanguageTag>>,
420
421    /// URL that the authorization server provides to the person registering the
422    /// client to read about the authorization server's requirements on how the
423    /// client can use the data provided by the authorization server.
424    pub op_policy_uri: Option<Url>,
425
426    /// URL that the authorization server provides to the person registering the
427    /// client to read about the authorization server's terms of service.
428    pub op_tos_uri: Option<Url>,
429
430    /// URL of the authorization server's [OAuth 2.0 revocation endpoint].
431    ///
432    /// If this field is present, the URL must use a `https` scheme, and must
433    /// not contain a fragment.
434    ///
435    /// [OAuth 2.0 revocation endpoint]: https://www.rfc-editor.org/rfc/rfc7009
436    pub revocation_endpoint: Option<Url>,
437
438    /// JSON array containing a list of client authentication methods supported
439    /// by this revocation endpoint.
440    ///
441    /// Defaults to [`DEFAULT_AUTH_METHODS_SUPPORTED`].
442    pub revocation_endpoint_auth_methods_supported: Option<Vec<OAuthClientAuthenticationMethod>>,
443
444    /// JSON array containing a list of the JWS signing algorithms supported by
445    /// the revocation endpoint for the signature on the JWT used to
446    /// authenticate the client at the revocation endpoint.
447    ///
448    /// If this field is present, it must not contain
449    /// [`JsonWebSignatureAlg::None`]. This field is required if
450    /// `revocation_endpoint_auth_methods_supported` contains
451    /// [`OAuthClientAuthenticationMethod::PrivateKeyJwt`] or
452    /// [`OAuthClientAuthenticationMethod::ClientSecretJwt`].
453    pub revocation_endpoint_auth_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
454
455    /// URL of the authorization server's [OAuth 2.0 introspection endpoint].
456    ///
457    /// If this field is present, the URL must use a `https` scheme.
458    ///
459    /// [OAuth 2.0 introspection endpoint]: https://www.rfc-editor.org/rfc/rfc7662
460    pub introspection_endpoint: Option<Url>,
461
462    /// JSON array containing a list of client authentication methods or token
463    /// types supported by this introspection endpoint.
464    pub introspection_endpoint_auth_methods_supported:
465        Option<Vec<AuthenticationMethodOrAccessTokenType>>,
466
467    /// JSON array containing a list of the JWS signing algorithms supported by
468    /// the introspection endpoint for the signature on the JWT used to
469    /// authenticate the client at the introspection endpoint.
470    ///
471    /// If this field is present, it must not contain
472    /// [`JsonWebSignatureAlg::None`]. This field is required if
473    /// `intospection_endpoint_auth_methods_supported` contains
474    /// [`OAuthClientAuthenticationMethod::PrivateKeyJwt`] or
475    /// [`OAuthClientAuthenticationMethod::ClientSecretJwt`].
476    pub introspection_endpoint_auth_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
477
478    /// [PKCE code challenge methods] supported by this authorization server.
479    /// If omitted, the authorization server does not support PKCE.
480    ///
481    /// [PKCE code challenge]: https://www.rfc-editor.org/rfc/rfc7636
482    pub code_challenge_methods_supported: Option<Vec<PkceCodeChallengeMethod>>,
483
484    /// URL of the OP's [UserInfo Endpoint].
485    ///
486    /// [UserInfo Endpoint]: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
487    pub userinfo_endpoint: Option<Url>,
488
489    /// JSON array containing a list of the Authentication Context Class
490    /// References that this OP supports.
491    pub acr_values_supported: Option<Vec<String>>,
492
493    /// JSON array containing a list of the Subject Identifier types that this
494    /// OP supports.
495    ///
496    /// This field is required.
497    pub subject_types_supported: Option<Vec<SubjectType>>,
498
499    /// JSON array containing a list of the JWS signing algorithms (`alg`
500    /// values) supported by the OP for the ID Token.
501    ///
502    /// This field is required.
503    pub id_token_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
504
505    /// JSON array containing a list of the JWE encryption algorithms (`alg`
506    /// values) supported by the OP for the ID Token.
507    pub id_token_encryption_alg_values_supported: Option<Vec<JsonWebEncryptionAlg>>,
508
509    /// JSON array containing a list of the JWE encryption algorithms (`enc`
510    /// values) supported by the OP for the ID Token.
511    pub id_token_encryption_enc_values_supported: Option<Vec<JsonWebEncryptionEnc>>,
512
513    /// JSON array containing a list of the JWS signing algorithms (`alg`
514    /// values) supported by the UserInfo Endpoint.
515    pub userinfo_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
516
517    /// JSON array containing a list of the JWE encryption algorithms (`alg`
518    /// values) supported by the UserInfo Endpoint.
519    pub userinfo_encryption_alg_values_supported: Option<Vec<JsonWebEncryptionAlg>>,
520
521    /// JSON array containing a list of the JWE encryption algorithms (`enc`
522    /// values) supported by the UserInfo Endpoint.
523    pub userinfo_encryption_enc_values_supported: Option<Vec<JsonWebEncryptionEnc>>,
524
525    /// JSON array containing a list of the JWS signing algorithms (`alg`
526    /// values) supported by the OP for Request Objects.
527    pub request_object_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
528
529    /// JSON array containing a list of the JWE encryption algorithms (`alg`
530    /// values) supported by the OP for Request Objects.
531    pub request_object_encryption_alg_values_supported: Option<Vec<JsonWebEncryptionAlg>>,
532
533    /// JSON array containing a list of the JWE encryption algorithms (`enc`
534    /// values) supported by the OP for Request Objects.
535    pub request_object_encryption_enc_values_supported: Option<Vec<JsonWebEncryptionEnc>>,
536
537    /// JSON array containing a list of the "display" parameter values that the
538    /// OpenID Provider supports.
539    pub display_values_supported: Option<Vec<Display>>,
540
541    /// JSON array containing a list of the Claim Types that the OpenID Provider
542    /// supports.
543    ///
544    /// Defaults to [`DEFAULT_CLAIM_TYPES_SUPPORTED`].
545    pub claim_types_supported: Option<Vec<ClaimType>>,
546
547    /// JSON array containing a list of the Claim Names of the Claims that the
548    /// OpenID Provider MAY be able to supply values for.
549    pub claims_supported: Option<Vec<String>>,
550
551    /// Languages and scripts supported for values in Claims being returned,
552    /// represented as a JSON array of BCP 47 language tag values.
553    pub claims_locales_supported: Option<Vec<LanguageTag>>,
554
555    /// Boolean value specifying whether the OP supports use of the `claims`
556    /// parameter.
557    ///
558    /// Defaults to `false`.
559    pub claims_parameter_supported: Option<bool>,
560
561    /// Boolean value specifying whether the OP supports use of the `request`
562    /// parameter.
563    ///
564    /// Defaults to `false`.
565    pub request_parameter_supported: Option<bool>,
566
567    /// Boolean value specifying whether the OP supports use of the
568    /// `request_uri` parameter.
569    ///
570    /// Defaults to `true`.
571    pub request_uri_parameter_supported: Option<bool>,
572
573    /// Boolean value specifying whether the OP requires any `request_uri`
574    /// values used to be pre-registered.
575    ///
576    /// Defaults to `false`.
577    pub require_request_uri_registration: Option<bool>,
578
579    /// Indicates where authorization request needs to be protected as [Request
580    /// Object] and provided through either request or `request_uri` parameter.
581    ///
582    /// Defaults to `false`.
583    ///
584    /// [Request Object]: https://www.rfc-editor.org/rfc/rfc9101.html
585    pub require_signed_request_object: Option<bool>,
586
587    /// URL of the authorization server's [pushed authorization request
588    /// endpoint].
589    ///
590    /// [pushed authorization request endpoint]: https://www.rfc-editor.org/rfc/rfc9126.html
591    pub pushed_authorization_request_endpoint: Option<Url>,
592
593    /// Indicates whether the authorization server accepts authorization
594    /// requests only via PAR.
595    ///
596    /// Defaults to `false`.
597    pub require_pushed_authorization_requests: Option<bool>,
598
599    /// Array containing the list of prompt values that this OP supports.
600    ///
601    /// This field can be used to detect if the OP supports the [prompt
602    /// `create`] value.
603    ///
604    /// [prompt `create`]: https://openid.net/specs/openid-connect-prompt-create-1_0.html
605    pub prompt_values_supported: Option<Vec<Prompt>>,
606
607    /// URL of the authorization server's [device authorization endpoint].
608    ///
609    /// [device authorization endpoint]: https://www.rfc-editor.org/rfc/rfc8628
610    pub device_authorization_endpoint: Option<Url>,
611
612    /// URL of the authorization server's [RP-Initiated Logout endpoint].
613    ///
614    /// [RP-Initiated Logout endpoint]: https://openid.net/specs/openid-connect-rpinitiated-1_0.html
615    pub end_session_endpoint: Option<Url>,
616
617    /// URL where the user is able to access the account management capabilities
618    /// of this OP.
619    ///
620    /// This is a Matrix extension introduced in [MSC2965](https://github.com/matrix-org/matrix-spec-proposals/pull/2965).
621    pub account_management_uri: Option<Url>,
622
623    /// Array of actions that the account management URL supports.
624    ///
625    /// This is a Matrix extension introduced in [MSC2965](https://github.com/matrix-org/matrix-spec-proposals/pull/2965).
626    pub account_management_actions_supported: Option<Vec<AccountManagementAction>>,
627}
628
629impl ProviderMetadata {
630    /// Validate this `ProviderMetadata` according to the [OpenID Connect
631    /// Discovery Spec 1.0].
632    ///
633    /// # Parameters
634    ///
635    /// - `issuer`: The issuer that was discovered to get this
636    ///   `ProviderMetadata`.
637    ///
638    /// # Errors
639    ///
640    /// Will return `Err` if validation fails.
641    ///
642    /// [OpenID Connect Discovery Spec 1.0]: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
643    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    /// Verify this `ProviderMetadata`.
741    ///
742    /// Contrary to [`ProviderMetadata::validate()`], it only checks that the
743    /// required fields are present.
744    ///
745    /// This can be used during development to test against a local OpenID
746    /// Provider, for example.
747    ///
748    /// # Parameters
749    ///
750    /// - `issuer`: The issuer that was discovered to get this
751    ///   `ProviderMetadata`.
752    ///
753    /// # Errors
754    ///
755    /// Will return `Err` if a required field is missing.
756    ///
757    /// # Warning
758    ///
759    /// It is not recommended to use this method in production as it doesn't
760    /// ensure that the issuer implements the proper security practices.
761    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    /// JSON array containing a list of the OAuth 2.0 `response_mode` values
796    /// that this authorization server supports.
797    ///
798    /// Defaults to [`DEFAULT_RESPONSE_MODES_SUPPORTED`].
799    #[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    /// JSON array containing a list of the OAuth 2.0 grant type values that
807    /// this authorization server supports.
808    ///
809    /// Defaults to [`DEFAULT_GRANT_TYPES_SUPPORTED`].
810    #[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    /// JSON array containing a list of client authentication methods supported
818    /// by the token endpoint.
819    ///
820    /// Defaults to [`DEFAULT_AUTH_METHODS_SUPPORTED`].
821    #[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    /// JSON array containing a list of client authentication methods supported
829    /// by the revocation endpoint.
830    ///
831    /// Defaults to [`DEFAULT_AUTH_METHODS_SUPPORTED`].
832    #[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    /// JSON array containing a list of the Claim Types that the OpenID Provider
840    /// supports.
841    ///
842    /// Defaults to [`DEFAULT_CLAIM_TYPES_SUPPORTED`].
843    #[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    /// Boolean value specifying whether the OP supports use of the `claims`
851    /// parameter.
852    ///
853    /// Defaults to `false`.
854    #[must_use]
855    pub fn claims_parameter_supported(&self) -> bool {
856        self.claims_parameter_supported.unwrap_or(false)
857    }
858
859    /// Boolean value specifying whether the OP supports use of the `request`
860    /// parameter.
861    ///
862    /// Defaults to `false`.
863    #[must_use]
864    pub fn request_parameter_supported(&self) -> bool {
865        self.request_parameter_supported.unwrap_or(false)
866    }
867
868    /// Boolean value specifying whether the OP supports use of the
869    /// `request_uri` parameter.
870    ///
871    /// Defaults to `true`.
872    #[must_use]
873    pub fn request_uri_parameter_supported(&self) -> bool {
874        self.request_uri_parameter_supported.unwrap_or(true)
875    }
876
877    /// Boolean value specifying whether the OP requires any `request_uri`
878    /// values used to be pre-registered.
879    ///
880    /// Defaults to `false`.
881    #[must_use]
882    pub fn require_request_uri_registration(&self) -> bool {
883        self.require_request_uri_registration.unwrap_or(false)
884    }
885
886    /// Indicates where authorization request needs to be protected as Request
887    /// Object and provided through either `request` or `request_uri` parameter.
888    ///
889    /// Defaults to `false`.
890    #[must_use]
891    pub fn require_signed_request_object(&self) -> bool {
892        self.require_signed_request_object.unwrap_or(false)
893    }
894
895    /// Indicates whether the authorization server accepts authorization
896    /// requests only via PAR.
897    ///
898    /// Defaults to `false`.
899    #[must_use]
900    pub fn require_pushed_authorization_requests(&self) -> bool {
901        self.require_pushed_authorization_requests.unwrap_or(false)
902    }
903}
904
905/// The verified authorization server metadata.
906///
907/// All the fields required by the [OpenID Connect Discovery Spec 1.0] or with
908/// a default value are accessible via methods.
909///
910/// To access other fields, use this type's `Deref` implementation.
911///
912/// [OpenID Connect Discovery Spec 1.0]: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
913#[derive(Debug, Clone)]
914pub struct VerifiedProviderMetadata {
915    inner: ProviderMetadata,
916}
917
918impl VerifiedProviderMetadata {
919    /// Authorization server's issuer identifier URL.
920    #[must_use]
921    pub fn issuer(&self) -> &str {
922        match &self.issuer {
923            Some(u) => u,
924            None => unreachable!(),
925        }
926    }
927
928    /// URL of the authorization server's authorization endpoint.
929    #[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    /// URL of the authorization server's userinfo endpoint.
938    #[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    /// URL of the authorization server's token endpoint.
947    #[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    /// URL of the authorization server's JWK Set document.
956    #[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    /// JSON array containing a list of the OAuth 2.0 `response_type` values
965    /// that this authorization server supports.
966    #[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    /// JSON array containing a list of the Subject Identifier types that this
975    /// OP supports.
976    #[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    /// JSON array containing a list of the JWS `alg` values supported by the OP
985    /// for the ID Token.
986    #[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/// All errors that can happen when verifying [`ProviderMetadata`]
1004#[derive(Debug, Error)]
1005pub enum ProviderMetadataVerificationError {
1006    /// The issuer is missing.
1007    #[error("issuer is missing")]
1008    MissingIssuer,
1009
1010    /// The issuer is not a valid URL.
1011    #[error("issuer is not a valid URL")]
1012    IssuerNotUrl,
1013
1014    /// The authorization endpoint is missing.
1015    #[error("authorization endpoint is missing")]
1016    MissingAuthorizationEndpoint,
1017
1018    /// The token endpoint is missing.
1019    #[error("token endpoint is missing")]
1020    MissingTokenEndpoint,
1021
1022    /// The JWK Set URI is missing.
1023    #[error("JWK Set URI is missing")]
1024    MissingJwksUri,
1025
1026    /// The supported response types are missing.
1027    #[error("supported response types are missing")]
1028    MissingResponseTypesSupported,
1029
1030    /// The supported subject types are missing.
1031    #[error("supported subject types are missing")]
1032    MissingSubjectTypesSupported,
1033
1034    /// The supported ID token signing algorithm values are missing.
1035    #[error("supported ID token signing algorithm values are missing")]
1036    MissingIdTokenSigningAlgValuesSupported,
1037
1038    /// The URL of the given field doesn't use a `https` scheme.
1039    #[error("{0}'s URL doesn't use a https scheme: {1}")]
1040    UrlNonHttpsScheme(&'static str, Url),
1041
1042    /// The URL of the given field contains a query, but it's not allowed.
1043    #[error("{0}'s URL contains a query: {1}")]
1044    UrlWithQuery(&'static str, Url),
1045
1046    /// The URL of the given field contains a fragment, but it's not allowed.
1047    #[error("{0}'s URL contains a fragment: {1}")]
1048    UrlWithFragment(&'static str, Url),
1049
1050    /// The issuer URL doesn't match the one that was discovered.
1051    #[error("issuer URLs don't match: expected {expected:?}, got {actual:?}")]
1052    IssuerUrlsDontMatch {
1053        /// The expected issuer URL.
1054        expected: String,
1055        /// The issuer URL that was discovered.
1056        actual: String,
1057    },
1058
1059    /// `openid` is missing from the supported scopes.
1060    #[error("missing openid scope")]
1061    ScopesMissingOpenid,
1062
1063    /// `code` is missing from the supported response types.
1064    #[error("missing `code` response type")]
1065    ResponseTypesMissingCode,
1066
1067    /// `id_token` is missing from the supported response types.
1068    #[error("missing `id_token` response type")]
1069    ResponseTypesMissingIdToken,
1070
1071    /// `id_token token` is missing from the supported response types.
1072    #[error("missing `id_token token` response type")]
1073    ResponseTypesMissingIdTokenToken,
1074
1075    /// `authorization_code` is missing from the supported grant types.
1076    #[error("missing `authorization_code` grant type")]
1077    GrantTypesMissingAuthorizationCode,
1078
1079    /// `implicit` is missing from the supported grant types.
1080    #[error("missing `implicit` grant type")]
1081    GrantTypesMissingImplicit,
1082
1083    /// `none` is in the given endpoint's signing algorithm values, but is not
1084    /// allowed.
1085    #[error("{0} signing algorithm values contain `none`")]
1086    SigningAlgValuesWithNone(&'static str),
1087}
1088
1089/// Possible extra restrictions on a URL.
1090#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1091enum ExtraUrlRestrictions {
1092    /// No extra restrictions.
1093    None,
1094
1095    /// The URL must not contain a fragment.
1096    NoFragment,
1097
1098    /// The URL must not contain a query or a fragment.
1099    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
1112/// Validate the URL of the field with the given extra restrictions.
1113///
1114/// The basic restriction is that the URL must use the `https` scheme.
1115fn 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
1144/// Validate the algorithm values of the endpoint according to the
1145/// authentication methods.
1146///
1147/// The restrictions are:
1148/// - The algorithm values must not contain `none`,
1149/// - If the `client_secret_jwt` or `private_key_jwt` authentication methods are
1150///   supported, the values must be present.
1151fn 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/// The body of a request to the [RP-Initiated Logout Endpoint].
1166///
1167/// [RP-Initiated Logout Endpoint]: https://openid.net/specs/openid-connect-rpinitiated-1_0.html
1168#[skip_serializing_none]
1169#[serde_as]
1170#[derive(Default, Serialize, Deserialize, Clone)]
1171pub struct RpInitiatedLogoutRequest {
1172    /// ID Token previously issued by the OP to the RP.
1173    ///
1174    /// Recommended, used as a hint about the End-User's current authenticated
1175    /// session with the Client.
1176    pub id_token_hint: Option<String>,
1177
1178    /// Hint to the Authorization Server about the End-User that is logging out.
1179    ///
1180    /// The value and meaning of this parameter is left up to the OP's
1181    /// discretion. For instance, the value might contain an email address,
1182    /// phone number, username, or session identifier pertaining to the RP's
1183    /// session with the OP for the End-User.
1184    pub logout_hint: Option<String>,
1185
1186    /// OAuth 2.0 Client Identifier valid at the Authorization Server.
1187    ///
1188    /// The most common use case for this parameter is to specify the Client
1189    /// Identifier when `post_logout_redirect_uri` is used but `id_token_hint`
1190    /// is not. Another use is for symmetrically encrypted ID Tokens used as
1191    /// `id_token_hint` values that require the Client Identifier to be
1192    /// specified by other means, so that the ID Tokens can be decrypted by
1193    /// the OP.
1194    pub client_id: Option<String>,
1195
1196    /// URI to which the RP is requesting that the End-User's User Agent be
1197    /// redirected after a logout has been performed.
1198    ///
1199    /// The value MUST have been previously registered with the OP, using the
1200    /// `post_logout_redirect_uris` registration parameter.
1201    pub post_logout_redirect_uri: Option<Url>,
1202
1203    /// Opaque value used by the RP to maintain state between the logout request
1204    /// and the callback to the endpoint specified by the
1205    /// `post_logout_redirect_uri` parameter.
1206    pub state: Option<String>,
1207
1208    /// End-User's preferred languages and scripts for the user interface,
1209    /// ordered by preference.
1210    #[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        // Err - Missing
1265        metadata.issuer = None;
1266        assert_matches!(
1267            metadata.clone().validate(&issuer),
1268            Err(ProviderMetadataVerificationError::MissingIssuer)
1269        );
1270
1271        // Err - Not an url
1272        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        // Err - Wrong issuer
1279        metadata.issuer = Some("https://example.com/".to_owned());
1280        assert_matches!(
1281            metadata.clone().validate(&issuer),
1282            Err(ProviderMetadataVerificationError::IssuerUrlsDontMatch { .. })
1283        );
1284
1285        // Err - Not https
1286        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        // Err - Query
1296        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        // Err - Fragment
1306        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        // Ok - Path
1316        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        // Err - Missing
1326        metadata.authorization_endpoint = None;
1327        assert_matches!(
1328            metadata.clone().validate(&issuer),
1329            Err(ProviderMetadataVerificationError::MissingAuthorizationEndpoint)
1330        );
1331
1332        // Err - Not https
1333        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        // Err - Fragment
1343        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        // Ok - Query
1353        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        // Err - Missing
1362        metadata.token_endpoint = None;
1363        assert_matches!(
1364            metadata.clone().validate(&issuer),
1365            Err(ProviderMetadataVerificationError::MissingTokenEndpoint)
1366        );
1367
1368        // Err - Not https
1369        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        // Err - Fragment
1379        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        // Ok - Query
1389        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        // Err - Missing
1398        metadata.jwks_uri = None;
1399        assert_matches!(
1400            metadata.clone().validate(&issuer),
1401            Err(ProviderMetadataVerificationError::MissingJwksUri)
1402        );
1403
1404        // Err - Not https
1405        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        // Ok - Query & fragment
1415        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        // Err - Not https
1424        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        // Ok - Missing
1434        metadata.registration_endpoint = None;
1435        metadata.clone().validate(&issuer).unwrap();
1436
1437        // Ok - Query & fragment
1438        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        // Err - No `openid`
1448        metadata.scopes_supported = Some(vec!["custom".to_owned()]);
1449        assert_matches!(
1450            metadata.clone().validate(&issuer),
1451            Err(ProviderMetadataVerificationError::ScopesMissingOpenid)
1452        );
1453
1454        // Ok - Missing
1455        metadata.scopes_supported = None;
1456        metadata.clone().validate(&issuer).unwrap();
1457
1458        // Ok - With `openid`
1459        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        // Err - Missing
1468        metadata.response_types_supported = None;
1469        assert_matches!(
1470            metadata.clone().validate(&issuer),
1471            Err(ProviderMetadataVerificationError::MissingResponseTypesSupported)
1472        );
1473
1474        // Ok - Present
1475        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        // Ok - Missing
1485        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        // Err - With `none`
1490        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        // Ok - Other signing alg values.
1499        metadata.token_endpoint_auth_signing_alg_values_supported =
1500            Some(vec![JsonWebSignatureAlg::Rs256, JsonWebSignatureAlg::EdDsa]);
1501        metadata.clone().validate(&issuer).unwrap();
1502
1503        // Ok - `client_secret_jwt` with signing alg values.
1504        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        // Ok - `private_key_jwt` with signing alg values.
1511        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        // Ok - `client_secret_jwt` without signing alg values.
1518        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        // Ok - `private_key_jwt` without signing alg values.
1524        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        // Ok - Other auth methods without signing alg values.
1530        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        // Ok - Missing
1543        metadata.revocation_endpoint = None;
1544        metadata.clone().validate(&issuer).unwrap();
1545
1546        // Err - Not https
1547        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        // Err - Fragment
1557        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        // Ok - Query
1567        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        // Only check that this field is validated, algorithm checks are already
1577        // tested for the token endpoint.
1578
1579        // Ok - Missing
1580        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        // Err - With `none`
1585        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        // Ok - Missing
1599        metadata.introspection_endpoint = None;
1600        metadata.clone().validate(&issuer).unwrap();
1601
1602        // Err - Not https
1603        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        // Ok - Query & Fragment
1613        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        // Only check that this field is validated, algorithm checks are already
1623        // tested for the token endpoint.
1624
1625        // Ok - Missing
1626        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        // Err - With `none`
1631        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        // Ok - Missing
1645        metadata.userinfo_endpoint = None;
1646        metadata.clone().validate(&issuer).unwrap();
1647
1648        // Err - Not https
1649        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        // Ok - Query & Fragment
1659        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        // Err - Missing
1669        metadata.subject_types_supported = None;
1670        assert_matches!(
1671            metadata.clone().validate(&issuer),
1672            Err(ProviderMetadataVerificationError::MissingSubjectTypesSupported)
1673        );
1674
1675        // Ok - Present
1676        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        // Err - Missing
1685        metadata.id_token_signing_alg_values_supported = None;
1686        assert_matches!(
1687            metadata.clone().validate(&issuer),
1688            Err(ProviderMetadataVerificationError::MissingIdTokenSigningAlgValuesSupported)
1689        );
1690
1691        // Ok - Present
1692        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        // Ok - Missing
1702        metadata.pushed_authorization_request_endpoint = None;
1703        metadata.clone().validate(&issuer).unwrap();
1704
1705        // Err - Not https
1706        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        // Ok - Query & Fragment
1716        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}