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            if !scopes.iter().any(|s| s == "openid") {
685                return Err(ProviderMetadataVerificationError::ScopesMissingOpenid);
686            }
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            metadata
696                .token_endpoint_auth_methods_supported
697                .iter()
698                .flatten(),
699        )?;
700
701        if let Some(url) = &metadata.revocation_endpoint {
702            validate_url("revocation_endpoint", url, ExtraUrlRestrictions::NoFragment)?;
703        }
704
705        validate_signing_alg_values_supported(
706            "revocation_endpoint",
707            metadata
708                .revocation_endpoint_auth_signing_alg_values_supported
709                .iter()
710                .flatten(),
711            metadata
712                .revocation_endpoint_auth_methods_supported
713                .iter()
714                .flatten(),
715        )?;
716
717        if let Some(url) = &metadata.introspection_endpoint {
718            validate_url("introspection_endpoint", url, ExtraUrlRestrictions::None)?;
719        }
720
721        // The list can also contain token types so remove them as we don't need to
722        // check them.
723        let introspection_methods = metadata
724            .introspection_endpoint_auth_methods_supported
725            .as_ref()
726            .map(|v| {
727                v.iter()
728                    .filter_map(AuthenticationMethodOrAccessTokenType::authentication_method)
729                    .collect::<Vec<_>>()
730            });
731        validate_signing_alg_values_supported(
732            "introspection_endpoint",
733            metadata
734                .introspection_endpoint_auth_signing_alg_values_supported
735                .iter()
736                .flatten(),
737            introspection_methods.into_iter().flatten(),
738        )?;
739
740        if let Some(url) = &metadata.userinfo_endpoint {
741            validate_url("userinfo_endpoint", url, ExtraUrlRestrictions::None)?;
742        }
743
744        if let Some(url) = &metadata.pushed_authorization_request_endpoint {
745            validate_url(
746                "pushed_authorization_request_endpoint",
747                url,
748                ExtraUrlRestrictions::None,
749            )?;
750        }
751
752        if let Some(url) = &metadata.end_session_endpoint {
753            validate_url("end_session_endpoint", url, ExtraUrlRestrictions::None)?;
754        }
755
756        Ok(metadata)
757    }
758
759    /// Verify this `ProviderMetadata`.
760    ///
761    /// Contrary to [`ProviderMetadata::validate()`], it only checks that the
762    /// required fields are present.
763    ///
764    /// This can be used during development to test against a local OpenID
765    /// Provider, for example.
766    ///
767    /// # Parameters
768    ///
769    /// - `issuer`: The issuer that was discovered to get this
770    ///   `ProviderMetadata`.
771    ///
772    /// # Errors
773    ///
774    /// Will return `Err` if a required field is missing.
775    ///
776    /// # Warning
777    ///
778    /// It is not recommended to use this method in production as it doesn't
779    /// ensure that the issuer implements the proper security practices.
780    pub fn insecure_verify_metadata(
781        self,
782    ) -> Result<VerifiedProviderMetadata, ProviderMetadataVerificationError> {
783        self.issuer
784            .as_ref()
785            .ok_or(ProviderMetadataVerificationError::MissingIssuer)?;
786
787        self.authorization_endpoint
788            .as_ref()
789            .ok_or(ProviderMetadataVerificationError::MissingAuthorizationEndpoint)?;
790
791        self.token_endpoint
792            .as_ref()
793            .ok_or(ProviderMetadataVerificationError::MissingTokenEndpoint)?;
794
795        self.jwks_uri
796            .as_ref()
797            .ok_or(ProviderMetadataVerificationError::MissingJwksUri)?;
798
799        self.response_types_supported
800            .as_ref()
801            .ok_or(ProviderMetadataVerificationError::MissingResponseTypesSupported)?;
802
803        self.subject_types_supported
804            .as_ref()
805            .ok_or(ProviderMetadataVerificationError::MissingSubjectTypesSupported)?;
806
807        self.id_token_signing_alg_values_supported
808            .as_ref()
809            .ok_or(ProviderMetadataVerificationError::MissingIdTokenSigningAlgValuesSupported)?;
810
811        Ok(VerifiedProviderMetadata { inner: self })
812    }
813
814    /// JSON array containing a list of the OAuth 2.0 `response_mode` values
815    /// that this authorization server supports.
816    ///
817    /// Defaults to [`DEFAULT_RESPONSE_MODES_SUPPORTED`].
818    #[must_use]
819    pub fn response_modes_supported(&self) -> &[ResponseMode] {
820        self.response_modes_supported
821            .as_deref()
822            .unwrap_or(DEFAULT_RESPONSE_MODES_SUPPORTED)
823    }
824
825    /// JSON array containing a list of the OAuth 2.0 grant type values that
826    /// this authorization server supports.
827    ///
828    /// Defaults to [`DEFAULT_GRANT_TYPES_SUPPORTED`].
829    #[must_use]
830    pub fn grant_types_supported(&self) -> &[GrantType] {
831        self.grant_types_supported
832            .as_deref()
833            .unwrap_or(DEFAULT_GRANT_TYPES_SUPPORTED)
834    }
835
836    /// JSON array containing a list of client authentication methods supported
837    /// by the token endpoint.
838    ///
839    /// Defaults to [`DEFAULT_AUTH_METHODS_SUPPORTED`].
840    #[must_use]
841    pub fn token_endpoint_auth_methods_supported(&self) -> &[OAuthClientAuthenticationMethod] {
842        self.token_endpoint_auth_methods_supported
843            .as_deref()
844            .unwrap_or(DEFAULT_AUTH_METHODS_SUPPORTED)
845    }
846
847    /// JSON array containing a list of client authentication methods supported
848    /// by the revocation endpoint.
849    ///
850    /// Defaults to [`DEFAULT_AUTH_METHODS_SUPPORTED`].
851    #[must_use]
852    pub fn revocation_endpoint_auth_methods_supported(&self) -> &[OAuthClientAuthenticationMethod] {
853        self.revocation_endpoint_auth_methods_supported
854            .as_deref()
855            .unwrap_or(DEFAULT_AUTH_METHODS_SUPPORTED)
856    }
857
858    /// JSON array containing a list of the Claim Types that the OpenID Provider
859    /// supports.
860    ///
861    /// Defaults to [`DEFAULT_CLAIM_TYPES_SUPPORTED`].
862    #[must_use]
863    pub fn claim_types_supported(&self) -> &[ClaimType] {
864        self.claim_types_supported
865            .as_deref()
866            .unwrap_or(DEFAULT_CLAIM_TYPES_SUPPORTED)
867    }
868
869    /// Boolean value specifying whether the OP supports use of the `claims`
870    /// parameter.
871    ///
872    /// Defaults to `false`.
873    #[must_use]
874    pub fn claims_parameter_supported(&self) -> bool {
875        self.claims_parameter_supported.unwrap_or(false)
876    }
877
878    /// Boolean value specifying whether the OP supports use of the `request`
879    /// parameter.
880    ///
881    /// Defaults to `false`.
882    #[must_use]
883    pub fn request_parameter_supported(&self) -> bool {
884        self.request_parameter_supported.unwrap_or(false)
885    }
886
887    /// Boolean value specifying whether the OP supports use of the
888    /// `request_uri` parameter.
889    ///
890    /// Defaults to `true`.
891    #[must_use]
892    pub fn request_uri_parameter_supported(&self) -> bool {
893        self.request_uri_parameter_supported.unwrap_or(true)
894    }
895
896    /// Boolean value specifying whether the OP requires any `request_uri`
897    /// values used to be pre-registered.
898    ///
899    /// Defaults to `false`.
900    #[must_use]
901    pub fn require_request_uri_registration(&self) -> bool {
902        self.require_request_uri_registration.unwrap_or(false)
903    }
904
905    /// Indicates where authorization request needs to be protected as Request
906    /// Object and provided through either `request` or `request_uri` parameter.
907    ///
908    /// Defaults to `false`.
909    #[must_use]
910    pub fn require_signed_request_object(&self) -> bool {
911        self.require_signed_request_object.unwrap_or(false)
912    }
913
914    /// Indicates whether the authorization server accepts authorization
915    /// requests only via PAR.
916    ///
917    /// Defaults to `false`.
918    #[must_use]
919    pub fn require_pushed_authorization_requests(&self) -> bool {
920        self.require_pushed_authorization_requests.unwrap_or(false)
921    }
922}
923
924/// The verified authorization server metadata.
925///
926/// All the fields required by the [OpenID Connect Discovery Spec 1.0] or with
927/// a default value are accessible via methods.
928///
929/// To access other fields, use this type's `Deref` implementation.
930///
931/// [OpenID Connect Discovery Spec 1.0]: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
932#[derive(Debug, Clone)]
933pub struct VerifiedProviderMetadata {
934    inner: ProviderMetadata,
935}
936
937impl VerifiedProviderMetadata {
938    /// Authorization server's issuer identifier URL.
939    #[must_use]
940    pub fn issuer(&self) -> &str {
941        match &self.issuer {
942            Some(u) => u,
943            None => unreachable!(),
944        }
945    }
946
947    /// URL of the authorization server's authorization endpoint.
948    #[must_use]
949    pub fn authorization_endpoint(&self) -> &Url {
950        match &self.authorization_endpoint {
951            Some(u) => u,
952            None => unreachable!(),
953        }
954    }
955
956    /// URL of the authorization server's userinfo endpoint.
957    #[must_use]
958    pub fn userinfo_endpoint(&self) -> &Url {
959        match &self.userinfo_endpoint {
960            Some(u) => u,
961            None => unreachable!(),
962        }
963    }
964
965    /// URL of the authorization server's token endpoint.
966    #[must_use]
967    pub fn token_endpoint(&self) -> &Url {
968        match &self.token_endpoint {
969            Some(u) => u,
970            None => unreachable!(),
971        }
972    }
973
974    /// URL of the authorization server's JWK Set document.
975    #[must_use]
976    pub fn jwks_uri(&self) -> &Url {
977        match &self.jwks_uri {
978            Some(u) => u,
979            None => unreachable!(),
980        }
981    }
982
983    /// JSON array containing a list of the OAuth 2.0 `response_type` values
984    /// that this authorization server supports.
985    #[must_use]
986    pub fn response_types_supported(&self) -> &[ResponseType] {
987        match &self.response_types_supported {
988            Some(u) => u,
989            None => unreachable!(),
990        }
991    }
992
993    /// JSON array containing a list of the Subject Identifier types that this
994    /// OP supports.
995    #[must_use]
996    pub fn subject_types_supported(&self) -> &[SubjectType] {
997        match &self.subject_types_supported {
998            Some(u) => u,
999            None => unreachable!(),
1000        }
1001    }
1002
1003    /// JSON array containing a list of the JWS `alg` values supported by the OP
1004    /// for the ID Token.
1005    #[must_use]
1006    pub fn id_token_signing_alg_values_supported(&self) -> &[JsonWebSignatureAlg] {
1007        match &self.id_token_signing_alg_values_supported {
1008            Some(u) => u,
1009            None => unreachable!(),
1010        }
1011    }
1012}
1013
1014impl Deref for VerifiedProviderMetadata {
1015    type Target = ProviderMetadata;
1016
1017    fn deref(&self) -> &Self::Target {
1018        &self.inner
1019    }
1020}
1021
1022/// All errors that can happen when verifying [`ProviderMetadata`]
1023#[derive(Debug, Error)]
1024pub enum ProviderMetadataVerificationError {
1025    /// The issuer is missing.
1026    #[error("issuer is missing")]
1027    MissingIssuer,
1028
1029    /// The issuer is not a valid URL.
1030    #[error("issuer is not a valid URL")]
1031    IssuerNotUrl,
1032
1033    /// The authorization endpoint is missing.
1034    #[error("authorization endpoint is missing")]
1035    MissingAuthorizationEndpoint,
1036
1037    /// The token endpoint is missing.
1038    #[error("token endpoint is missing")]
1039    MissingTokenEndpoint,
1040
1041    /// The JWK Set URI is missing.
1042    #[error("JWK Set URI is missing")]
1043    MissingJwksUri,
1044
1045    /// The supported response types are missing.
1046    #[error("supported response types are missing")]
1047    MissingResponseTypesSupported,
1048
1049    /// The supported subject types are missing.
1050    #[error("supported subject types are missing")]
1051    MissingSubjectTypesSupported,
1052
1053    /// The supported ID token signing algorithm values are missing.
1054    #[error("supported ID token signing algorithm values are missing")]
1055    MissingIdTokenSigningAlgValuesSupported,
1056
1057    /// The URL of the given field doesn't use a `https` scheme.
1058    #[error("{0}'s URL doesn't use a https scheme: {1}")]
1059    UrlNonHttpsScheme(&'static str, Url),
1060
1061    /// The URL of the given field contains a query, but it's not allowed.
1062    #[error("{0}'s URL contains a query: {1}")]
1063    UrlWithQuery(&'static str, Url),
1064
1065    /// The URL of the given field contains a fragment, but it's not allowed.
1066    #[error("{0}'s URL contains a fragment: {1}")]
1067    UrlWithFragment(&'static str, Url),
1068
1069    /// The issuer URL doesn't match the one that was discovered.
1070    #[error("issuer URLs don't match: expected {expected:?}, got {actual:?}")]
1071    IssuerUrlsDontMatch {
1072        /// The expected issuer URL.
1073        expected: String,
1074        /// The issuer URL that was discovered.
1075        actual: String,
1076    },
1077
1078    /// `openid` is missing from the supported scopes.
1079    #[error("missing openid scope")]
1080    ScopesMissingOpenid,
1081
1082    /// `code` is missing from the supported response types.
1083    #[error("missing `code` response type")]
1084    ResponseTypesMissingCode,
1085
1086    /// `id_token` is missing from the supported response types.
1087    #[error("missing `id_token` response type")]
1088    ResponseTypesMissingIdToken,
1089
1090    /// `id_token token` is missing from the supported response types.
1091    #[error("missing `id_token token` response type")]
1092    ResponseTypesMissingIdTokenToken,
1093
1094    /// `authorization_code` is missing from the supported grant types.
1095    #[error("missing `authorization_code` grant type")]
1096    GrantTypesMissingAuthorizationCode,
1097
1098    /// `implicit` is missing from the supported grant types.
1099    #[error("missing `implicit` grant type")]
1100    GrantTypesMissingImplicit,
1101
1102    /// The given endpoint is missing auth signing algorithm values, but they
1103    /// are required because it supports at least one of the `client_secret_jwt`
1104    /// or `private_key_jwt` authentication methods.
1105    #[error("{0} missing auth signing algorithm values")]
1106    MissingAuthSigningAlgValues(&'static str),
1107
1108    /// `none` is in the given endpoint's signing algorithm values, but is not
1109    /// allowed.
1110    #[error("{0} signing algorithm values contain `none`")]
1111    SigningAlgValuesWithNone(&'static str),
1112}
1113
1114/// Possible extra restrictions on a URL.
1115#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1116enum ExtraUrlRestrictions {
1117    /// No extra restrictions.
1118    None,
1119
1120    /// The URL must not contain a fragment.
1121    NoFragment,
1122
1123    /// The URL must not contain a query or a fragment.
1124    NoQueryOrFragment,
1125}
1126
1127impl ExtraUrlRestrictions {
1128    fn can_have_fragment(self) -> bool {
1129        self == Self::None
1130    }
1131
1132    fn can_have_query(self) -> bool {
1133        self != Self::NoQueryOrFragment
1134    }
1135}
1136
1137/// Validate the URL of the field with the given extra restrictions.
1138///
1139/// The basic restriction is that the URL must use the `https` scheme.
1140fn validate_url(
1141    field: &'static str,
1142    url: &Url,
1143    restrictions: ExtraUrlRestrictions,
1144) -> Result<(), ProviderMetadataVerificationError> {
1145    if url.scheme() != "https" {
1146        return Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(
1147            field,
1148            url.clone(),
1149        ));
1150    }
1151
1152    if !restrictions.can_have_query() && url.query().is_some() {
1153        return Err(ProviderMetadataVerificationError::UrlWithQuery(
1154            field,
1155            url.clone(),
1156        ));
1157    }
1158
1159    if !restrictions.can_have_fragment() && url.fragment().is_some() {
1160        return Err(ProviderMetadataVerificationError::UrlWithFragment(
1161            field,
1162            url.clone(),
1163        ));
1164    }
1165
1166    Ok(())
1167}
1168
1169/// Validate the algorithm values of the endpoint according to the
1170/// authentication methods.
1171///
1172/// The restrictions are:
1173/// - The algorithm values must not contain `none`,
1174/// - If the `client_secret_jwt` or `private_key_jwt` authentication methods are
1175///   supported, the values must be present.
1176fn validate_signing_alg_values_supported<'a>(
1177    endpoint: &'static str,
1178    values: impl Iterator<Item = &'a JsonWebSignatureAlg>,
1179    mut methods: impl Iterator<Item = &'a OAuthClientAuthenticationMethod>,
1180) -> Result<(), ProviderMetadataVerificationError> {
1181    let mut no_values = true;
1182
1183    for value in values {
1184        if *value == JsonWebSignatureAlg::None {
1185            return Err(ProviderMetadataVerificationError::SigningAlgValuesWithNone(
1186                endpoint,
1187            ));
1188        }
1189
1190        no_values = false;
1191    }
1192
1193    if no_values
1194        && methods.any(|method| {
1195            matches!(
1196                method,
1197                OAuthClientAuthenticationMethod::ClientSecretJwt
1198                    | OAuthClientAuthenticationMethod::PrivateKeyJwt
1199            )
1200        })
1201    {
1202        return Err(ProviderMetadataVerificationError::MissingAuthSigningAlgValues(endpoint));
1203    }
1204
1205    Ok(())
1206}
1207
1208/// The body of a request to the [RP-Initiated Logout Endpoint].
1209///
1210/// [RP-Initiated Logout Endpoint]: https://openid.net/specs/openid-connect-rpinitiated-1_0.html
1211#[skip_serializing_none]
1212#[serde_as]
1213#[derive(Default, Serialize, Deserialize, Clone)]
1214pub struct RpInitiatedLogoutRequest {
1215    /// ID Token previously issued by the OP to the RP.
1216    ///
1217    /// Recommended, used as a hint about the End-User's current authenticated
1218    /// session with the Client.
1219    pub id_token_hint: Option<String>,
1220
1221    /// Hint to the Authorization Server about the End-User that is logging out.
1222    ///
1223    /// The value and meaning of this parameter is left up to the OP's
1224    /// discretion. For instance, the value might contain an email address,
1225    /// phone number, username, or session identifier pertaining to the RP's
1226    /// session with the OP for the End-User.
1227    pub logout_hint: Option<String>,
1228
1229    /// OAuth 2.0 Client Identifier valid at the Authorization Server.
1230    ///
1231    /// The most common use case for this parameter is to specify the Client
1232    /// Identifier when `post_logout_redirect_uri` is used but `id_token_hint`
1233    /// is not. Another use is for symmetrically encrypted ID Tokens used as
1234    /// `id_token_hint` values that require the Client Identifier to be
1235    /// specified by other means, so that the ID Tokens can be decrypted by
1236    /// the OP.
1237    pub client_id: Option<String>,
1238
1239    /// URI to which the RP is requesting that the End-User's User Agent be
1240    /// redirected after a logout has been performed.
1241    ///
1242    /// The value MUST have been previously registered with the OP, using the
1243    /// `post_logout_redirect_uris` registration parameter.
1244    pub post_logout_redirect_uri: Option<Url>,
1245
1246    /// Opaque value used by the RP to maintain state between the logout request
1247    /// and the callback to the endpoint specified by the
1248    /// `post_logout_redirect_uri` parameter.
1249    pub state: Option<String>,
1250
1251    /// End-User's preferred languages and scripts for the user interface,
1252    /// ordered by preference.
1253    #[serde_as(as = "Option<StringWithSeparator::<SpaceSeparator, LanguageTag>>")]
1254    #[serde(default)]
1255    pub ui_locales: Option<Vec<LanguageTag>>,
1256}
1257
1258impl fmt::Debug for RpInitiatedLogoutRequest {
1259    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1260        f.debug_struct("RpInitiatedLogoutRequest")
1261            .field("logout_hint", &self.logout_hint)
1262            .field("post_logout_redirect_uri", &self.post_logout_redirect_uri)
1263            .field("ui_locales", &self.ui_locales)
1264            .finish_non_exhaustive()
1265    }
1266}
1267
1268#[cfg(test)]
1269mod tests {
1270    use assert_matches::assert_matches;
1271    use mas_iana::{
1272        jose::JsonWebSignatureAlg,
1273        oauth::{OAuthAuthorizationEndpointResponseType, OAuthClientAuthenticationMethod},
1274    };
1275    use url::Url;
1276
1277    use super::*;
1278
1279    fn valid_provider_metadata() -> (ProviderMetadata, String) {
1280        let issuer = "https://localhost".to_owned();
1281        let metadata = ProviderMetadata {
1282            issuer: Some(issuer.clone()),
1283            authorization_endpoint: Some(Url::parse("https://localhost/auth").unwrap()),
1284            token_endpoint: Some(Url::parse("https://localhost/token").unwrap()),
1285            jwks_uri: Some(Url::parse("https://localhost/jwks").unwrap()),
1286            response_types_supported: Some(vec![
1287                OAuthAuthorizationEndpointResponseType::Code.into(),
1288            ]),
1289            subject_types_supported: Some(vec![SubjectType::Public]),
1290            id_token_signing_alg_values_supported: Some(vec![JsonWebSignatureAlg::Rs256]),
1291            ..Default::default()
1292        };
1293
1294        (metadata, issuer)
1295    }
1296
1297    #[test]
1298    fn validate_required_metadata() {
1299        let (metadata, issuer) = valid_provider_metadata();
1300        metadata.validate(&issuer).unwrap();
1301    }
1302
1303    #[test]
1304    fn validate_issuer() {
1305        let (mut metadata, issuer) = valid_provider_metadata();
1306
1307        // Err - Missing
1308        metadata.issuer = None;
1309        assert_matches!(
1310            metadata.clone().validate(&issuer),
1311            Err(ProviderMetadataVerificationError::MissingIssuer)
1312        );
1313
1314        // Err - Not an url
1315        metadata.issuer = Some("not-an-url".to_owned());
1316        assert_matches!(
1317            metadata.clone().validate("not-an-url"),
1318            Err(ProviderMetadataVerificationError::IssuerNotUrl)
1319        );
1320
1321        // Err - Wrong issuer
1322        metadata.issuer = Some("https://example.com/".to_owned());
1323        assert_matches!(
1324            metadata.clone().validate(&issuer),
1325            Err(ProviderMetadataVerificationError::IssuerUrlsDontMatch { .. })
1326        );
1327
1328        // Err - Not https
1329        let issuer = "http://localhost/".to_owned();
1330        metadata.issuer = Some(issuer.clone());
1331        let (field, url) = assert_matches!(
1332            metadata.clone().validate(&issuer),
1333            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1334        );
1335        assert_eq!(field, "issuer");
1336        assert_eq!(url.as_str(), issuer);
1337
1338        // Err - Query
1339        let issuer = "https://localhost/?query".to_owned();
1340        metadata.issuer = Some(issuer.clone());
1341        let (field, url) = assert_matches!(
1342            metadata.clone().validate(&issuer),
1343            Err(ProviderMetadataVerificationError::UrlWithQuery(field, url)) => (field, url)
1344        );
1345        assert_eq!(field, "issuer");
1346        assert_eq!(url.as_str(), issuer);
1347
1348        // Err - Fragment
1349        let issuer = "https://localhost/#fragment".to_owned();
1350        metadata.issuer = Some(issuer.clone());
1351        let (field, url) = assert_matches!(
1352            metadata.clone().validate(&issuer),
1353            Err(ProviderMetadataVerificationError::UrlWithFragment(field, url)) => (field, url)
1354        );
1355        assert_eq!(field, "issuer");
1356        assert_eq!(url.as_str(), issuer);
1357
1358        // Ok - Path
1359        let issuer = "https://localhost/issuer1".to_owned();
1360        metadata.issuer = Some(issuer.clone());
1361        metadata.validate(&issuer).unwrap();
1362    }
1363
1364    #[test]
1365    fn validate_authorization_endpoint() {
1366        let (mut metadata, issuer) = valid_provider_metadata();
1367
1368        // Err - Missing
1369        metadata.authorization_endpoint = None;
1370        assert_matches!(
1371            metadata.clone().validate(&issuer),
1372            Err(ProviderMetadataVerificationError::MissingAuthorizationEndpoint)
1373        );
1374
1375        // Err - Not https
1376        let endpoint = Url::parse("http://localhost/auth").unwrap();
1377        metadata.authorization_endpoint = Some(endpoint.clone());
1378        let (field, url) = assert_matches!(
1379            metadata.clone().validate(&issuer),
1380            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1381        );
1382        assert_eq!(field, "authorization_endpoint");
1383        assert_eq!(url, endpoint);
1384
1385        // Err - Fragment
1386        let endpoint = Url::parse("https://localhost/auth#fragment").unwrap();
1387        metadata.authorization_endpoint = Some(endpoint.clone());
1388        let (field, url) = assert_matches!(
1389            metadata.clone().validate(&issuer),
1390            Err(ProviderMetadataVerificationError::UrlWithFragment(field, url)) => (field, url)
1391        );
1392        assert_eq!(field, "authorization_endpoint");
1393        assert_eq!(url, endpoint);
1394
1395        // Ok - Query
1396        metadata.authorization_endpoint = Some(Url::parse("https://localhost/auth?query").unwrap());
1397        metadata.validate(&issuer).unwrap();
1398    }
1399
1400    #[test]
1401    fn validate_token_endpoint() {
1402        let (mut metadata, issuer) = valid_provider_metadata();
1403
1404        // Err - Missing
1405        metadata.token_endpoint = None;
1406        assert_matches!(
1407            metadata.clone().validate(&issuer),
1408            Err(ProviderMetadataVerificationError::MissingTokenEndpoint)
1409        );
1410
1411        // Err - Not https
1412        let endpoint = Url::parse("http://localhost/token").unwrap();
1413        metadata.token_endpoint = Some(endpoint.clone());
1414        let (field, url) = assert_matches!(
1415            metadata.clone().validate(&issuer),
1416            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1417        );
1418        assert_eq!(field, "token_endpoint");
1419        assert_eq!(url, endpoint);
1420
1421        // Err - Fragment
1422        let endpoint = Url::parse("https://localhost/token#fragment").unwrap();
1423        metadata.token_endpoint = Some(endpoint.clone());
1424        let (field, url) = assert_matches!(
1425            metadata.clone().validate(&issuer),
1426            Err(ProviderMetadataVerificationError::UrlWithFragment(field, url)) => (field, url)
1427        );
1428        assert_eq!(field, "token_endpoint");
1429        assert_eq!(url, endpoint);
1430
1431        // Ok - Query
1432        metadata.token_endpoint = Some(Url::parse("https://localhost/token?query").unwrap());
1433        metadata.validate(&issuer).unwrap();
1434    }
1435
1436    #[test]
1437    fn validate_jwks_uri() {
1438        let (mut metadata, issuer) = valid_provider_metadata();
1439
1440        // Err - Missing
1441        metadata.jwks_uri = None;
1442        assert_matches!(
1443            metadata.clone().validate(&issuer),
1444            Err(ProviderMetadataVerificationError::MissingJwksUri)
1445        );
1446
1447        // Err - Not https
1448        let endpoint = Url::parse("http://localhost/jwks").unwrap();
1449        metadata.jwks_uri = Some(endpoint.clone());
1450        let (field, url) = assert_matches!(
1451            metadata.clone().validate(&issuer),
1452            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1453        );
1454        assert_eq!(field, "jwks_uri");
1455        assert_eq!(url, endpoint);
1456
1457        // Ok - Query & fragment
1458        metadata.jwks_uri = Some(Url::parse("https://localhost/token?query#fragment").unwrap());
1459        metadata.validate(&issuer).unwrap();
1460    }
1461
1462    #[test]
1463    fn validate_registration_endpoint() {
1464        let (mut metadata, issuer) = valid_provider_metadata();
1465
1466        // Err - Not https
1467        let endpoint = Url::parse("http://localhost/registration").unwrap();
1468        metadata.registration_endpoint = Some(endpoint.clone());
1469        let (field, url) = assert_matches!(
1470            metadata.clone().validate(&issuer),
1471            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1472        );
1473        assert_eq!(field, "registration_endpoint");
1474        assert_eq!(url, endpoint);
1475
1476        // Ok - Missing
1477        metadata.registration_endpoint = None;
1478        metadata.clone().validate(&issuer).unwrap();
1479
1480        // Ok - Query & fragment
1481        metadata.registration_endpoint =
1482            Some(Url::parse("https://localhost/registration?query#fragment").unwrap());
1483        metadata.validate(&issuer).unwrap();
1484    }
1485
1486    #[test]
1487    fn validate_scopes_supported() {
1488        let (mut metadata, issuer) = valid_provider_metadata();
1489
1490        // Err - No `openid`
1491        metadata.scopes_supported = Some(vec!["custom".to_owned()]);
1492        assert_matches!(
1493            metadata.clone().validate(&issuer),
1494            Err(ProviderMetadataVerificationError::ScopesMissingOpenid)
1495        );
1496
1497        // Ok - Missing
1498        metadata.scopes_supported = None;
1499        metadata.clone().validate(&issuer).unwrap();
1500
1501        // Ok - With `openid`
1502        metadata.scopes_supported = Some(vec!["openid".to_owned(), "custom".to_owned()]);
1503        metadata.validate(&issuer).unwrap();
1504    }
1505
1506    #[test]
1507    fn validate_response_types_supported() {
1508        let (mut metadata, issuer) = valid_provider_metadata();
1509
1510        // Err - Missing
1511        metadata.response_types_supported = None;
1512        assert_matches!(
1513            metadata.clone().validate(&issuer),
1514            Err(ProviderMetadataVerificationError::MissingResponseTypesSupported)
1515        );
1516
1517        // Ok - Present
1518        metadata.response_types_supported =
1519            Some(vec![OAuthAuthorizationEndpointResponseType::Code.into()]);
1520        metadata.validate(&issuer).unwrap();
1521    }
1522
1523    #[test]
1524    fn validate_token_endpoint_signing_alg_values_supported() {
1525        let (mut metadata, issuer) = valid_provider_metadata();
1526
1527        // Ok - Missing
1528        metadata.token_endpoint_auth_signing_alg_values_supported = None;
1529        metadata.token_endpoint_auth_methods_supported = None;
1530        metadata.clone().validate(&issuer).unwrap();
1531
1532        // Err - With `none`
1533        metadata.token_endpoint_auth_signing_alg_values_supported =
1534            Some(vec![JsonWebSignatureAlg::None]);
1535        let endpoint = assert_matches!(
1536            metadata.clone().validate(&issuer),
1537            Err(ProviderMetadataVerificationError::SigningAlgValuesWithNone(endpoint)) => endpoint
1538        );
1539        assert_eq!(endpoint, "token_endpoint");
1540
1541        // Ok - Other signing alg values.
1542        metadata.token_endpoint_auth_signing_alg_values_supported =
1543            Some(vec![JsonWebSignatureAlg::Rs256, JsonWebSignatureAlg::EdDsa]);
1544        metadata.clone().validate(&issuer).unwrap();
1545
1546        // Err - `client_secret_jwt` without signing alg values.
1547        metadata.token_endpoint_auth_methods_supported =
1548            Some(vec![OAuthClientAuthenticationMethod::ClientSecretJwt]);
1549        metadata.token_endpoint_auth_signing_alg_values_supported = None;
1550        let endpoint = assert_matches!(
1551            metadata.clone().validate(&issuer),
1552            Err(ProviderMetadataVerificationError::MissingAuthSigningAlgValues(endpoint)) => endpoint
1553        );
1554        assert_eq!(endpoint, "token_endpoint");
1555
1556        // Ok - `client_secret_jwt` with signing alg values.
1557        metadata.token_endpoint_auth_signing_alg_values_supported =
1558            Some(vec![JsonWebSignatureAlg::Rs256]);
1559        metadata.clone().validate(&issuer).unwrap();
1560
1561        // Err - `private_key_jwt` without signing alg values.
1562        metadata.token_endpoint_auth_methods_supported =
1563            Some(vec![OAuthClientAuthenticationMethod::PrivateKeyJwt]);
1564        metadata.token_endpoint_auth_signing_alg_values_supported = None;
1565        let endpoint = assert_matches!(
1566            metadata.clone().validate(&issuer),
1567            Err(ProviderMetadataVerificationError::MissingAuthSigningAlgValues(endpoint)) => endpoint
1568        );
1569        assert_eq!(endpoint, "token_endpoint");
1570
1571        // Ok - `private_key_jwt` with signing alg values.
1572        metadata.token_endpoint_auth_signing_alg_values_supported =
1573            Some(vec![JsonWebSignatureAlg::Rs256]);
1574        metadata.clone().validate(&issuer).unwrap();
1575
1576        // Ok - Other auth methods without signing alg values.
1577        metadata.token_endpoint_auth_methods_supported = Some(vec![
1578            OAuthClientAuthenticationMethod::ClientSecretBasic,
1579            OAuthClientAuthenticationMethod::ClientSecretPost,
1580        ]);
1581        metadata.token_endpoint_auth_signing_alg_values_supported = None;
1582        metadata.validate(&issuer).unwrap();
1583    }
1584
1585    #[test]
1586    fn validate_revocation_endpoint() {
1587        let (mut metadata, issuer) = valid_provider_metadata();
1588
1589        // Ok - Missing
1590        metadata.revocation_endpoint = None;
1591        metadata.clone().validate(&issuer).unwrap();
1592
1593        // Err - Not https
1594        let endpoint = Url::parse("http://localhost/revocation").unwrap();
1595        metadata.revocation_endpoint = Some(endpoint.clone());
1596        let (field, url) = assert_matches!(
1597            metadata.clone().validate(&issuer),
1598            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1599        );
1600        assert_eq!(field, "revocation_endpoint");
1601        assert_eq!(url, endpoint);
1602
1603        // Err - Fragment
1604        let endpoint = Url::parse("https://localhost/revocation#fragment").unwrap();
1605        metadata.revocation_endpoint = Some(endpoint.clone());
1606        let (field, url) = assert_matches!(
1607            metadata.clone().validate(&issuer),
1608            Err(ProviderMetadataVerificationError::UrlWithFragment(field, url)) => (field, url)
1609        );
1610        assert_eq!(field, "revocation_endpoint");
1611        assert_eq!(url, endpoint);
1612
1613        // Ok - Query
1614        metadata.revocation_endpoint =
1615            Some(Url::parse("https://localhost/revocation?query").unwrap());
1616        metadata.validate(&issuer).unwrap();
1617    }
1618
1619    #[test]
1620    fn validate_revocation_endpoint_signing_alg_values_supported() {
1621        let (mut metadata, issuer) = valid_provider_metadata();
1622
1623        // Only check that this field is validated, algorithm checks are already
1624        // tested for the token endpoint.
1625
1626        // Ok - Missing
1627        metadata.revocation_endpoint_auth_signing_alg_values_supported = None;
1628        metadata.revocation_endpoint_auth_methods_supported = None;
1629        metadata.clone().validate(&issuer).unwrap();
1630
1631        // Err - With `none`
1632        metadata.revocation_endpoint_auth_signing_alg_values_supported =
1633            Some(vec![JsonWebSignatureAlg::None]);
1634        let endpoint = assert_matches!(
1635            metadata.validate(&issuer),
1636            Err(ProviderMetadataVerificationError::SigningAlgValuesWithNone(endpoint)) => endpoint
1637        );
1638        assert_eq!(endpoint, "revocation_endpoint");
1639    }
1640
1641    #[test]
1642    fn validate_introspection_endpoint() {
1643        let (mut metadata, issuer) = valid_provider_metadata();
1644
1645        // Ok - Missing
1646        metadata.introspection_endpoint = None;
1647        metadata.clone().validate(&issuer).unwrap();
1648
1649        // Err - Not https
1650        let endpoint = Url::parse("http://localhost/introspection").unwrap();
1651        metadata.introspection_endpoint = Some(endpoint.clone());
1652        let (field, url) = assert_matches!(
1653            metadata.clone().validate(&issuer),
1654            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1655        );
1656        assert_eq!(field, "introspection_endpoint");
1657        assert_eq!(url, endpoint);
1658
1659        // Ok - Query & Fragment
1660        metadata.introspection_endpoint =
1661            Some(Url::parse("https://localhost/introspection?query#fragment").unwrap());
1662        metadata.validate(&issuer).unwrap();
1663    }
1664
1665    #[test]
1666    fn validate_introspection_endpoint_signing_alg_values_supported() {
1667        let (mut metadata, issuer) = valid_provider_metadata();
1668
1669        // Only check that this field is validated, algorithm checks are already
1670        // tested for the token endpoint.
1671
1672        // Ok - Missing
1673        metadata.introspection_endpoint_auth_signing_alg_values_supported = None;
1674        metadata.introspection_endpoint_auth_methods_supported = None;
1675        metadata.clone().validate(&issuer).unwrap();
1676
1677        // Err - With `none`
1678        metadata.introspection_endpoint_auth_signing_alg_values_supported =
1679            Some(vec![JsonWebSignatureAlg::None]);
1680        let endpoint = assert_matches!(
1681            metadata.validate(&issuer),
1682            Err(ProviderMetadataVerificationError::SigningAlgValuesWithNone(endpoint)) => endpoint
1683        );
1684        assert_eq!(endpoint, "introspection_endpoint");
1685    }
1686
1687    #[test]
1688    fn validate_userinfo_endpoint() {
1689        let (mut metadata, issuer) = valid_provider_metadata();
1690
1691        // Ok - Missing
1692        metadata.userinfo_endpoint = None;
1693        metadata.clone().validate(&issuer).unwrap();
1694
1695        // Err - Not https
1696        let endpoint = Url::parse("http://localhost/userinfo").unwrap();
1697        metadata.userinfo_endpoint = Some(endpoint.clone());
1698        let (field, url) = assert_matches!(
1699            metadata.clone().validate(&issuer),
1700            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1701        );
1702        assert_eq!(field, "userinfo_endpoint");
1703        assert_eq!(url, endpoint);
1704
1705        // Ok - Query & Fragment
1706        metadata.userinfo_endpoint =
1707            Some(Url::parse("https://localhost/userinfo?query#fragment").unwrap());
1708        metadata.validate(&issuer).unwrap();
1709    }
1710
1711    #[test]
1712    fn validate_subject_types_supported() {
1713        let (mut metadata, issuer) = valid_provider_metadata();
1714
1715        // Err - Missing
1716        metadata.subject_types_supported = None;
1717        assert_matches!(
1718            metadata.clone().validate(&issuer),
1719            Err(ProviderMetadataVerificationError::MissingSubjectTypesSupported)
1720        );
1721
1722        // Ok - Present
1723        metadata.subject_types_supported = Some(vec![SubjectType::Public, SubjectType::Pairwise]);
1724        metadata.validate(&issuer).unwrap();
1725    }
1726
1727    #[test]
1728    fn validate_id_token_signing_alg_values_supported() {
1729        let (mut metadata, issuer) = valid_provider_metadata();
1730
1731        // Err - Missing
1732        metadata.id_token_signing_alg_values_supported = None;
1733        assert_matches!(
1734            metadata.clone().validate(&issuer),
1735            Err(ProviderMetadataVerificationError::MissingIdTokenSigningAlgValuesSupported)
1736        );
1737
1738        // Ok - Present
1739        metadata.id_token_signing_alg_values_supported =
1740            Some(vec![JsonWebSignatureAlg::Rs256, JsonWebSignatureAlg::EdDsa]);
1741        metadata.validate(&issuer).unwrap();
1742    }
1743
1744    #[test]
1745    fn validate_pushed_authorization_request_endpoint() {
1746        let (mut metadata, issuer) = valid_provider_metadata();
1747
1748        // Ok - Missing
1749        metadata.pushed_authorization_request_endpoint = None;
1750        metadata.clone().validate(&issuer).unwrap();
1751
1752        // Err - Not https
1753        let endpoint = Url::parse("http://localhost/par").unwrap();
1754        metadata.pushed_authorization_request_endpoint = Some(endpoint.clone());
1755        let (field, url) = assert_matches!(
1756            metadata.clone().validate(&issuer),
1757            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1758        );
1759        assert_eq!(field, "pushed_authorization_request_endpoint");
1760        assert_eq!(url, endpoint);
1761
1762        // Ok - Query & Fragment
1763        metadata.pushed_authorization_request_endpoint =
1764            Some(Url::parse("https://localhost/par?query#fragment").unwrap());
1765        metadata.validate(&issuer).unwrap();
1766    }
1767
1768    #[test]
1769    fn serialize_application_type() {
1770        assert_eq!(
1771            serde_json::to_string(&ApplicationType::Web).unwrap(),
1772            "\"web\""
1773        );
1774        assert_eq!(
1775            serde_json::to_string(&ApplicationType::Native).unwrap(),
1776            "\"native\""
1777        );
1778    }
1779
1780    #[test]
1781    fn deserialize_application_type() {
1782        assert_eq!(
1783            serde_json::from_str::<ApplicationType>("\"web\"").unwrap(),
1784            ApplicationType::Web
1785        );
1786        assert_eq!(
1787            serde_json::from_str::<ApplicationType>("\"native\"").unwrap(),
1788            ApplicationType::Native
1789        );
1790    }
1791
1792    #[test]
1793    fn serialize_subject_type() {
1794        assert_eq!(
1795            serde_json::to_string(&SubjectType::Public).unwrap(),
1796            "\"public\""
1797        );
1798        assert_eq!(
1799            serde_json::to_string(&SubjectType::Pairwise).unwrap(),
1800            "\"pairwise\""
1801        );
1802    }
1803
1804    #[test]
1805    fn deserialize_subject_type() {
1806        assert_eq!(
1807            serde_json::from_str::<SubjectType>("\"public\"").unwrap(),
1808            SubjectType::Public
1809        );
1810        assert_eq!(
1811            serde_json::from_str::<SubjectType>("\"pairwise\"").unwrap(),
1812            SubjectType::Pairwise
1813        );
1814    }
1815
1816    #[test]
1817    fn serialize_claim_type() {
1818        assert_eq!(
1819            serde_json::to_string(&ClaimType::Normal).unwrap(),
1820            "\"normal\""
1821        );
1822        assert_eq!(
1823            serde_json::to_string(&ClaimType::Aggregated).unwrap(),
1824            "\"aggregated\""
1825        );
1826        assert_eq!(
1827            serde_json::to_string(&ClaimType::Distributed).unwrap(),
1828            "\"distributed\""
1829        );
1830    }
1831
1832    #[test]
1833    fn deserialize_claim_type() {
1834        assert_eq!(
1835            serde_json::from_str::<ClaimType>("\"normal\"").unwrap(),
1836            ClaimType::Normal
1837        );
1838        assert_eq!(
1839            serde_json::from_str::<ClaimType>("\"aggregated\"").unwrap(),
1840            ClaimType::Aggregated
1841        );
1842        assert_eq!(
1843            serde_json::from_str::<ClaimType>("\"distributed\"").unwrap(),
1844            ClaimType::Distributed
1845        );
1846    }
1847
1848    #[test]
1849    fn deserialize_auth_method_or_token_type_type() {
1850        assert_eq!(
1851            serde_json::from_str::<AuthenticationMethodOrAccessTokenType>("\"none\"").unwrap(),
1852            AuthenticationMethodOrAccessTokenType::AuthenticationMethod(
1853                OAuthClientAuthenticationMethod::None
1854            )
1855        );
1856        assert_eq!(
1857            serde_json::from_str::<AuthenticationMethodOrAccessTokenType>("\"Bearer\"").unwrap(),
1858            AuthenticationMethodOrAccessTokenType::AccessTokenType(OAuthAccessTokenType::Bearer)
1859        );
1860        assert_eq!(
1861            serde_json::from_str::<AuthenticationMethodOrAccessTokenType>("\"unknown_value\"")
1862                .unwrap(),
1863            AuthenticationMethodOrAccessTokenType::Unknown("unknown_value".to_owned())
1864        );
1865    }
1866}