oauth2_types/
oidc.rs

1// Copyright 2021 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Types to interact with the [OpenID Connect] specification.
16//!
17//! [OpenID Connect]: https://openid.net/connect/
18
19use std::{fmt, ops::Deref};
20
21use language_tags::LanguageTag;
22use mas_iana::{
23    jose::{JsonWebEncryptionAlg, JsonWebEncryptionEnc, JsonWebSignatureAlg},
24    oauth::{OAuthAccessTokenType, OAuthClientAuthenticationMethod, PkceCodeChallengeMethod},
25};
26use serde::{Deserialize, Serialize};
27use serde_with::{
28    formats::SpaceSeparator, serde_as, skip_serializing_none, DeserializeFromStr, SerializeDisplay,
29    StringWithSeparator,
30};
31use thiserror::Error;
32use url::Url;
33
34use crate::{
35    requests::{Display, GrantType, Prompt, ResponseMode},
36    response_type::ResponseType,
37};
38
39/// An enum for types that accept either an [`OAuthClientAuthenticationMethod`]
40/// or an [`OAuthAccessTokenType`].
41#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug)]
42pub enum AuthenticationMethodOrAccessTokenType {
43    /// An authentication method.
44    AuthenticationMethod(OAuthClientAuthenticationMethod),
45
46    /// An access token type.
47    AccessTokenType(OAuthAccessTokenType),
48
49    /// An unknown value.
50    ///
51    /// Note that this variant should only be used as the result parsing a
52    /// string of unknown type. To build a custom variant, first parse a
53    /// string with the wanted type then use `.into()`.
54    Unknown(String),
55}
56
57impl core::fmt::Display for AuthenticationMethodOrAccessTokenType {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        match self {
60            Self::AuthenticationMethod(m) => m.fmt(f),
61            Self::AccessTokenType(t) => t.fmt(f),
62            Self::Unknown(s) => s.fmt(f),
63        }
64    }
65}
66
67impl core::str::FromStr for AuthenticationMethodOrAccessTokenType {
68    type Err = core::convert::Infallible;
69
70    fn from_str(s: &str) -> Result<Self, Self::Err> {
71        match OAuthClientAuthenticationMethod::from_str(s) {
72            Ok(OAuthClientAuthenticationMethod::Unknown(_)) | Err(_) => {}
73            Ok(m) => return Ok(m.into()),
74        }
75
76        match OAuthAccessTokenType::from_str(s) {
77            Ok(OAuthAccessTokenType::Unknown(_)) | Err(_) => {}
78            Ok(m) => return Ok(m.into()),
79        }
80
81        Ok(Self::Unknown(s.to_owned()))
82    }
83}
84
85impl AuthenticationMethodOrAccessTokenType {
86    /// Get the authentication method of this
87    /// `AuthenticationMethodOrAccessTokenType`.
88    #[must_use]
89    pub fn authentication_method(&self) -> Option<&OAuthClientAuthenticationMethod> {
90        match self {
91            Self::AuthenticationMethod(m) => Some(m),
92            _ => None,
93        }
94    }
95
96    /// Get the access token type of this
97    /// `AuthenticationMethodOrAccessTokenType`.
98    #[must_use]
99    pub fn access_token_type(&self) -> Option<&OAuthAccessTokenType> {
100        match self {
101            Self::AccessTokenType(t) => Some(t),
102            _ => None,
103        }
104    }
105}
106
107impl From<OAuthClientAuthenticationMethod> for AuthenticationMethodOrAccessTokenType {
108    fn from(t: OAuthClientAuthenticationMethod) -> Self {
109        Self::AuthenticationMethod(t)
110    }
111}
112
113impl From<OAuthAccessTokenType> for AuthenticationMethodOrAccessTokenType {
114    fn from(t: OAuthAccessTokenType) -> Self {
115        Self::AccessTokenType(t)
116    }
117}
118
119/// The kind of an application.
120#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug)]
121pub enum ApplicationType {
122    /// A web application.
123    Web,
124
125    /// A native application.
126    Native,
127
128    /// An unknown value.
129    Unknown(String),
130}
131
132impl core::fmt::Display for ApplicationType {
133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        match self {
135            Self::Web => f.write_str("web"),
136            Self::Native => f.write_str("native"),
137            Self::Unknown(s) => f.write_str(s),
138        }
139    }
140}
141
142impl core::str::FromStr for ApplicationType {
143    type Err = core::convert::Infallible;
144
145    fn from_str(s: &str) -> Result<Self, Self::Err> {
146        match s {
147            "web" => Ok(Self::Web),
148            "native" => Ok(Self::Native),
149            s => Ok(Self::Unknown(s.to_owned())),
150        }
151    }
152}
153
154/// Subject Identifier types.
155///
156/// A Subject Identifier is a locally unique and never reassigned identifier
157/// within the Issuer for the End-User, which is intended to be consumed by the
158/// Client.
159#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug)]
160pub enum SubjectType {
161    /// This provides the same `sub` (subject) value to all Clients.
162    Public,
163
164    /// This provides a different `sub` value to each Client, so as not to
165    /// enable Clients to correlate the End-User's activities without
166    /// permission.
167    Pairwise,
168
169    /// An unknown value.
170    Unknown(String),
171}
172
173impl core::fmt::Display for SubjectType {
174    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175        match self {
176            Self::Public => f.write_str("public"),
177            Self::Pairwise => f.write_str("pairwise"),
178            Self::Unknown(s) => f.write_str(s),
179        }
180    }
181}
182
183impl core::str::FromStr for SubjectType {
184    type Err = core::convert::Infallible;
185
186    fn from_str(s: &str) -> Result<Self, Self::Err> {
187        match s {
188            "public" => Ok(Self::Public),
189            "pairwise" => Ok(Self::Pairwise),
190            s => Ok(Self::Unknown(s.to_owned())),
191        }
192    }
193}
194
195/// Claim types.
196#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug)]
197pub enum ClaimType {
198    /// Claims that are directly asserted by the OpenID Provider.
199    Normal,
200
201    /// Claims that are asserted by a Claims Provider other than the OpenID
202    /// Provider but are returned by OpenID Provider.
203    Aggregated,
204
205    /// Claims that are asserted by a Claims Provider other than the OpenID
206    /// Provider but are returned as references by the OpenID Provider.
207    Distributed,
208
209    /// An unknown value.
210    Unknown(String),
211}
212
213impl core::fmt::Display for ClaimType {
214    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215        match self {
216            Self::Normal => f.write_str("normal"),
217            Self::Aggregated => f.write_str("aggregated"),
218            Self::Distributed => f.write_str("distributed"),
219            Self::Unknown(s) => f.write_str(s),
220        }
221    }
222}
223
224impl core::str::FromStr for ClaimType {
225    type Err = core::convert::Infallible;
226
227    fn from_str(s: &str) -> Result<Self, Self::Err> {
228        match s {
229            "normal" => Ok(Self::Normal),
230            "aggregated" => Ok(Self::Aggregated),
231            "distributed" => Ok(Self::Distributed),
232            s => Ok(Self::Unknown(s.to_owned())),
233        }
234    }
235}
236
237/// An account management action that a user can take.
238///
239/// Source: <https://github.com/matrix-org/matrix-spec-proposals/pull/2965>
240#[derive(
241    SerializeDisplay, DeserializeFromStr, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
242)]
243#[non_exhaustive]
244pub enum AccountManagementAction {
245    /// `org.matrix.profile`
246    ///
247    /// The user wishes to view their profile (name, avatar, contact details).
248    Profile,
249
250    /// `org.matrix.sessions_list`
251    ///
252    /// The user wishes to view a list of their sessions.
253    SessionsList,
254
255    /// `org.matrix.session_view`
256    ///
257    /// The user wishes to view the details of a specific session.
258    SessionView,
259
260    /// `org.matrix.session_end`
261    ///
262    /// The user wishes to end/log out of a specific session.
263    SessionEnd,
264
265    /// `org.matrix.account_deactivate`
266    ///
267    /// The user wishes to deactivate their account.
268    AccountDeactivate,
269
270    /// `org.matrix.cross_signing_reset`
271    ///
272    /// The user wishes to reset their cross-signing keys.
273    CrossSigningReset,
274
275    /// An unknown value.
276    Unknown(String),
277}
278
279impl core::fmt::Display for AccountManagementAction {
280    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
281        match self {
282            Self::Profile => write!(f, "org.matrix.profile"),
283            Self::SessionsList => write!(f, "org.matrix.sessions_list"),
284            Self::SessionView => write!(f, "org.matrix.session_view"),
285            Self::SessionEnd => write!(f, "org.matrix.session_end"),
286            Self::AccountDeactivate => write!(f, "org.matrix.account_deactivate"),
287            Self::CrossSigningReset => write!(f, "org.matrix.cross_signing_reset"),
288            Self::Unknown(value) => write!(f, "{value}"),
289        }
290    }
291}
292
293impl core::str::FromStr for AccountManagementAction {
294    type Err = core::convert::Infallible;
295
296    fn from_str(s: &str) -> Result<Self, Self::Err> {
297        match s {
298            "org.matrix.profile" => Ok(Self::Profile),
299            "org.matrix.sessions_list" => Ok(Self::SessionsList),
300            "org.matrix.session_view" => Ok(Self::SessionView),
301            "org.matrix.session_end" => Ok(Self::SessionEnd),
302            "org.matrix.account_deactivate" => Ok(Self::AccountDeactivate),
303            "org.matrix.cross_signing_reset" => Ok(Self::CrossSigningReset),
304            value => Ok(Self::Unknown(value.to_owned())),
305        }
306    }
307}
308
309/// The default value of `response_modes_supported` if it is not set.
310pub static DEFAULT_RESPONSE_MODES_SUPPORTED: &[ResponseMode] =
311    &[ResponseMode::Query, ResponseMode::Fragment];
312
313/// The default value of `grant_types_supported` if it is not set.
314pub static DEFAULT_GRANT_TYPES_SUPPORTED: &[GrantType] =
315    &[GrantType::AuthorizationCode, GrantType::Implicit];
316
317/// The default value of `token_endpoint_auth_methods_supported` if it is not
318/// set.
319pub static DEFAULT_AUTH_METHODS_SUPPORTED: &[OAuthClientAuthenticationMethod] =
320    &[OAuthClientAuthenticationMethod::ClientSecretBasic];
321
322/// The default value of `claim_types_supported` if it is not set.
323pub static DEFAULT_CLAIM_TYPES_SUPPORTED: &[ClaimType] = &[ClaimType::Normal];
324
325/// Authorization server metadata, as described by the [IANA registry].
326///
327/// All the fields with a default value are accessible via methods.
328///
329/// [IANA registry]: https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml#authorization-server-metadata
330#[skip_serializing_none]
331#[derive(Debug, Serialize, Deserialize, Clone, Default)]
332pub struct ProviderMetadata {
333    /// Authorization server's issuer identifier URL.
334    ///
335    /// This field is required. The URL must use a `https` scheme, and must not
336    /// contain a query or fragment. It must match the one used to build the
337    /// well-known URI to query this metadata.
338    pub issuer: Option<String>,
339
340    /// URL of the authorization server's [authorization endpoint].
341    ///
342    /// This field is required. The URL must use a `https` scheme, and must not
343    /// contain a fragment.
344    ///
345    /// [authorization endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.1
346    pub authorization_endpoint: Option<Url>,
347
348    /// URL of the authorization server's [token endpoint].
349    ///
350    /// This field is required. The URL must use a `https` scheme, and must not
351    /// contain a fragment.
352    ///
353    /// [token endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.2
354    pub token_endpoint: Option<Url>,
355
356    /// URL of the authorization server's [JWK] Set document.
357    ///
358    /// This field is required. The URL must use a `https` scheme.
359    ///
360    /// [JWK]: https://www.rfc-editor.org/rfc/rfc7517.html
361    pub jwks_uri: Option<Url>,
362
363    /// URL of the authorization server's [OAuth 2.0 Dynamic Client
364    /// Registration] endpoint.
365    ///
366    /// If this field is present, the URL must use a `https` scheme.
367    ///
368    /// [OAuth 2.0 Dynamic Client Registration]: https://www.rfc-editor.org/rfc/rfc7591
369    pub registration_endpoint: Option<Url>,
370
371    /// JSON array containing a list of the OAuth 2.0 `scope` values that this
372    /// authorization server supports.
373    ///
374    /// If this field is present, it must contain at least the `openid` scope
375    /// value.
376    pub scopes_supported: Option<Vec<String>>,
377
378    /// JSON array containing a list of the [OAuth 2.0 `response_type` values]
379    /// that this authorization server supports.
380    ///
381    /// This field is required.
382    ///
383    /// [OAuth 2.0 `response_type` values]: https://www.rfc-editor.org/rfc/rfc7591#page-9
384    pub response_types_supported: Option<Vec<ResponseType>>,
385
386    /// JSON array containing a list of the [OAuth 2.0 `response_mode` values]
387    /// that this authorization server supports.
388    ///
389    /// Defaults to [`DEFAULT_RESPONSE_MODES_SUPPORTED`].
390    ///
391    /// [OAuth 2.0 `response_mode` values]: https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html
392    pub response_modes_supported: Option<Vec<ResponseMode>>,
393
394    /// JSON array containing a list of the [OAuth 2.0 `grant_type` values] that
395    /// this authorization server supports.
396    ///
397    /// Defaults to [`DEFAULT_GRANT_TYPES_SUPPORTED`].
398    ///
399    /// [OAuth 2.0 `grant_type` values]: https://www.rfc-editor.org/rfc/rfc7591#page-9
400    pub grant_types_supported: Option<Vec<GrantType>>,
401
402    /// JSON array containing a list of client authentication methods supported
403    /// by this token endpoint.
404    ///
405    /// Defaults to [`DEFAULT_AUTH_METHODS_SUPPORTED`].
406    pub token_endpoint_auth_methods_supported: Option<Vec<OAuthClientAuthenticationMethod>>,
407
408    /// JSON array containing a list of the JWS signing algorithms supported
409    /// by the token endpoint for the signature on the JWT used to
410    /// authenticate the client at the token endpoint.
411    ///
412    /// If this field is present, it must not contain
413    /// [`JsonWebSignatureAlg::None`]. This field is required if
414    /// `token_endpoint_auth_methods_supported` contains
415    /// [`OAuthClientAuthenticationMethod::PrivateKeyJwt`] or
416    /// [`OAuthClientAuthenticationMethod::ClientSecretJwt`].
417    pub token_endpoint_auth_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
418
419    /// URL of a page containing human-readable information that developers
420    /// might want or need to know when using the authorization server.
421    pub service_documentation: Option<Url>,
422
423    /// Languages and scripts supported for the user interface, represented as a
424    /// JSON array of language tag values from BCP 47.
425    ///
426    /// If omitted, the set of supported languages and scripts is unspecified.
427    pub ui_locales_supported: Option<Vec<LanguageTag>>,
428
429    /// URL that the authorization server provides to the person registering the
430    /// client to read about the authorization server's requirements on how the
431    /// client can use the data provided by the authorization server.
432    pub op_policy_uri: Option<Url>,
433
434    /// URL that the authorization server provides to the person registering the
435    /// client to read about the authorization server's terms of service.
436    pub op_tos_uri: Option<Url>,
437
438    /// URL of the authorization server's [OAuth 2.0 revocation endpoint].
439    ///
440    /// If this field is present, the URL must use a `https` scheme, and must
441    /// not contain a fragment.
442    ///
443    /// [OAuth 2.0 revocation endpoint]: https://www.rfc-editor.org/rfc/rfc7009
444    pub revocation_endpoint: Option<Url>,
445
446    /// JSON array containing a list of client authentication methods supported
447    /// by this revocation endpoint.
448    ///
449    /// Defaults to [`DEFAULT_AUTH_METHODS_SUPPORTED`].
450    pub revocation_endpoint_auth_methods_supported: Option<Vec<OAuthClientAuthenticationMethod>>,
451
452    /// JSON array containing a list of the JWS signing algorithms supported by
453    /// the revocation endpoint for the signature on the JWT used to
454    /// authenticate the client at the revocation endpoint.
455    ///
456    /// If this field is present, it must not contain
457    /// [`JsonWebSignatureAlg::None`]. This field is required if
458    /// `revocation_endpoint_auth_methods_supported` contains
459    /// [`OAuthClientAuthenticationMethod::PrivateKeyJwt`] or
460    /// [`OAuthClientAuthenticationMethod::ClientSecretJwt`].
461    pub revocation_endpoint_auth_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
462
463    /// URL of the authorization server's [OAuth 2.0 introspection endpoint].
464    ///
465    /// If this field is present, the URL must use a `https` scheme.
466    ///
467    /// [OAuth 2.0 introspection endpoint]: https://www.rfc-editor.org/rfc/rfc7662
468    pub introspection_endpoint: Option<Url>,
469
470    /// JSON array containing a list of client authentication methods or token
471    /// types supported by this introspection endpoint.
472    pub introspection_endpoint_auth_methods_supported:
473        Option<Vec<AuthenticationMethodOrAccessTokenType>>,
474
475    /// JSON array containing a list of the JWS signing algorithms supported by
476    /// the introspection endpoint for the signature on the JWT used to
477    /// authenticate the client at the introspection endpoint.
478    ///
479    /// If this field is present, it must not contain
480    /// [`JsonWebSignatureAlg::None`]. This field is required if
481    /// `intospection_endpoint_auth_methods_supported` contains
482    /// [`OAuthClientAuthenticationMethod::PrivateKeyJwt`] or
483    /// [`OAuthClientAuthenticationMethod::ClientSecretJwt`].
484    pub introspection_endpoint_auth_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
485
486    /// [PKCE code challenge methods] supported by this authorization server.
487    /// If omitted, the authorization server does not support PKCE.
488    ///
489    /// [PKCE code challenge]: https://www.rfc-editor.org/rfc/rfc7636
490    pub code_challenge_methods_supported: Option<Vec<PkceCodeChallengeMethod>>,
491
492    /// URL of the OP's [UserInfo Endpoint].
493    ///
494    /// [UserInfo Endpoint]: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
495    pub userinfo_endpoint: Option<Url>,
496
497    /// JSON array containing a list of the Authentication Context Class
498    /// References that this OP supports.
499    pub acr_values_supported: Option<Vec<String>>,
500
501    /// JSON array containing a list of the Subject Identifier types that this
502    /// OP supports.
503    ///
504    /// This field is required.
505    pub subject_types_supported: Option<Vec<SubjectType>>,
506
507    /// JSON array containing a list of the JWS signing algorithms (`alg`
508    /// values) supported by the OP for the ID Token.
509    ///
510    /// This field is required.
511    pub id_token_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
512
513    /// JSON array containing a list of the JWE encryption algorithms (`alg`
514    /// values) supported by the OP for the ID Token.
515    pub id_token_encryption_alg_values_supported: Option<Vec<JsonWebEncryptionAlg>>,
516
517    /// JSON array containing a list of the JWE encryption algorithms (`enc`
518    /// values) supported by the OP for the ID Token.
519    pub id_token_encryption_enc_values_supported: Option<Vec<JsonWebEncryptionEnc>>,
520
521    /// JSON array containing a list of the JWS signing algorithms (`alg`
522    /// values) supported by the UserInfo Endpoint.
523    pub userinfo_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
524
525    /// JSON array containing a list of the JWE encryption algorithms (`alg`
526    /// values) supported by the UserInfo Endpoint.
527    pub userinfo_encryption_alg_values_supported: Option<Vec<JsonWebEncryptionAlg>>,
528
529    /// JSON array containing a list of the JWE encryption algorithms (`enc`
530    /// values) supported by the UserInfo Endpoint.
531    pub userinfo_encryption_enc_values_supported: Option<Vec<JsonWebEncryptionEnc>>,
532
533    /// JSON array containing a list of the JWS signing algorithms (`alg`
534    /// values) supported by the OP for Request Objects.
535    pub request_object_signing_alg_values_supported: Option<Vec<JsonWebSignatureAlg>>,
536
537    /// JSON array containing a list of the JWE encryption algorithms (`alg`
538    /// values) supported by the OP for Request Objects.
539    pub request_object_encryption_alg_values_supported: Option<Vec<JsonWebEncryptionAlg>>,
540
541    /// JSON array containing a list of the JWE encryption algorithms (`enc`
542    /// values) supported by the OP for Request Objects.
543    pub request_object_encryption_enc_values_supported: Option<Vec<JsonWebEncryptionEnc>>,
544
545    /// JSON array containing a list of the "display" parameter values that the
546    /// OpenID Provider supports.
547    pub display_values_supported: Option<Vec<Display>>,
548
549    /// JSON array containing a list of the Claim Types that the OpenID Provider
550    /// supports.
551    ///
552    /// Defaults to [`DEFAULT_CLAIM_TYPES_SUPPORTED`].
553    pub claim_types_supported: Option<Vec<ClaimType>>,
554
555    /// JSON array containing a list of the Claim Names of the Claims that the
556    /// OpenID Provider MAY be able to supply values for.
557    pub claims_supported: Option<Vec<String>>,
558
559    /// Languages and scripts supported for values in Claims being returned,
560    /// represented as a JSON array of BCP 47 language tag values.
561    pub claims_locales_supported: Option<Vec<LanguageTag>>,
562
563    /// Boolean value specifying whether the OP supports use of the `claims`
564    /// parameter.
565    ///
566    /// Defaults to `false`.
567    pub claims_parameter_supported: Option<bool>,
568
569    /// Boolean value specifying whether the OP supports use of the `request`
570    /// parameter.
571    ///
572    /// Defaults to `false`.
573    pub request_parameter_supported: Option<bool>,
574
575    /// Boolean value specifying whether the OP supports use of the
576    /// `request_uri` parameter.
577    ///
578    /// Defaults to `true`.
579    pub request_uri_parameter_supported: Option<bool>,
580
581    /// Boolean value specifying whether the OP requires any `request_uri`
582    /// values used to be pre-registered.
583    ///
584    /// Defaults to `false`.
585    pub require_request_uri_registration: Option<bool>,
586
587    /// Indicates where authorization request needs to be protected as [Request
588    /// Object] and provided through either request or request_uri parameter.
589    ///
590    /// Defaults to `false`.
591    ///
592    /// [Request Object]: https://www.rfc-editor.org/rfc/rfc9101.html
593    pub require_signed_request_object: Option<bool>,
594
595    /// URL of the authorization server's [pushed authorization request
596    /// endpoint].
597    ///
598    /// [pushed authorization request endpoint]: https://www.rfc-editor.org/rfc/rfc9126.html
599    pub pushed_authorization_request_endpoint: Option<Url>,
600
601    /// Indicates whether the authorization server accepts authorization
602    /// requests only via PAR.
603    ///
604    /// Defaults to `false`.
605    pub require_pushed_authorization_requests: Option<bool>,
606
607    /// Array containing the list of prompt values that this OP supports.
608    ///
609    /// This field can be used to detect if the OP supports the [prompt
610    /// `create`] value.
611    ///
612    /// [prompt `create`]: https://openid.net/specs/openid-connect-prompt-create-1_0.html
613    pub prompt_values_supported: Option<Vec<Prompt>>,
614
615    /// URL of the authorization server's [device authorization endpoint].
616    ///
617    /// [device authorization endpoint]: https://www.rfc-editor.org/rfc/rfc8628
618    pub device_authorization_endpoint: Option<Url>,
619
620    /// URL of the authorization server's [RP-Initiated Logout endpoint].
621    ///
622    /// [RP-Initiated Logout endpoint]: https://openid.net/specs/openid-connect-rpinitiated-1_0.html
623    pub end_session_endpoint: Option<Url>,
624
625    /// URL where the user is able to access the account management capabilities
626    /// of this OP.
627    ///
628    /// This is a Matrix extension introduced in [MSC2965](https://github.com/matrix-org/matrix-spec-proposals/pull/2965).
629    pub account_management_uri: Option<Url>,
630
631    /// Array of actions that the account management URL supports.
632    ///
633    /// This is a Matrix extension introduced in [MSC2965](https://github.com/matrix-org/matrix-spec-proposals/pull/2965).
634    pub account_management_actions_supported: Option<Vec<AccountManagementAction>>,
635}
636
637impl ProviderMetadata {
638    /// Validate this `ProviderMetadata` according to the [OpenID Connect
639    /// Discovery Spec 1.0].
640    ///
641    /// # Parameters
642    ///
643    /// - `issuer`: The issuer that was discovered to get this
644    ///   `ProviderMetadata`.
645    ///
646    /// # Errors
647    ///
648    /// Will return `Err` if validation fails.
649    ///
650    /// [OpenID Connect Discovery Spec 1.0]: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
651    pub fn validate(
652        self,
653        issuer: &str,
654    ) -> Result<VerifiedProviderMetadata, ProviderMetadataVerificationError> {
655        let metadata = self.insecure_verify_metadata()?;
656
657        if metadata.issuer() != issuer {
658            return Err(ProviderMetadataVerificationError::IssuerUrlsDontMatch);
659        }
660
661        validate_url(
662            "issuer",
663            &metadata
664                .issuer()
665                .parse()
666                .map_err(|_| ProviderMetadataVerificationError::IssuerNotUrl)?,
667            ExtraUrlRestrictions::NoQueryOrFragment,
668        )?;
669
670        validate_url(
671            "authorization_endpoint",
672            metadata.authorization_endpoint(),
673            ExtraUrlRestrictions::NoFragment,
674        )?;
675
676        validate_url(
677            "token_endpoint",
678            metadata.token_endpoint(),
679            ExtraUrlRestrictions::NoFragment,
680        )?;
681
682        validate_url("jwks_uri", metadata.jwks_uri(), ExtraUrlRestrictions::None)?;
683
684        if let Some(url) = &metadata.registration_endpoint {
685            validate_url("registration_endpoint", url, ExtraUrlRestrictions::None)?;
686        }
687
688        if let Some(scopes) = &metadata.scopes_supported {
689            if !scopes.iter().any(|s| s == "openid") {
690                return Err(ProviderMetadataVerificationError::ScopesMissingOpenid);
691            }
692        }
693
694        validate_signing_alg_values_supported(
695            "token_endpoint",
696            metadata
697                .token_endpoint_auth_signing_alg_values_supported
698                .iter()
699                .flatten(),
700            metadata
701                .token_endpoint_auth_methods_supported
702                .iter()
703                .flatten(),
704        )?;
705
706        if let Some(url) = &metadata.revocation_endpoint {
707            validate_url("revocation_endpoint", url, ExtraUrlRestrictions::NoFragment)?;
708        }
709
710        validate_signing_alg_values_supported(
711            "revocation_endpoint",
712            metadata
713                .revocation_endpoint_auth_signing_alg_values_supported
714                .iter()
715                .flatten(),
716            metadata
717                .revocation_endpoint_auth_methods_supported
718                .iter()
719                .flatten(),
720        )?;
721
722        if let Some(url) = &metadata.introspection_endpoint {
723            validate_url("introspection_endpoint", url, ExtraUrlRestrictions::None)?;
724        }
725
726        // The list can also contain token types so remove them as we don't need to
727        // check them.
728        let introspection_methods = metadata
729            .introspection_endpoint_auth_methods_supported
730            .as_ref()
731            .map(|v| {
732                v.iter()
733                    .filter_map(AuthenticationMethodOrAccessTokenType::authentication_method)
734                    .collect::<Vec<_>>()
735            });
736        validate_signing_alg_values_supported(
737            "introspection_endpoint",
738            metadata
739                .introspection_endpoint_auth_signing_alg_values_supported
740                .iter()
741                .flatten(),
742            introspection_methods.into_iter().flatten(),
743        )?;
744
745        if let Some(url) = &metadata.userinfo_endpoint {
746            validate_url("userinfo_endpoint", url, ExtraUrlRestrictions::None)?;
747        }
748
749        if let Some(url) = &metadata.pushed_authorization_request_endpoint {
750            validate_url(
751                "pushed_authorization_request_endpoint",
752                url,
753                ExtraUrlRestrictions::None,
754            )?;
755        }
756
757        if let Some(url) = &metadata.end_session_endpoint {
758            validate_url("end_session_endpoint", url, ExtraUrlRestrictions::None)?;
759        }
760
761        Ok(metadata)
762    }
763
764    /// Verify this `ProviderMetadata`.
765    ///
766    /// Contrary to [`ProviderMetadata::validate()`], it only checks that the
767    /// required fields are present.
768    ///
769    /// This can be used during development to test against a local OpenID
770    /// Provider, for example.
771    ///
772    /// # Parameters
773    ///
774    /// - `issuer`: The issuer that was discovered to get this
775    ///   `ProviderMetadata`.
776    ///
777    /// # Errors
778    ///
779    /// Will return `Err` if a required field is missing.
780    ///
781    /// # Warning
782    ///
783    /// It is not recommended to use this method in production as it doesn't
784    /// ensure that the issuer implements the proper security practices.
785    pub fn insecure_verify_metadata(
786        self,
787    ) -> Result<VerifiedProviderMetadata, ProviderMetadataVerificationError> {
788        self.issuer
789            .as_ref()
790            .ok_or(ProviderMetadataVerificationError::MissingIssuer)?;
791
792        self.authorization_endpoint
793            .as_ref()
794            .ok_or(ProviderMetadataVerificationError::MissingAuthorizationEndpoint)?;
795
796        self.token_endpoint
797            .as_ref()
798            .ok_or(ProviderMetadataVerificationError::MissingTokenEndpoint)?;
799
800        self.jwks_uri
801            .as_ref()
802            .ok_or(ProviderMetadataVerificationError::MissingJwksUri)?;
803
804        self.response_types_supported
805            .as_ref()
806            .ok_or(ProviderMetadataVerificationError::MissingResponseTypesSupported)?;
807
808        self.subject_types_supported
809            .as_ref()
810            .ok_or(ProviderMetadataVerificationError::MissingSubjectTypesSupported)?;
811
812        self.id_token_signing_alg_values_supported
813            .as_ref()
814            .ok_or(ProviderMetadataVerificationError::MissingIdTokenSigningAlgValuesSupported)?;
815
816        Ok(VerifiedProviderMetadata { inner: self })
817    }
818
819    /// JSON array containing a list of the OAuth 2.0 `response_mode` values
820    /// that this authorization server supports.
821    ///
822    /// Defaults to [`DEFAULT_RESPONSE_MODES_SUPPORTED`].
823    #[must_use]
824    pub fn response_modes_supported(&self) -> &[ResponseMode] {
825        self.response_modes_supported
826            .as_deref()
827            .unwrap_or(DEFAULT_RESPONSE_MODES_SUPPORTED)
828    }
829
830    /// JSON array containing a list of the OAuth 2.0 grant type values that
831    /// this authorization server supports.
832    ///
833    /// Defaults to [`DEFAULT_GRANT_TYPES_SUPPORTED`].
834    #[must_use]
835    pub fn grant_types_supported(&self) -> &[GrantType] {
836        self.grant_types_supported
837            .as_deref()
838            .unwrap_or(DEFAULT_GRANT_TYPES_SUPPORTED)
839    }
840
841    /// JSON array containing a list of client authentication methods supported
842    /// by the token endpoint.
843    ///
844    /// Defaults to [`DEFAULT_AUTH_METHODS_SUPPORTED`].
845    #[must_use]
846    pub fn token_endpoint_auth_methods_supported(&self) -> &[OAuthClientAuthenticationMethod] {
847        self.token_endpoint_auth_methods_supported
848            .as_deref()
849            .unwrap_or(DEFAULT_AUTH_METHODS_SUPPORTED)
850    }
851
852    /// JSON array containing a list of client authentication methods supported
853    /// by the revocation endpoint.
854    ///
855    /// Defaults to [`DEFAULT_AUTH_METHODS_SUPPORTED`].
856    #[must_use]
857    pub fn revocation_endpoint_auth_methods_supported(&self) -> &[OAuthClientAuthenticationMethod] {
858        self.revocation_endpoint_auth_methods_supported
859            .as_deref()
860            .unwrap_or(DEFAULT_AUTH_METHODS_SUPPORTED)
861    }
862
863    /// JSON array containing a list of the Claim Types that the OpenID Provider
864    /// supports.
865    ///
866    /// Defaults to [`DEFAULT_CLAIM_TYPES_SUPPORTED`].
867    #[must_use]
868    pub fn claim_types_supported(&self) -> &[ClaimType] {
869        self.claim_types_supported
870            .as_deref()
871            .unwrap_or(DEFAULT_CLAIM_TYPES_SUPPORTED)
872    }
873
874    /// Boolean value specifying whether the OP supports use of the `claims`
875    /// parameter.
876    ///
877    /// Defaults to `false`.
878    #[must_use]
879    pub fn claims_parameter_supported(&self) -> bool {
880        self.claims_parameter_supported.unwrap_or(false)
881    }
882
883    /// Boolean value specifying whether the OP supports use of the `request`
884    /// parameter.
885    ///
886    /// Defaults to `false`.
887    #[must_use]
888    pub fn request_parameter_supported(&self) -> bool {
889        self.request_parameter_supported.unwrap_or(false)
890    }
891
892    /// Boolean value specifying whether the OP supports use of the
893    /// `request_uri` parameter.
894    ///
895    /// Defaults to `true`.
896    #[must_use]
897    pub fn request_uri_parameter_supported(&self) -> bool {
898        self.request_uri_parameter_supported.unwrap_or(true)
899    }
900
901    /// Boolean value specifying whether the OP requires any `request_uri`
902    /// values used to be pre-registered.
903    ///
904    /// Defaults to `false`.
905    #[must_use]
906    pub fn require_request_uri_registration(&self) -> bool {
907        self.require_request_uri_registration.unwrap_or(false)
908    }
909
910    /// Indicates where authorization request needs to be protected as Request
911    /// Object and provided through either `request` or `request_uri` parameter.
912    ///
913    /// Defaults to `false`.
914    #[must_use]
915    pub fn require_signed_request_object(&self) -> bool {
916        self.require_signed_request_object.unwrap_or(false)
917    }
918
919    /// Indicates whether the authorization server accepts authorization
920    /// requests only via PAR.
921    ///
922    /// Defaults to `false`.
923    #[must_use]
924    pub fn require_pushed_authorization_requests(&self) -> bool {
925        self.require_pushed_authorization_requests.unwrap_or(false)
926    }
927}
928
929/// The verified authorization server metadata.
930///
931/// All the fields required by the [OpenID Connect Discovery Spec 1.0] or with
932/// a default value are accessible via methods.
933///
934/// To access other fields, use this type's `Deref` implementation.
935///
936/// # Example
937///
938/// ```no_run
939/// use oauth2_types::{
940///     oidc::VerifiedProviderMetadata,
941///     requests::GrantType,
942/// };
943/// use url::Url;
944/// # use oauth2_types::oidc::{ProviderMetadata, ProviderMetadataVerificationError};
945/// # let metadata = ProviderMetadata::default();
946/// # let issuer = "http://localhost/";
947/// let verified_metadata = metadata.validate(&issuer)?;
948///
949/// // The endpoint is required during validation so this is not an `Option`.
950/// let _: &Url = verified_metadata.authorization_endpoint();
951///
952/// // The field has a default value so this is not an `Option`.
953/// let _: &[GrantType] = verified_metadata.grant_types_supported();
954///
955/// // Other fields can be accessed via `Deref`.
956/// if let Some(registration_endpoint) = &verified_metadata.registration_endpoint {
957///     println!("Registration is supported at {registration_endpoint}");
958/// }
959/// # Ok::<(), ProviderMetadataVerificationError>(())
960/// ```
961///
962/// [OpenID Connect Discovery Spec 1.0]: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
963#[derive(Debug, Clone)]
964pub struct VerifiedProviderMetadata {
965    inner: ProviderMetadata,
966}
967
968impl VerifiedProviderMetadata {
969    /// Authorization server's issuer identifier URL.
970    #[must_use]
971    pub fn issuer(&self) -> &str {
972        match &self.issuer {
973            Some(u) => u,
974            None => unreachable!(),
975        }
976    }
977
978    /// URL of the authorization server's authorization endpoint.
979    #[must_use]
980    pub fn authorization_endpoint(&self) -> &Url {
981        match &self.authorization_endpoint {
982            Some(u) => u,
983            None => unreachable!(),
984        }
985    }
986
987    /// URL of the authorization server's token endpoint.
988    #[must_use]
989    pub fn token_endpoint(&self) -> &Url {
990        match &self.token_endpoint {
991            Some(u) => u,
992            None => unreachable!(),
993        }
994    }
995
996    /// URL of the authorization server's JWK Set document.
997    #[must_use]
998    pub fn jwks_uri(&self) -> &Url {
999        match &self.jwks_uri {
1000            Some(u) => u,
1001            None => unreachable!(),
1002        }
1003    }
1004
1005    /// JSON array containing a list of the OAuth 2.0 `response_type` values
1006    /// that this authorization server supports.
1007    #[must_use]
1008    pub fn response_types_supported(&self) -> &[ResponseType] {
1009        match &self.response_types_supported {
1010            Some(u) => u,
1011            None => unreachable!(),
1012        }
1013    }
1014
1015    /// JSON array containing a list of the Subject Identifier types that this
1016    /// OP supports.
1017    #[must_use]
1018    pub fn subject_types_supported(&self) -> &[SubjectType] {
1019        match &self.subject_types_supported {
1020            Some(u) => u,
1021            None => unreachable!(),
1022        }
1023    }
1024
1025    /// JSON array containing a list of the JWS `alg` values supported by the OP
1026    /// for the ID Token.
1027    #[must_use]
1028    pub fn id_token_signing_alg_values_supported(&self) -> &[JsonWebSignatureAlg] {
1029        match &self.id_token_signing_alg_values_supported {
1030            Some(u) => u,
1031            None => unreachable!(),
1032        }
1033    }
1034}
1035
1036impl Deref for VerifiedProviderMetadata {
1037    type Target = ProviderMetadata;
1038
1039    fn deref(&self) -> &Self::Target {
1040        &self.inner
1041    }
1042}
1043
1044/// All errors that can happen when verifying [`ProviderMetadata`]
1045#[derive(Debug, Error)]
1046pub enum ProviderMetadataVerificationError {
1047    /// The issuer is missing.
1048    #[error("issuer is missing")]
1049    MissingIssuer,
1050
1051    /// The issuer is not a valid URL.
1052    #[error("issuer is not a valid URL")]
1053    IssuerNotUrl,
1054
1055    /// The authorization endpoint is missing.
1056    #[error("authorization endpoint is missing")]
1057    MissingAuthorizationEndpoint,
1058
1059    /// The token endpoint is missing.
1060    #[error("token endpoint is missing")]
1061    MissingTokenEndpoint,
1062
1063    /// The JWK Set URI is missing.
1064    #[error("JWK Set URI is missing")]
1065    MissingJwksUri,
1066
1067    /// The supported response types are missing.
1068    #[error("supported response types are missing")]
1069    MissingResponseTypesSupported,
1070
1071    /// The supported subject types are missing.
1072    #[error("supported subject types are missing")]
1073    MissingSubjectTypesSupported,
1074
1075    /// The supported ID token signing algorithm values are missing.
1076    #[error("supported ID token signing algorithm values are missing")]
1077    MissingIdTokenSigningAlgValuesSupported,
1078
1079    /// The URL of the given field doesn't use a `https` scheme.
1080    #[error("{0}'s URL doesn't use a https scheme: {1}")]
1081    UrlNonHttpsScheme(&'static str, Url),
1082
1083    /// The URL of the given field contains a query, but it's not allowed.
1084    #[error("{0}'s URL contains a query: {1}")]
1085    UrlWithQuery(&'static str, Url),
1086
1087    /// The URL of the given field contains a fragment, but it's not allowed.
1088    #[error("{0}'s URL contains a fragment: {1}")]
1089    UrlWithFragment(&'static str, Url),
1090
1091    /// The issuer URL doesn't match the one that was discovered.
1092    #[error("issuer URLs don't match")]
1093    IssuerUrlsDontMatch,
1094
1095    /// `openid` is missing from the supported scopes.
1096    #[error("missing openid scope")]
1097    ScopesMissingOpenid,
1098
1099    /// `code` is missing from the supported response types.
1100    #[error("missing `code` response type")]
1101    ResponseTypesMissingCode,
1102
1103    /// `id_token` is missing from the supported response types.
1104    #[error("missing `id_token` response type")]
1105    ResponseTypesMissingIdToken,
1106
1107    /// `id_token token` is missing from the supported response types.
1108    #[error("missing `id_token token` response type")]
1109    ResponseTypesMissingIdTokenToken,
1110
1111    /// `authorization_code` is missing from the supported grant types.
1112    #[error("missing `authorization_code` grant type")]
1113    GrantTypesMissingAuthorizationCode,
1114
1115    /// `implicit` is missing from the supported grant types.
1116    #[error("missing `implicit` grant type")]
1117    GrantTypesMissingImplicit,
1118
1119    /// The given endpoint is missing auth signing algorithm values, but they
1120    /// are required because it supports at least one of the `client_secret_jwt`
1121    /// or `private_key_jwt` authentication methods.
1122    #[error("{0} missing auth signing algorithm values")]
1123    MissingAuthSigningAlgValues(&'static str),
1124
1125    /// `none` is in the given endpoint's signing algorithm values, but is not
1126    /// allowed.
1127    #[error("{0} signing algorithm values contain `none`")]
1128    SigningAlgValuesWithNone(&'static str),
1129}
1130
1131/// Possible extra restrictions on a URL.
1132#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1133enum ExtraUrlRestrictions {
1134    /// No extra restrictions.
1135    None,
1136
1137    /// The URL must not contain a fragment.
1138    NoFragment,
1139
1140    /// The URL must not contain a query or a fragment.
1141    NoQueryOrFragment,
1142}
1143
1144impl ExtraUrlRestrictions {
1145    fn can_have_fragment(self) -> bool {
1146        self == Self::None
1147    }
1148
1149    fn can_have_query(self) -> bool {
1150        self != Self::NoQueryOrFragment
1151    }
1152}
1153
1154/// Validate the URL of the field with the given extra restrictions.
1155///
1156/// The basic restriction is that the URL must use the `https` scheme.
1157fn validate_url(
1158    field: &'static str,
1159    url: &Url,
1160    restrictions: ExtraUrlRestrictions,
1161) -> Result<(), ProviderMetadataVerificationError> {
1162    if url.scheme() != "https" {
1163        return Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(
1164            field,
1165            url.clone(),
1166        ));
1167    }
1168
1169    if !restrictions.can_have_query() && url.query().is_some() {
1170        return Err(ProviderMetadataVerificationError::UrlWithQuery(
1171            field,
1172            url.clone(),
1173        ));
1174    }
1175
1176    if !restrictions.can_have_fragment() && url.fragment().is_some() {
1177        return Err(ProviderMetadataVerificationError::UrlWithFragment(
1178            field,
1179            url.clone(),
1180        ));
1181    }
1182
1183    Ok(())
1184}
1185
1186/// Validate the algorithm values of the endpoint according to the
1187/// authentication methods.
1188///
1189/// The restrictions are:
1190/// - The algorithm values must not contain `none`,
1191/// - If the `client_secret_jwt` or `private_key_jwt` authentication methods are
1192///   supported, the values must be present.
1193fn validate_signing_alg_values_supported<'a>(
1194    endpoint: &'static str,
1195    values: impl Iterator<Item = &'a JsonWebSignatureAlg>,
1196    mut methods: impl Iterator<Item = &'a OAuthClientAuthenticationMethod>,
1197) -> Result<(), ProviderMetadataVerificationError> {
1198    let mut no_values = true;
1199
1200    for value in values {
1201        if *value == JsonWebSignatureAlg::None {
1202            return Err(ProviderMetadataVerificationError::SigningAlgValuesWithNone(
1203                endpoint,
1204            ));
1205        }
1206
1207        no_values = false;
1208    }
1209
1210    if no_values
1211        && methods.any(|method| {
1212            matches!(
1213                method,
1214                OAuthClientAuthenticationMethod::ClientSecretJwt
1215                    | OAuthClientAuthenticationMethod::PrivateKeyJwt
1216            )
1217        })
1218    {
1219        return Err(ProviderMetadataVerificationError::MissingAuthSigningAlgValues(endpoint));
1220    }
1221
1222    Ok(())
1223}
1224
1225/// The body of a request to the [RP-Initiated Logout Endpoint].
1226///
1227/// [RP-Initiated Logout Endpoint]: https://openid.net/specs/openid-connect-rpinitiated-1_0.html
1228#[skip_serializing_none]
1229#[serde_as]
1230#[derive(Default, Serialize, Deserialize, Clone)]
1231pub struct RpInitiatedLogoutRequest {
1232    /// ID Token previously issued by the OP to the RP.
1233    ///
1234    /// Recommended, used as a hint about the End-User's current authenticated
1235    /// session with the Client.
1236    pub id_token_hint: Option<String>,
1237
1238    /// Hint to the Authorization Server about the End-User that is logging out.
1239    ///
1240    /// The value and meaning of this parameter is left up to the OP's
1241    /// discretion. For instance, the value might contain an email address,
1242    /// phone number, username, or session identifier pertaining to the RP's
1243    /// session with the OP for the End-User.
1244    pub logout_hint: Option<String>,
1245
1246    /// OAuth 2.0 Client Identifier valid at the Authorization Server.
1247    ///
1248    /// The most common use case for this parameter is to specify the Client
1249    /// Identifier when `post_logout_redirect_uri` is used but `id_token_hint`
1250    /// is not. Another use is for symmetrically encrypted ID Tokens used as
1251    /// `id_token_hint` values that require the Client Identifier to be
1252    /// specified by other means, so that the ID Tokens can be decrypted by
1253    /// the OP.
1254    pub client_id: Option<String>,
1255
1256    /// URI to which the RP is requesting that the End-User's User Agent be
1257    /// redirected after a logout has been performed.
1258    ///
1259    /// The value MUST have been previously registered with the OP, using the
1260    /// `post_logout_redirect_uris` registration parameter.
1261    pub post_logout_redirect_uri: Option<Url>,
1262
1263    /// Opaque value used by the RP to maintain state between the logout request
1264    /// and the callback to the endpoint specified by the
1265    /// `post_logout_redirect_uri` parameter.
1266    pub state: Option<String>,
1267
1268    /// End-User's preferred languages and scripts for the user interface,
1269    /// ordered by preference.
1270    #[serde_as(as = "Option<StringWithSeparator::<SpaceSeparator, LanguageTag>>")]
1271    #[serde(default)]
1272    pub ui_locales: Option<Vec<LanguageTag>>,
1273}
1274
1275impl fmt::Debug for RpInitiatedLogoutRequest {
1276    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1277        f.debug_struct("RpInitiatedLogoutRequest")
1278            .field("logout_hint", &self.logout_hint)
1279            .field("post_logout_redirect_uri", &self.post_logout_redirect_uri)
1280            .field("ui_locales", &self.ui_locales)
1281            .finish_non_exhaustive()
1282    }
1283}
1284
1285#[cfg(test)]
1286mod tests {
1287    use assert_matches::assert_matches;
1288    use mas_iana::{
1289        jose::JsonWebSignatureAlg,
1290        oauth::{OAuthAuthorizationEndpointResponseType, OAuthClientAuthenticationMethod},
1291    };
1292    use url::Url;
1293
1294    use super::*;
1295
1296    fn valid_provider_metadata() -> (ProviderMetadata, String) {
1297        let issuer = "https://localhost".to_owned();
1298        let metadata = ProviderMetadata {
1299            issuer: Some(issuer.clone()),
1300            authorization_endpoint: Some(Url::parse("https://localhost/auth").unwrap()),
1301            token_endpoint: Some(Url::parse("https://localhost/token").unwrap()),
1302            jwks_uri: Some(Url::parse("https://localhost/jwks").unwrap()),
1303            response_types_supported: Some(vec![
1304                OAuthAuthorizationEndpointResponseType::Code.into()
1305            ]),
1306            subject_types_supported: Some(vec![SubjectType::Public]),
1307            id_token_signing_alg_values_supported: Some(vec![JsonWebSignatureAlg::Rs256]),
1308            ..Default::default()
1309        };
1310
1311        (metadata, issuer)
1312    }
1313
1314    #[test]
1315    fn validate_required_metadata() {
1316        let (metadata, issuer) = valid_provider_metadata();
1317        metadata.validate(&issuer).unwrap();
1318    }
1319
1320    #[test]
1321    fn validate_issuer() {
1322        let (mut metadata, issuer) = valid_provider_metadata();
1323
1324        // Err - Missing
1325        metadata.issuer = None;
1326        assert_matches!(
1327            metadata.clone().validate(&issuer),
1328            Err(ProviderMetadataVerificationError::MissingIssuer)
1329        );
1330
1331        // Err - Not an url
1332        metadata.issuer = Some("not-an-url".to_owned());
1333        assert_matches!(
1334            metadata.clone().validate("not-an-url"),
1335            Err(ProviderMetadataVerificationError::IssuerNotUrl)
1336        );
1337
1338        // Err - Wrong issuer
1339        metadata.issuer = Some("https://example.com/".to_owned());
1340        assert_matches!(
1341            metadata.clone().validate(&issuer),
1342            Err(ProviderMetadataVerificationError::IssuerUrlsDontMatch)
1343        );
1344
1345        // Err - Not https
1346        let issuer = "http://localhost/".to_owned();
1347        metadata.issuer = Some(issuer.clone());
1348        let (field, url) = assert_matches!(
1349            metadata.clone().validate(&issuer),
1350            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1351        );
1352        assert_eq!(field, "issuer");
1353        assert_eq!(url.as_str(), issuer);
1354
1355        // Err - Query
1356        let issuer = "https://localhost/?query".to_owned();
1357        metadata.issuer = Some(issuer.clone());
1358        let (field, url) = assert_matches!(
1359            metadata.clone().validate(&issuer),
1360            Err(ProviderMetadataVerificationError::UrlWithQuery(field, url)) => (field, url)
1361        );
1362        assert_eq!(field, "issuer");
1363        assert_eq!(url.as_str(), issuer);
1364
1365        // Err - Fragment
1366        let issuer = "https://localhost/#fragment".to_owned();
1367        metadata.issuer = Some(issuer.clone());
1368        let (field, url) = assert_matches!(
1369            metadata.clone().validate(&issuer),
1370            Err(ProviderMetadataVerificationError::UrlWithFragment(field, url)) => (field, url)
1371        );
1372        assert_eq!(field, "issuer");
1373        assert_eq!(url.as_str(), issuer);
1374
1375        // Ok - Path
1376        let issuer = "https://localhost/issuer1".to_owned();
1377        metadata.issuer = Some(issuer.clone());
1378        metadata.validate(&issuer).unwrap();
1379    }
1380
1381    #[test]
1382    fn validate_authorization_endpoint() {
1383        let (mut metadata, issuer) = valid_provider_metadata();
1384
1385        // Err - Missing
1386        metadata.authorization_endpoint = None;
1387        assert_matches!(
1388            metadata.clone().validate(&issuer),
1389            Err(ProviderMetadataVerificationError::MissingAuthorizationEndpoint)
1390        );
1391
1392        // Err - Not https
1393        let endpoint = Url::parse("http://localhost/auth").unwrap();
1394        metadata.authorization_endpoint = Some(endpoint.clone());
1395        let (field, url) = assert_matches!(
1396            metadata.clone().validate(&issuer),
1397            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1398        );
1399        assert_eq!(field, "authorization_endpoint");
1400        assert_eq!(url, endpoint);
1401
1402        // Err - Fragment
1403        let endpoint = Url::parse("https://localhost/auth#fragment").unwrap();
1404        metadata.authorization_endpoint = Some(endpoint.clone());
1405        let (field, url) = assert_matches!(
1406            metadata.clone().validate(&issuer),
1407            Err(ProviderMetadataVerificationError::UrlWithFragment(field, url)) => (field, url)
1408        );
1409        assert_eq!(field, "authorization_endpoint");
1410        assert_eq!(url, endpoint);
1411
1412        // Ok - Query
1413        metadata.authorization_endpoint = Some(Url::parse("https://localhost/auth?query").unwrap());
1414        metadata.validate(&issuer).unwrap();
1415    }
1416
1417    #[test]
1418    fn validate_token_endpoint() {
1419        let (mut metadata, issuer) = valid_provider_metadata();
1420
1421        // Err - Missing
1422        metadata.token_endpoint = None;
1423        assert_matches!(
1424            metadata.clone().validate(&issuer),
1425            Err(ProviderMetadataVerificationError::MissingTokenEndpoint)
1426        );
1427
1428        // Err - Not https
1429        let endpoint = Url::parse("http://localhost/token").unwrap();
1430        metadata.token_endpoint = Some(endpoint.clone());
1431        let (field, url) = assert_matches!(
1432            metadata.clone().validate(&issuer),
1433            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1434        );
1435        assert_eq!(field, "token_endpoint");
1436        assert_eq!(url, endpoint);
1437
1438        // Err - Fragment
1439        let endpoint = Url::parse("https://localhost/token#fragment").unwrap();
1440        metadata.token_endpoint = Some(endpoint.clone());
1441        let (field, url) = assert_matches!(
1442            metadata.clone().validate(&issuer),
1443            Err(ProviderMetadataVerificationError::UrlWithFragment(field, url)) => (field, url)
1444        );
1445        assert_eq!(field, "token_endpoint");
1446        assert_eq!(url, endpoint);
1447
1448        // Ok - Query
1449        metadata.token_endpoint = Some(Url::parse("https://localhost/token?query").unwrap());
1450        metadata.validate(&issuer).unwrap();
1451    }
1452
1453    #[test]
1454    fn validate_jwks_uri() {
1455        let (mut metadata, issuer) = valid_provider_metadata();
1456
1457        // Err - Missing
1458        metadata.jwks_uri = None;
1459        assert_matches!(
1460            metadata.clone().validate(&issuer),
1461            Err(ProviderMetadataVerificationError::MissingJwksUri)
1462        );
1463
1464        // Err - Not https
1465        let endpoint = Url::parse("http://localhost/jwks").unwrap();
1466        metadata.jwks_uri = Some(endpoint.clone());
1467        let (field, url) = assert_matches!(
1468            metadata.clone().validate(&issuer),
1469            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1470        );
1471        assert_eq!(field, "jwks_uri");
1472        assert_eq!(url, endpoint);
1473
1474        // Ok - Query & fragment
1475        metadata.jwks_uri = Some(Url::parse("https://localhost/token?query#fragment").unwrap());
1476        metadata.validate(&issuer).unwrap();
1477    }
1478
1479    #[test]
1480    fn validate_registration_endpoint() {
1481        let (mut metadata, issuer) = valid_provider_metadata();
1482
1483        // Err - Not https
1484        let endpoint = Url::parse("http://localhost/registration").unwrap();
1485        metadata.registration_endpoint = Some(endpoint.clone());
1486        let (field, url) = assert_matches!(
1487            metadata.clone().validate(&issuer),
1488            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1489        );
1490        assert_eq!(field, "registration_endpoint");
1491        assert_eq!(url, endpoint);
1492
1493        // Ok - Missing
1494        metadata.registration_endpoint = None;
1495        metadata.clone().validate(&issuer).unwrap();
1496
1497        // Ok - Query & fragment
1498        metadata.registration_endpoint =
1499            Some(Url::parse("https://localhost/registration?query#fragment").unwrap());
1500        metadata.validate(&issuer).unwrap();
1501    }
1502
1503    #[test]
1504    fn validate_scopes_supported() {
1505        let (mut metadata, issuer) = valid_provider_metadata();
1506
1507        // Err - No `openid`
1508        metadata.scopes_supported = Some(vec!["custom".to_owned()]);
1509        assert_matches!(
1510            metadata.clone().validate(&issuer),
1511            Err(ProviderMetadataVerificationError::ScopesMissingOpenid)
1512        );
1513
1514        // Ok - Missing
1515        metadata.scopes_supported = None;
1516        metadata.clone().validate(&issuer).unwrap();
1517
1518        // Ok - With `openid`
1519        metadata.scopes_supported = Some(vec!["openid".to_owned(), "custom".to_owned()]);
1520        metadata.validate(&issuer).unwrap();
1521    }
1522
1523    #[test]
1524    fn validate_response_types_supported() {
1525        let (mut metadata, issuer) = valid_provider_metadata();
1526
1527        // Err - Missing
1528        metadata.response_types_supported = None;
1529        assert_matches!(
1530            metadata.clone().validate(&issuer),
1531            Err(ProviderMetadataVerificationError::MissingResponseTypesSupported)
1532        );
1533
1534        // Ok - Present
1535        metadata.response_types_supported =
1536            Some(vec![OAuthAuthorizationEndpointResponseType::Code.into()]);
1537        metadata.validate(&issuer).unwrap();
1538    }
1539
1540    #[test]
1541    fn validate_token_endpoint_signing_alg_values_supported() {
1542        let (mut metadata, issuer) = valid_provider_metadata();
1543
1544        // Ok - Missing
1545        metadata.token_endpoint_auth_signing_alg_values_supported = None;
1546        metadata.token_endpoint_auth_methods_supported = None;
1547        metadata.clone().validate(&issuer).unwrap();
1548
1549        // Err - With `none`
1550        metadata.token_endpoint_auth_signing_alg_values_supported =
1551            Some(vec![JsonWebSignatureAlg::None]);
1552        let endpoint = assert_matches!(
1553            metadata.clone().validate(&issuer),
1554            Err(ProviderMetadataVerificationError::SigningAlgValuesWithNone(endpoint)) => endpoint
1555        );
1556        assert_eq!(endpoint, "token_endpoint");
1557
1558        // Ok - Other signing alg values.
1559        metadata.token_endpoint_auth_signing_alg_values_supported =
1560            Some(vec![JsonWebSignatureAlg::Rs256, JsonWebSignatureAlg::EdDsa]);
1561        metadata.clone().validate(&issuer).unwrap();
1562
1563        // Err - `client_secret_jwt` without signing alg values.
1564        metadata.token_endpoint_auth_methods_supported =
1565            Some(vec![OAuthClientAuthenticationMethod::ClientSecretJwt]);
1566        metadata.token_endpoint_auth_signing_alg_values_supported = None;
1567        let endpoint = assert_matches!(
1568            metadata.clone().validate(&issuer),
1569            Err(ProviderMetadataVerificationError::MissingAuthSigningAlgValues(endpoint)) => endpoint
1570        );
1571        assert_eq!(endpoint, "token_endpoint");
1572
1573        // Ok - `client_secret_jwt` with signing alg values.
1574        metadata.token_endpoint_auth_signing_alg_values_supported =
1575            Some(vec![JsonWebSignatureAlg::Rs256]);
1576        metadata.clone().validate(&issuer).unwrap();
1577
1578        // Err - `private_key_jwt` without signing alg values.
1579        metadata.token_endpoint_auth_methods_supported =
1580            Some(vec![OAuthClientAuthenticationMethod::PrivateKeyJwt]);
1581        metadata.token_endpoint_auth_signing_alg_values_supported = None;
1582        let endpoint = assert_matches!(
1583            metadata.clone().validate(&issuer),
1584            Err(ProviderMetadataVerificationError::MissingAuthSigningAlgValues(endpoint)) => endpoint
1585        );
1586        assert_eq!(endpoint, "token_endpoint");
1587
1588        // Ok - `private_key_jwt` with signing alg values.
1589        metadata.token_endpoint_auth_signing_alg_values_supported =
1590            Some(vec![JsonWebSignatureAlg::Rs256]);
1591        metadata.clone().validate(&issuer).unwrap();
1592
1593        // Ok - Other auth methods without signing alg values.
1594        metadata.token_endpoint_auth_methods_supported = Some(vec![
1595            OAuthClientAuthenticationMethod::ClientSecretBasic,
1596            OAuthClientAuthenticationMethod::ClientSecretPost,
1597        ]);
1598        metadata.token_endpoint_auth_signing_alg_values_supported = None;
1599        metadata.validate(&issuer).unwrap();
1600    }
1601
1602    #[test]
1603    fn validate_revocation_endpoint() {
1604        let (mut metadata, issuer) = valid_provider_metadata();
1605
1606        // Ok - Missing
1607        metadata.revocation_endpoint = None;
1608        metadata.clone().validate(&issuer).unwrap();
1609
1610        // Err - Not https
1611        let endpoint = Url::parse("http://localhost/revocation").unwrap();
1612        metadata.revocation_endpoint = Some(endpoint.clone());
1613        let (field, url) = assert_matches!(
1614            metadata.clone().validate(&issuer),
1615            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1616        );
1617        assert_eq!(field, "revocation_endpoint");
1618        assert_eq!(url, endpoint);
1619
1620        // Err - Fragment
1621        let endpoint = Url::parse("https://localhost/revocation#fragment").unwrap();
1622        metadata.revocation_endpoint = Some(endpoint.clone());
1623        let (field, url) = assert_matches!(
1624            metadata.clone().validate(&issuer),
1625            Err(ProviderMetadataVerificationError::UrlWithFragment(field, url)) => (field, url)
1626        );
1627        assert_eq!(field, "revocation_endpoint");
1628        assert_eq!(url, endpoint);
1629
1630        // Ok - Query
1631        metadata.revocation_endpoint =
1632            Some(Url::parse("https://localhost/revocation?query").unwrap());
1633        metadata.validate(&issuer).unwrap();
1634    }
1635
1636    #[test]
1637    fn validate_revocation_endpoint_signing_alg_values_supported() {
1638        let (mut metadata, issuer) = valid_provider_metadata();
1639
1640        // Only check that this field is validated, algorithm checks are already
1641        // tested for the token endpoint.
1642
1643        // Ok - Missing
1644        metadata.revocation_endpoint_auth_signing_alg_values_supported = None;
1645        metadata.revocation_endpoint_auth_methods_supported = None;
1646        metadata.clone().validate(&issuer).unwrap();
1647
1648        // Err - With `none`
1649        metadata.revocation_endpoint_auth_signing_alg_values_supported =
1650            Some(vec![JsonWebSignatureAlg::None]);
1651        let endpoint = assert_matches!(
1652            metadata.validate(&issuer),
1653            Err(ProviderMetadataVerificationError::SigningAlgValuesWithNone(endpoint)) => endpoint
1654        );
1655        assert_eq!(endpoint, "revocation_endpoint");
1656    }
1657
1658    #[test]
1659    fn validate_introspection_endpoint() {
1660        let (mut metadata, issuer) = valid_provider_metadata();
1661
1662        // Ok - Missing
1663        metadata.introspection_endpoint = None;
1664        metadata.clone().validate(&issuer).unwrap();
1665
1666        // Err - Not https
1667        let endpoint = Url::parse("http://localhost/introspection").unwrap();
1668        metadata.introspection_endpoint = Some(endpoint.clone());
1669        let (field, url) = assert_matches!(
1670            metadata.clone().validate(&issuer),
1671            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1672        );
1673        assert_eq!(field, "introspection_endpoint");
1674        assert_eq!(url, endpoint);
1675
1676        // Ok - Query & Fragment
1677        metadata.introspection_endpoint =
1678            Some(Url::parse("https://localhost/introspection?query#fragment").unwrap());
1679        metadata.validate(&issuer).unwrap();
1680    }
1681
1682    #[test]
1683    fn validate_introspection_endpoint_signing_alg_values_supported() {
1684        let (mut metadata, issuer) = valid_provider_metadata();
1685
1686        // Only check that this field is validated, algorithm checks are already
1687        // tested for the token endpoint.
1688
1689        // Ok - Missing
1690        metadata.introspection_endpoint_auth_signing_alg_values_supported = None;
1691        metadata.introspection_endpoint_auth_methods_supported = None;
1692        metadata.clone().validate(&issuer).unwrap();
1693
1694        // Err - With `none`
1695        metadata.introspection_endpoint_auth_signing_alg_values_supported =
1696            Some(vec![JsonWebSignatureAlg::None]);
1697        let endpoint = assert_matches!(
1698            metadata.validate(&issuer),
1699            Err(ProviderMetadataVerificationError::SigningAlgValuesWithNone(endpoint)) => endpoint
1700        );
1701        assert_eq!(endpoint, "introspection_endpoint");
1702    }
1703
1704    #[test]
1705    fn validate_userinfo_endpoint() {
1706        let (mut metadata, issuer) = valid_provider_metadata();
1707
1708        // Ok - Missing
1709        metadata.userinfo_endpoint = None;
1710        metadata.clone().validate(&issuer).unwrap();
1711
1712        // Err - Not https
1713        let endpoint = Url::parse("http://localhost/userinfo").unwrap();
1714        metadata.userinfo_endpoint = Some(endpoint.clone());
1715        let (field, url) = assert_matches!(
1716            metadata.clone().validate(&issuer),
1717            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1718        );
1719        assert_eq!(field, "userinfo_endpoint");
1720        assert_eq!(url, endpoint);
1721
1722        // Ok - Query & Fragment
1723        metadata.userinfo_endpoint =
1724            Some(Url::parse("https://localhost/userinfo?query#fragment").unwrap());
1725        metadata.validate(&issuer).unwrap();
1726    }
1727
1728    #[test]
1729    fn validate_subject_types_supported() {
1730        let (mut metadata, issuer) = valid_provider_metadata();
1731
1732        // Err - Missing
1733        metadata.subject_types_supported = None;
1734        assert_matches!(
1735            metadata.clone().validate(&issuer),
1736            Err(ProviderMetadataVerificationError::MissingSubjectTypesSupported)
1737        );
1738
1739        // Ok - Present
1740        metadata.subject_types_supported = Some(vec![SubjectType::Public, SubjectType::Pairwise]);
1741        metadata.validate(&issuer).unwrap();
1742    }
1743
1744    #[test]
1745    fn validate_id_token_signing_alg_values_supported() {
1746        let (mut metadata, issuer) = valid_provider_metadata();
1747
1748        // Err - Missing
1749        metadata.id_token_signing_alg_values_supported = None;
1750        assert_matches!(
1751            metadata.clone().validate(&issuer),
1752            Err(ProviderMetadataVerificationError::MissingIdTokenSigningAlgValuesSupported)
1753        );
1754
1755        // Ok - Present
1756        metadata.id_token_signing_alg_values_supported =
1757            Some(vec![JsonWebSignatureAlg::Rs256, JsonWebSignatureAlg::EdDsa]);
1758        metadata.validate(&issuer).unwrap();
1759    }
1760
1761    #[test]
1762    fn validate_pushed_authorization_request_endpoint() {
1763        let (mut metadata, issuer) = valid_provider_metadata();
1764
1765        // Ok - Missing
1766        metadata.pushed_authorization_request_endpoint = None;
1767        metadata.clone().validate(&issuer).unwrap();
1768
1769        // Err - Not https
1770        let endpoint = Url::parse("http://localhost/par").unwrap();
1771        metadata.pushed_authorization_request_endpoint = Some(endpoint.clone());
1772        let (field, url) = assert_matches!(
1773            metadata.clone().validate(&issuer),
1774            Err(ProviderMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1775        );
1776        assert_eq!(field, "pushed_authorization_request_endpoint");
1777        assert_eq!(url, endpoint);
1778
1779        // Ok - Query & Fragment
1780        metadata.pushed_authorization_request_endpoint =
1781            Some(Url::parse("https://localhost/par?query#fragment").unwrap());
1782        metadata.validate(&issuer).unwrap();
1783    }
1784
1785    #[test]
1786    fn serialize_application_type() {
1787        assert_eq!(
1788            serde_json::to_string(&ApplicationType::Web).unwrap(),
1789            "\"web\""
1790        );
1791        assert_eq!(
1792            serde_json::to_string(&ApplicationType::Native).unwrap(),
1793            "\"native\""
1794        );
1795    }
1796
1797    #[test]
1798    fn deserialize_application_type() {
1799        assert_eq!(
1800            serde_json::from_str::<ApplicationType>("\"web\"").unwrap(),
1801            ApplicationType::Web
1802        );
1803        assert_eq!(
1804            serde_json::from_str::<ApplicationType>("\"native\"").unwrap(),
1805            ApplicationType::Native
1806        );
1807    }
1808
1809    #[test]
1810    fn serialize_subject_type() {
1811        assert_eq!(
1812            serde_json::to_string(&SubjectType::Public).unwrap(),
1813            "\"public\""
1814        );
1815        assert_eq!(
1816            serde_json::to_string(&SubjectType::Pairwise).unwrap(),
1817            "\"pairwise\""
1818        );
1819    }
1820
1821    #[test]
1822    fn deserialize_subject_type() {
1823        assert_eq!(
1824            serde_json::from_str::<SubjectType>("\"public\"").unwrap(),
1825            SubjectType::Public
1826        );
1827        assert_eq!(
1828            serde_json::from_str::<SubjectType>("\"pairwise\"").unwrap(),
1829            SubjectType::Pairwise
1830        );
1831    }
1832
1833    #[test]
1834    fn serialize_claim_type() {
1835        assert_eq!(
1836            serde_json::to_string(&ClaimType::Normal).unwrap(),
1837            "\"normal\""
1838        );
1839        assert_eq!(
1840            serde_json::to_string(&ClaimType::Aggregated).unwrap(),
1841            "\"aggregated\""
1842        );
1843        assert_eq!(
1844            serde_json::to_string(&ClaimType::Distributed).unwrap(),
1845            "\"distributed\""
1846        );
1847    }
1848
1849    #[test]
1850    fn deserialize_claim_type() {
1851        assert_eq!(
1852            serde_json::from_str::<ClaimType>("\"normal\"").unwrap(),
1853            ClaimType::Normal
1854        );
1855        assert_eq!(
1856            serde_json::from_str::<ClaimType>("\"aggregated\"").unwrap(),
1857            ClaimType::Aggregated
1858        );
1859        assert_eq!(
1860            serde_json::from_str::<ClaimType>("\"distributed\"").unwrap(),
1861            ClaimType::Distributed
1862        );
1863    }
1864
1865    #[test]
1866    fn deserialize_auth_method_or_token_type_type() {
1867        assert_eq!(
1868            serde_json::from_str::<AuthenticationMethodOrAccessTokenType>("\"none\"").unwrap(),
1869            AuthenticationMethodOrAccessTokenType::AuthenticationMethod(
1870                OAuthClientAuthenticationMethod::None
1871            )
1872        );
1873        assert_eq!(
1874            serde_json::from_str::<AuthenticationMethodOrAccessTokenType>("\"Bearer\"").unwrap(),
1875            AuthenticationMethodOrAccessTokenType::AccessTokenType(OAuthAccessTokenType::Bearer)
1876        );
1877        assert_eq!(
1878            serde_json::from_str::<AuthenticationMethodOrAccessTokenType>("\"unknown_value\"")
1879                .unwrap(),
1880            AuthenticationMethodOrAccessTokenType::Unknown("unknown_value".to_owned())
1881        );
1882    }
1883}