oauth2_types/registration/
mod.rs

1// Copyright 2024 New Vector Ltd.
2// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only
5// Please see LICENSE in the repository root for full details.
6
7//! Types for [Dynamic Client Registration].
8//!
9//! [Dynamic Client Registration]: https://openid.net/specs/openid-connect-registration-1_0.html
10
11use std::{collections::HashMap, ops::Deref};
12
13use chrono::{DateTime, Duration, Utc};
14use language_tags::LanguageTag;
15use mas_iana::{
16    jose::{JsonWebEncryptionAlg, JsonWebEncryptionEnc, JsonWebSignatureAlg},
17    oauth::{OAuthAuthorizationEndpointResponseType, OAuthClientAuthenticationMethod},
18};
19use mas_jose::jwk::PublicJsonWebKeySet;
20use serde::{Deserialize, Serialize};
21use serde_with::{TimestampSeconds, serde_as, skip_serializing_none};
22use thiserror::Error;
23use url::Url;
24
25use crate::{
26    oidc::{ApplicationType, SubjectType},
27    requests::GrantType,
28    response_type::ResponseType,
29};
30
31mod client_metadata_serde;
32use client_metadata_serde::ClientMetadataSerdeHelper;
33
34/// The default value of `response_types` if it is not set.
35pub const DEFAULT_RESPONSE_TYPES: [OAuthAuthorizationEndpointResponseType; 1] =
36    [OAuthAuthorizationEndpointResponseType::Code];
37
38/// The default value of `grant_types` if it is not set.
39pub const DEFAULT_GRANT_TYPES: &[GrantType] = &[GrantType::AuthorizationCode];
40
41/// The default value of `application_type` if it is not set.
42pub const DEFAULT_APPLICATION_TYPE: ApplicationType = ApplicationType::Web;
43
44/// The default value of `token_endpoint_auth_method` if it is not set.
45pub const DEFAULT_TOKEN_AUTH_METHOD: &OAuthClientAuthenticationMethod =
46    &OAuthClientAuthenticationMethod::ClientSecretBasic;
47
48/// The default value of `id_token_signed_response_alg` if it is not set.
49pub const DEFAULT_SIGNING_ALGORITHM: &JsonWebSignatureAlg = &JsonWebSignatureAlg::Rs256;
50
51/// The default value of `id_token_encrypted_response_enc` if it is not set.
52pub const DEFAULT_ENCRYPTION_ENC_ALGORITHM: &JsonWebEncryptionEnc =
53    &JsonWebEncryptionEnc::A128CbcHs256;
54
55/// A collection of localized variants.
56///
57/// Always includes one non-localized variant.
58#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct Localized<T> {
60    non_localized: T,
61    localized: HashMap<LanguageTag, T>,
62}
63
64impl<T> Localized<T> {
65    /// Constructs a new `Localized` with the given non-localized and localized
66    /// variants.
67    pub fn new(non_localized: T, localized: impl IntoIterator<Item = (LanguageTag, T)>) -> Self {
68        Self {
69            non_localized,
70            localized: localized.into_iter().collect(),
71        }
72    }
73
74    /// Returns the number of variants.
75    #[allow(clippy::len_without_is_empty)]
76    pub fn len(&self) -> usize {
77        self.localized.len() + 1
78    }
79
80    /// Get the non-localized variant.
81    pub fn non_localized(&self) -> &T {
82        &self.non_localized
83    }
84
85    /// Get the non-localized variant.
86    pub fn to_non_localized(self) -> T {
87        self.non_localized
88    }
89
90    /// Get the variant corresponding to the given language, if it exists.
91    pub fn get(&self, language: Option<&LanguageTag>) -> Option<&T> {
92        match language {
93            Some(lang) => self.localized.get(lang),
94            None => Some(&self.non_localized),
95        }
96    }
97
98    /// Get an iterator over the variants.
99    pub fn iter(&self) -> impl Iterator<Item = (Option<&LanguageTag>, &T)> {
100        Some(&self.non_localized)
101            .into_iter()
102            .map(|val| (None, val))
103            .chain(self.localized.iter().map(|(lang, val)| (Some(lang), val)))
104    }
105}
106
107impl<T> From<(T, HashMap<LanguageTag, T>)> for Localized<T> {
108    fn from(t: (T, HashMap<LanguageTag, T>)) -> Self {
109        Localized {
110            non_localized: t.0,
111            localized: t.1,
112        }
113    }
114}
115
116/// Client metadata, as described by the [IANA registry].
117///
118/// All the fields with a default value are accessible via methods.
119///
120/// [IANA registry]: https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml#client-metadata
121#[derive(Deserialize, Debug, PartialEq, Eq, Clone, Default)]
122#[serde(from = "ClientMetadataSerdeHelper")]
123pub struct ClientMetadata {
124    /// Array of redirection URIs for use in redirect-based flows such as the
125    /// [authorization code flow].
126    ///
127    /// All the URIs used by the client in an authorization request's
128    /// `redirect_uri` field must appear in this list.
129    ///
130    /// This field is required and the URIs must not contain a fragment.
131    ///
132    /// [authorization code flow]: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
133    pub redirect_uris: Option<Vec<Url>>,
134
135    /// Array of the [OAuth 2.0 `response_type` values] that the client can use
136    /// at the [authorization endpoint].
137    ///
138    /// All the types used by the client in an authorization request's
139    /// `response_type` field must appear in this list.
140    ///
141    /// Defaults to [`DEFAULT_RESPONSE_TYPES`].
142    ///
143    /// [OAuth 2.0 `response_type` values]: https://www.rfc-editor.org/rfc/rfc7591#page-9
144    /// [authorization endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.1
145    pub response_types: Option<Vec<ResponseType>>,
146
147    /// Array of [OAuth 2.0 `grant_type` values] that the client can use at the
148    /// [token endpoint].
149    ///
150    /// The possible grant types depend on the response types. Declaring support
151    /// for a grant type that is not compatible with the supported response
152    /// types will trigger an error during validation.
153    ///
154    /// All the types used by the client in a token request's `grant_type` field
155    /// must appear in this list.
156    ///
157    /// Defaults to [`DEFAULT_GRANT_TYPES`].
158    ///
159    /// [OAuth 2.0 `grant_type` values]: https://www.rfc-editor.org/rfc/rfc7591#page-9
160    /// [token endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.2
161    pub grant_types: Option<Vec<GrantType>>,
162
163    /// The kind of the application.
164    ///
165    /// Defaults to [`DEFAULT_APPLICATION_TYPE`].
166    pub application_type: Option<ApplicationType>,
167
168    /// Array of e-mail addresses of people responsible for this client.
169    pub contacts: Option<Vec<String>>,
170
171    /// Name of the client to be presented to the end-user during authorization.
172    pub client_name: Option<Localized<String>>,
173
174    /// URL that references a logo for the client application.
175    pub logo_uri: Option<Localized<Url>>,
176
177    /// URL of the home page of the client.
178    pub client_uri: Option<Localized<Url>>,
179
180    /// URL that the client provides to the end-user to read about the how the
181    /// profile data will be used.
182    pub policy_uri: Option<Localized<Url>>,
183
184    /// URL that the client provides to the end-user to read about the client's
185    /// terms of service.
186    pub tos_uri: Option<Localized<Url>>,
187
188    /// URL for the client's [JWK] Set document.
189    ///
190    /// If the client signs requests to the server, it contains the signing
191    /// key(s) the server uses to validate signatures from the client. The JWK
192    /// Set may also contain the client's encryption keys(s), which are used by
193    /// the server to encrypt responses to the client.
194    ///
195    /// This field is mutually exclusive with `jwks`.
196    ///
197    /// [JWK]: https://www.rfc-editor.org/rfc/rfc7517.html
198    pub jwks_uri: Option<Url>,
199
200    /// Client's [JWK] Set document, passed by value.
201    ///
202    /// The semantics of this field are the same as `jwks_uri`, other than that
203    /// the JWK Set is passed by value, rather than by reference.
204    ///
205    /// This field is mutually exclusive with `jwks_uri`.
206    ///
207    /// [JWK]: https://www.rfc-editor.org/rfc/rfc7517.html
208    pub jwks: Option<PublicJsonWebKeySet>,
209
210    /// A unique identifier string assigned by the client developer or software
211    /// publisher used by registration endpoints to identify the client software
212    /// to be dynamically registered.
213    ///
214    /// It should remain the same for all instances and versions of the client
215    /// software.
216    pub software_id: Option<String>,
217
218    /// A version identifier string for the client software identified by
219    /// `software_id`.
220    pub software_version: Option<String>,
221
222    /// URL to be used in calculating pseudonymous identifiers by the OpenID
223    /// Connect provider when [pairwise subject identifiers] are used.
224    ///
225    /// If present, this must use the `https` scheme.
226    ///
227    /// [pairwise subject identifiers]: https://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg
228    pub sector_identifier_uri: Option<Url>,
229
230    /// Subject type requested for responses to this client.
231    ///
232    /// This field must match one of the supported types by the provider.
233    pub subject_type: Option<SubjectType>,
234
235    /// Requested client authentication method for the [token endpoint].
236    ///
237    /// If this is set to [`OAuthClientAuthenticationMethod::PrivateKeyJwt`],
238    /// one of the `jwks_uri` or `jwks` fields is required.
239    ///
240    /// Defaults to [`DEFAULT_TOKEN_AUTH_METHOD`].
241    ///
242    /// [token endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.2
243    pub token_endpoint_auth_method: Option<OAuthClientAuthenticationMethod>,
244
245    /// [JWS] `alg` algorithm that must be used for signing the [JWT] used to
246    /// authenticate the client at the token endpoint.
247    ///
248    /// If this field is present, it must not be
249    /// [`JsonWebSignatureAlg::None`]. This field is required if
250    /// `token_endpoint_auth_method` is one of
251    /// [`OAuthClientAuthenticationMethod::PrivateKeyJwt`] or
252    /// [`OAuthClientAuthenticationMethod::ClientSecretJwt`].
253    ///
254    /// [JWS]: http://tools.ietf.org/html/draft-ietf-jose-json-web-signature
255    /// [JWT]: http://tools.ietf.org/html/draft-ietf-oauth-json-web-token
256    pub token_endpoint_auth_signing_alg: Option<JsonWebSignatureAlg>,
257
258    /// [JWS] `alg` algorithm required for signing the ID Token issued to this
259    /// client.
260    ///
261    /// If this field is present, it must not be
262    /// [`JsonWebSignatureAlg::None`], unless the client uses only response
263    /// types that return no ID Token from the authorization endpoint.
264    ///
265    /// Defaults to [`DEFAULT_SIGNING_ALGORITHM`].
266    ///
267    /// [JWS]: http://tools.ietf.org/html/draft-ietf-jose-json-web-signature
268    pub id_token_signed_response_alg: Option<JsonWebSignatureAlg>,
269
270    /// [JWE] `alg` algorithm required for encrypting the ID Token issued to
271    /// this client.
272    ///
273    /// This field is required if `id_token_encrypted_response_enc` is provided.
274    ///
275    /// [JWE]: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
276    pub id_token_encrypted_response_alg: Option<JsonWebEncryptionAlg>,
277
278    /// [JWE] `enc` algorithm required for encrypting the ID Token issued to
279    /// this client.
280    ///
281    /// Defaults to [`DEFAULT_ENCRYPTION_ENC_ALGORITHM`] if
282    /// `id_token_encrypted_response_alg` is provided.
283    ///
284    /// [JWE]: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
285    pub id_token_encrypted_response_enc: Option<JsonWebEncryptionEnc>,
286
287    /// [JWS] `alg` algorithm required for signing user info responses.
288    ///
289    /// [JWS]: http://tools.ietf.org/html/draft-ietf-jose-json-web-signature
290    pub userinfo_signed_response_alg: Option<JsonWebSignatureAlg>,
291
292    /// [JWE] `alg` algorithm required for encrypting user info responses.
293    ///
294    /// If `userinfo_signed_response_alg` is not provided, this field has no
295    /// effect.
296    ///
297    /// This field is required if `userinfo_encrypted_response_enc` is provided.
298    ///
299    /// [JWE]: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
300    pub userinfo_encrypted_response_alg: Option<JsonWebEncryptionAlg>,
301
302    /// [JWE] `enc` algorithm required for encrypting user info responses.
303    ///
304    /// If `userinfo_signed_response_alg` is not provided, this field has no
305    /// effect.
306    ///
307    /// Defaults to [`DEFAULT_ENCRYPTION_ENC_ALGORITHM`] if
308    /// `userinfo_encrypted_response_alg` is provided.
309    ///
310    /// [JWE]: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
311    pub userinfo_encrypted_response_enc: Option<JsonWebEncryptionEnc>,
312
313    /// [JWS] `alg` algorithm that must be used for signing Request Objects sent
314    /// to the provider.
315    ///
316    /// Defaults to any algorithm supported by the client and the provider.
317    ///
318    /// [JWS]: http://tools.ietf.org/html/draft-ietf-jose-json-web-signature
319    pub request_object_signing_alg: Option<JsonWebSignatureAlg>,
320
321    /// [JWE] `alg` algorithm the client is declaring that it may use for
322    /// encrypting Request Objects sent to the provider.
323    ///
324    /// This field is required if `request_object_encryption_enc` is provided.
325    ///
326    /// [JWE]: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
327    pub request_object_encryption_alg: Option<JsonWebEncryptionAlg>,
328
329    /// [JWE] `enc` algorithm the client is declaring that it may use for
330    /// encrypting Request Objects sent to the provider.
331    ///
332    /// Defaults to [`DEFAULT_ENCRYPTION_ENC_ALGORITHM`] if
333    /// `request_object_encryption_alg` is provided.
334    ///
335    /// [JWE]: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
336    pub request_object_encryption_enc: Option<JsonWebEncryptionEnc>,
337
338    /// Default maximum authentication age.
339    ///
340    /// Specifies that the End-User must be actively authenticated if the
341    /// end-user was authenticated longer ago than the specified number of
342    /// seconds.
343    ///
344    /// The `max_age` request parameter overrides this default value.
345    pub default_max_age: Option<Duration>,
346
347    /// Whether the `auth_time` Claim in the ID Token is required.
348    ///
349    /// Defaults to `false`.
350    pub require_auth_time: Option<bool>,
351
352    /// Default requested Authentication Context Class Reference values.
353    pub default_acr_values: Option<Vec<String>>,
354
355    /// URI that a third party can use to [initiate a login by the client].
356    ///
357    /// If present, this must use the `https` scheme.
358    ///
359    /// [initiate a login by the client]: https://openid.net/specs/openid-connect-core-1_0.html#ThirdPartyInitiatedLogin
360    pub initiate_login_uri: Option<Url>,
361
362    /// `request_uri` values that are pre-registered by the client for use at
363    /// the provider.
364    ///
365    /// Providers can require that `request_uri` values used be pre-registered
366    /// with the `require_request_uri_registration` discovery parameter.
367    ///
368    /// Servers MAY cache the contents of the files referenced by these URIs and
369    /// not retrieve them at the time they are used in a request. If the
370    /// contents of the request file could ever change, these URI values should
371    /// include the base64url encoded SHA-256 hash value of the file contents
372    /// referenced by the URI as the value of the URI fragment. If the fragment
373    /// value used for a URI changes, that signals the server that its cached
374    /// value for that URI with the old fragment value is no longer valid.
375    pub request_uris: Option<Vec<Url>>,
376
377    /// Whether the client will only send authorization requests as [Request
378    /// Objects].
379    ///
380    /// Defaults to `false`.
381    ///
382    /// [Request Object]: https://www.rfc-editor.org/rfc/rfc9101.html
383    pub require_signed_request_object: Option<bool>,
384
385    /// Whether the client will only send authorization requests via the [pushed
386    /// authorization request endpoint].
387    ///
388    /// Defaults to `false`.
389    ///
390    /// [pushed authorization request endpoint]: https://www.rfc-editor.org/rfc/rfc9126.html
391    pub require_pushed_authorization_requests: Option<bool>,
392
393    /// [JWS] `alg` algorithm for signing responses of the [introspection
394    /// endpoint].
395    ///
396    /// [JWS]: http://tools.ietf.org/html/draft-ietf-jose-json-web-signature
397    /// [introspection endpoint]: https://www.rfc-editor.org/info/rfc7662
398    pub introspection_signed_response_alg: Option<JsonWebSignatureAlg>,
399
400    /// [JWE] `alg` algorithm for encrypting responses of the [introspection
401    /// endpoint].
402    ///
403    /// If `introspection_signed_response_alg` is not provided, this field has
404    /// no effect.
405    ///
406    /// This field is required if `introspection_encrypted_response_enc` is
407    /// provided.
408    ///
409    /// [JWE]: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
410    /// [introspection endpoint]: https://www.rfc-editor.org/info/rfc7662
411    pub introspection_encrypted_response_alg: Option<JsonWebEncryptionAlg>,
412
413    /// [JWE] `enc` algorithm for encrypting responses of the [introspection
414    /// endpoint].
415    ///
416    /// If `introspection_signed_response_alg` is not provided, this field has
417    /// no effect.
418    ///
419    /// Defaults to [`DEFAULT_ENCRYPTION_ENC_ALGORITHM`] if
420    /// `introspection_encrypted_response_alg` is provided.
421    ///
422    /// [JWE]: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
423    /// [introspection endpoint]: https://www.rfc-editor.org/info/rfc7662
424    pub introspection_encrypted_response_enc: Option<JsonWebEncryptionEnc>,
425
426    /// `post_logout_redirect_uri` values that are pre-registered by the client
427    /// for use at the provider's [RP-Initiated Logout endpoint].
428    ///
429    /// [RP-Initiated Logout endpoint]: https://openid.net/specs/openid-connect-rpinitiated-1_0.html
430    pub post_logout_redirect_uris: Option<Vec<Url>>,
431}
432
433impl ClientMetadata {
434    /// Validate this `ClientMetadata` according to the [OpenID Connect Dynamic
435    /// Client Registration Spec 1.0].
436    ///
437    /// # Errors
438    ///
439    /// Will return `Err` if validation fails.
440    ///
441    /// [OpenID Connect Dynamic Client Registration Spec 1.0]: https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata
442    #[allow(clippy::too_many_lines)]
443    pub fn validate(self) -> Result<VerifiedClientMetadata, ClientMetadataVerificationError> {
444        let grant_types = self.grant_types();
445        let has_implicit = grant_types.contains(&GrantType::Implicit);
446        let has_authorization_code = grant_types.contains(&GrantType::AuthorizationCode);
447        let has_both = has_implicit && has_authorization_code;
448
449        if let Some(uris) = &self.redirect_uris {
450            if let Some(uri) = uris.iter().find(|uri| uri.fragment().is_some()) {
451                return Err(ClientMetadataVerificationError::RedirectUriWithFragment(
452                    uri.clone(),
453                ));
454            }
455        } else if has_authorization_code || has_implicit {
456            // Required for authorization code and implicit flows
457            return Err(ClientMetadataVerificationError::MissingRedirectUris);
458        }
459
460        let response_type_code = [OAuthAuthorizationEndpointResponseType::Code.into()];
461        let response_types = match &self.response_types {
462            Some(types) => &types[..],
463            // Default to code only if the client uses the authorization code or implicit flow
464            None if has_authorization_code || has_implicit => &response_type_code[..],
465            None => &[],
466        };
467
468        for response_type in response_types {
469            let has_code = response_type.has_code();
470            let has_id_token = response_type.has_id_token();
471            let has_token = response_type.has_token();
472            let is_ok = has_code && has_both
473                || !has_code && has_implicit
474                || has_authorization_code && !has_id_token && !has_token
475                || !has_code && !has_id_token && !has_token;
476
477            if !is_ok {
478                return Err(ClientMetadataVerificationError::IncoherentResponseType(
479                    response_type.clone(),
480                ));
481            }
482        }
483
484        if self.jwks_uri.is_some() && self.jwks.is_some() {
485            return Err(ClientMetadataVerificationError::JwksUriAndJwksMutuallyExclusive);
486        }
487
488        if let Some(url) = self
489            .sector_identifier_uri
490            .as_ref()
491            .filter(|url| url.scheme() != "https")
492        {
493            return Err(ClientMetadataVerificationError::UrlNonHttpsScheme(
494                "sector_identifier_uri",
495                url.clone(),
496            ));
497        }
498
499        if *self.token_endpoint_auth_method() == OAuthClientAuthenticationMethod::PrivateKeyJwt
500            && self.jwks_uri.is_none()
501            && self.jwks.is_none()
502        {
503            return Err(ClientMetadataVerificationError::MissingJwksForTokenMethod);
504        }
505
506        if let Some(alg) = &self.token_endpoint_auth_signing_alg {
507            if *alg == JsonWebSignatureAlg::None {
508                return Err(ClientMetadataVerificationError::UnauthorizedSigningAlgNone(
509                    "token_endpoint",
510                ));
511            }
512        } else if matches!(
513            self.token_endpoint_auth_method(),
514            OAuthClientAuthenticationMethod::PrivateKeyJwt
515                | OAuthClientAuthenticationMethod::ClientSecretJwt
516        ) {
517            return Err(ClientMetadataVerificationError::MissingAuthSigningAlg(
518                "token_endpoint",
519            ));
520        }
521
522        if *self.id_token_signed_response_alg() == JsonWebSignatureAlg::None
523            && response_types.iter().any(ResponseType::has_id_token)
524        {
525            return Err(ClientMetadataVerificationError::IdTokenSigningAlgNone);
526        }
527
528        if self.id_token_encrypted_response_enc.is_some() {
529            self.id_token_encrypted_response_alg.as_ref().ok_or(
530                ClientMetadataVerificationError::MissingEncryptionAlg("id_token"),
531            )?;
532        }
533
534        if self.userinfo_encrypted_response_enc.is_some() {
535            self.userinfo_encrypted_response_alg.as_ref().ok_or(
536                ClientMetadataVerificationError::MissingEncryptionAlg("userinfo"),
537            )?;
538        }
539
540        if self.request_object_encryption_enc.is_some() {
541            self.request_object_encryption_alg.as_ref().ok_or(
542                ClientMetadataVerificationError::MissingEncryptionAlg("request_object"),
543            )?;
544        }
545
546        if let Some(url) = self
547            .initiate_login_uri
548            .as_ref()
549            .filter(|url| url.scheme() != "https")
550        {
551            return Err(ClientMetadataVerificationError::UrlNonHttpsScheme(
552                "initiate_login_uri",
553                url.clone(),
554            ));
555        }
556
557        if self.introspection_encrypted_response_enc.is_some() {
558            self.introspection_encrypted_response_alg.as_ref().ok_or(
559                ClientMetadataVerificationError::MissingEncryptionAlg("introspection"),
560            )?;
561        }
562
563        Ok(VerifiedClientMetadata { inner: self })
564    }
565
566    /// Array of the [OAuth 2.0 `response_type` values] that the client can use
567    /// at the [authorization endpoint].
568    ///
569    /// All the types used by the client in an authorization request's
570    /// `response_type` field must appear in this list.
571    ///
572    /// Defaults to [`DEFAULT_RESPONSE_TYPES`].
573    ///
574    /// [OAuth 2.0 `response_type` values]: https://www.rfc-editor.org/rfc/rfc7591#page-9
575    /// [authorization endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.1
576    #[must_use]
577    pub fn response_types(&self) -> Vec<ResponseType> {
578        self.response_types.clone().unwrap_or_else(|| {
579            DEFAULT_RESPONSE_TYPES
580                .into_iter()
581                .map(ResponseType::from)
582                .collect()
583        })
584    }
585
586    /// Array of [OAuth 2.0 `grant_type` values] that the client can use at the
587    /// [token endpoint].
588    ///
589    /// Note that the possible grant types depend on the response types.
590    ///
591    /// All the types used by the client in a token request's `grant_type` field
592    /// must appear in this list.
593    ///
594    /// Defaults to [`DEFAULT_GRANT_TYPES`].
595    ///
596    /// [OAuth 2.0 `grant_type` values]: https://www.rfc-editor.org/rfc/rfc7591#page-9
597    /// [token endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.2
598    #[must_use]
599    pub fn grant_types(&self) -> &[GrantType] {
600        self.grant_types.as_deref().unwrap_or(DEFAULT_GRANT_TYPES)
601    }
602
603    /// The kind of the application.
604    ///
605    /// Defaults to [`DEFAULT_APPLICATION_TYPE`].
606    #[must_use]
607    pub fn application_type(&self) -> ApplicationType {
608        self.application_type
609            .clone()
610            .unwrap_or(DEFAULT_APPLICATION_TYPE)
611    }
612
613    /// Requested client authentication method for the [token endpoint].
614    ///
615    /// Defaults to [`DEFAULT_TOKEN_AUTH_METHOD`].
616    ///
617    /// [token endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.2
618    #[must_use]
619    pub fn token_endpoint_auth_method(&self) -> &OAuthClientAuthenticationMethod {
620        self.token_endpoint_auth_method
621            .as_ref()
622            .unwrap_or(DEFAULT_TOKEN_AUTH_METHOD)
623    }
624
625    /// [JWS] `alg` algorithm required for signing the ID Token issued to this
626    /// client.
627    ///
628    /// If this field is present, it must not be
629    /// [`JsonWebSignatureAlg::None`], unless the client uses only response
630    /// types that return no ID Token from the authorization endpoint.
631    ///
632    /// Defaults to [`DEFAULT_SIGNING_ALGORITHM`].
633    ///
634    /// [JWS]: http://tools.ietf.org/html/draft-ietf-jose-json-web-signature
635    #[must_use]
636    pub fn id_token_signed_response_alg(&self) -> &JsonWebSignatureAlg {
637        self.id_token_signed_response_alg
638            .as_ref()
639            .unwrap_or(DEFAULT_SIGNING_ALGORITHM)
640    }
641
642    /// [JWE] `alg` and `enc` algorithms required for encrypting the ID Token
643    /// issued to this client.
644    ///
645    /// Always returns `Some` if `id_token_encrypted_response_alg` is provided,
646    /// using the default of [`DEFAULT_ENCRYPTION_ENC_ALGORITHM`] for the `enc`
647    /// value if needed.
648    ///
649    /// [JWE]: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
650    #[must_use]
651    pub fn id_token_encrypted_response(
652        &self,
653    ) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> {
654        self.id_token_encrypted_response_alg.as_ref().map(|alg| {
655            (
656                alg,
657                self.id_token_encrypted_response_enc
658                    .as_ref()
659                    .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
660            )
661        })
662    }
663
664    /// [JWE] `alg` and `enc` algorithms required for encrypting user info
665    /// responses.
666    ///
667    /// Always returns `Some` if `userinfo_encrypted_response_alg` is provided,
668    /// using the default of [`DEFAULT_ENCRYPTION_ENC_ALGORITHM`] for the `enc`
669    /// value if needed.
670    ///
671    /// [JWE]: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
672    #[must_use]
673    pub fn userinfo_encrypted_response(
674        &self,
675    ) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> {
676        self.userinfo_encrypted_response_alg.as_ref().map(|alg| {
677            (
678                alg,
679                self.userinfo_encrypted_response_enc
680                    .as_ref()
681                    .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
682            )
683        })
684    }
685
686    /// [JWE] `alg` and `enc` algorithms the client is declaring that it may use
687    /// for encrypting Request Objects sent to the provider.
688    ///
689    /// Always returns `Some` if `request_object_encryption_alg` is provided,
690    /// using the default of [`DEFAULT_ENCRYPTION_ENC_ALGORITHM`] for the `enc`
691    /// value if needed.
692    ///
693    /// [JWE]: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
694    #[must_use]
695    pub fn request_object_encryption(
696        &self,
697    ) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> {
698        self.request_object_encryption_alg.as_ref().map(|alg| {
699            (
700                alg,
701                self.request_object_encryption_enc
702                    .as_ref()
703                    .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
704            )
705        })
706    }
707
708    /// Whether the `auth_time` Claim in the ID Token is required.
709    ///
710    /// Defaults to `false`.
711    #[must_use]
712    pub fn require_auth_time(&self) -> bool {
713        self.require_auth_time.unwrap_or_default()
714    }
715
716    /// Whether the client will only send authorization requests as [Request
717    /// Objects].
718    ///
719    /// Defaults to `false`.
720    ///
721    /// [Request Object]: https://www.rfc-editor.org/rfc/rfc9101.html
722    #[must_use]
723    pub fn require_signed_request_object(&self) -> bool {
724        self.require_signed_request_object.unwrap_or_default()
725    }
726
727    /// Whether the client will only send authorization requests via the [pushed
728    /// authorization request endpoint].
729    ///
730    /// Defaults to `false`.
731    ///
732    /// [pushed authorization request endpoint]: https://www.rfc-editor.org/rfc/rfc9126.html
733    #[must_use]
734    pub fn require_pushed_authorization_requests(&self) -> bool {
735        self.require_pushed_authorization_requests
736            .unwrap_or_default()
737    }
738
739    /// [JWE] `alg` and `enc` algorithms for encrypting responses of the
740    /// [introspection endpoint].
741    ///
742    /// Always returns `Some` if `introspection_encrypted_response_alg` is
743    /// provided, using the default of [`DEFAULT_ENCRYPTION_ENC_ALGORITHM`] for
744    /// the `enc` value if needed.
745    ///
746    /// [JWE]: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
747    /// [introspection endpoint]: https://www.rfc-editor.org/info/rfc7662
748    #[must_use]
749    pub fn introspection_encrypted_response(
750        &self,
751    ) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> {
752        self.introspection_encrypted_response_alg
753            .as_ref()
754            .map(|alg| {
755                (
756                    alg,
757                    self.introspection_encrypted_response_enc
758                        .as_ref()
759                        .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
760                )
761            })
762    }
763}
764
765/// The verified client metadata.
766///
767/// All the fields required by the [OpenID Connect Dynamic Client Registration
768/// Spec 1.0] or with a default value are accessible via methods.
769///
770/// To access other fields, use this type's `Deref` implementation.
771///
772/// [OpenID Connect Dynamic Client Registration Spec 1.0]: https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata
773#[derive(Serialize, Debug, PartialEq, Eq, Clone)]
774#[serde(into = "ClientMetadataSerdeHelper")]
775pub struct VerifiedClientMetadata {
776    inner: ClientMetadata,
777}
778
779impl VerifiedClientMetadata {
780    /// Array of redirection URIs for use in redirect-based flows such as the
781    /// [authorization code flow].
782    ///
783    /// All the URIs used by the client in an authorization request's
784    /// `redirect_uri` field must appear in this list.
785    ///
786    /// [authorization code flow]: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
787    #[must_use]
788    pub fn redirect_uris(&self) -> &[Url] {
789        match &self.redirect_uris {
790            Some(v) => v,
791            None => &[],
792        }
793    }
794}
795
796impl Deref for VerifiedClientMetadata {
797    type Target = ClientMetadata;
798
799    fn deref(&self) -> &Self::Target {
800        &self.inner
801    }
802}
803
804/// All errors that can happen when verifying [`ClientMetadata`].
805#[derive(Debug, Error)]
806pub enum ClientMetadataVerificationError {
807    /// The redirect URIs are missing.
808    #[error("redirect URIs are missing")]
809    MissingRedirectUris,
810
811    /// The redirect URI has a fragment, which is not allowed.
812    #[error("redirect URI with fragment: {0}")]
813    RedirectUriWithFragment(Url),
814
815    /// The given response type is not compatible with the grant types.
816    #[error("'{0}' response type not compatible with grant types")]
817    IncoherentResponseType(ResponseType),
818
819    /// Both the `jwks_uri` and `jwks` fields are present but only one is
820    /// allowed.
821    #[error("jwks_uri and jwks are mutually exclusive")]
822    JwksUriAndJwksMutuallyExclusive,
823
824    /// The URL of the given field doesn't use a `https` scheme.
825    #[error("{0}'s URL doesn't use a https scheme: {1}")]
826    UrlNonHttpsScheme(&'static str, Url),
827
828    /// No JWK Set was provided but one is required for the token auth method.
829    #[error("missing JWK Set for token auth method")]
830    MissingJwksForTokenMethod,
831
832    /// The given endpoint doesn't allow `none` as a signing algorithm.
833    #[error("none signing alg unauthorized for {0}")]
834    UnauthorizedSigningAlgNone(&'static str),
835
836    /// The given endpoint is missing an auth signing algorithm, but it is
837    /// required because it uses one of the `client_secret_jwt` or
838    /// `private_key_jwt` authentication methods.
839    #[error("{0} missing auth signing algorithm")]
840    MissingAuthSigningAlg(&'static str),
841
842    /// `none` is used as the signing algorithm for ID Tokens, but is not
843    /// allowed.
844    #[error("ID Token signing alg is none")]
845    IdTokenSigningAlgNone,
846
847    /// The given encryption field has an `enc` value but not `alg` value.
848    #[error("{0} missing encryption alg value")]
849    MissingEncryptionAlg(&'static str),
850}
851
852/// The issuer response to dynamic client registration.
853#[serde_as]
854#[skip_serializing_none]
855#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
856pub struct ClientRegistrationResponse {
857    /// A unique client identifier.
858    pub client_id: String,
859
860    /// A client secret, if the `token_endpoint_auth_method` requires one.
861    #[serde(default)]
862    pub client_secret: Option<String>,
863
864    /// Time at which the Client Identifier was issued.
865    #[serde(default)]
866    #[serde_as(as = "Option<TimestampSeconds<i64>>")]
867    pub client_id_issued_at: Option<DateTime<Utc>>,
868
869    /// Time at which the client_secret will expire or 0 if it will not expire.
870    ///
871    /// Required if `client_secret` is issued.
872    #[serde(default)]
873    #[serde_as(as = "Option<TimestampSeconds<i64>>")]
874    pub client_secret_expires_at: Option<DateTime<Utc>>,
875}
876
877#[cfg(test)]
878mod tests {
879    use assert_matches::assert_matches;
880    use mas_iana::{
881        jose::{JsonWebEncryptionAlg, JsonWebEncryptionEnc, JsonWebSignatureAlg},
882        oauth::{OAuthAuthorizationEndpointResponseType, OAuthClientAuthenticationMethod},
883    };
884    use mas_jose::jwk::PublicJsonWebKeySet;
885    use url::Url;
886
887    use super::{ClientMetadata, ClientMetadataVerificationError};
888    use crate::{requests::GrantType, response_type::ResponseType};
889
890    fn valid_client_metadata() -> ClientMetadata {
891        ClientMetadata {
892            redirect_uris: Some(vec![Url::parse("http://localhost/oidc").unwrap()]),
893            ..Default::default()
894        }
895    }
896
897    fn jwks() -> PublicJsonWebKeySet {
898        serde_json::from_value(serde_json::json!({
899            "keys": [
900                {
901                    "alg": "RS256",
902                    "kty": "RSA",
903                    "n": "tCwhHOxX_ylh5kVwfVqW7QIBTIsPjkjCjVCppDrynuF_3msEdtEaG64eJUz84ODFNMCC0BQ57G7wrKQVWkdSDxWUEqGk2BixBiHJRWZdofz1WOBTdPVicvHW5Zl_aIt7uXWMdOp_SODw-O2y2f05EqbFWFnR2-1y9K8KbiOp82CD72ny1Jbb_3PxTs2Z0F4ECAtTzpDteaJtjeeueRjr7040JAjQ-5fpL5D1g8x14LJyVIo-FL_y94NPFbMp7UCi69CIfVHXFO8WYFz949og-47mWRrID5lS4zpx-QLuvNhUb_lSqmylUdQB3HpRdOcYdj3xwy4MHJuu7tTaf0AmCQ",
904                    "use": "sig",
905                    "kid": "d98f49bc6ca4581eae8dfadd494fce10ea23aab0",
906                    "e": "AQAB"
907                }
908            ]
909        })).unwrap()
910    }
911
912    #[test]
913    fn validate_required_metadata() {
914        let metadata = valid_client_metadata();
915        metadata.validate().unwrap();
916    }
917
918    #[test]
919    fn validate_redirect_uris() {
920        let mut metadata = ClientMetadata::default();
921
922        // Err - Missing
923        assert_matches!(
924            metadata.clone().validate(),
925            Err(ClientMetadataVerificationError::MissingRedirectUris)
926        );
927
928        // Err - Fragment
929        let wrong_uri = Url::parse("http://localhost/#fragment").unwrap();
930        metadata.redirect_uris = Some(vec![
931            Url::parse("http://localhost/").unwrap(),
932            wrong_uri.clone(),
933        ]);
934        let uri = assert_matches!(
935            metadata.clone().validate(),
936            Err(ClientMetadataVerificationError::RedirectUriWithFragment(uri)) => uri
937        );
938        assert_eq!(uri, wrong_uri);
939
940        // Ok - Path & Query
941        metadata.redirect_uris = Some(vec![
942            Url::parse("http://localhost/").unwrap(),
943            Url::parse("http://localhost/oidc").unwrap(),
944            Url::parse("http://localhost/?oidc").unwrap(),
945            Url::parse("http://localhost/my-client?oidc").unwrap(),
946        ]);
947        metadata.validate().unwrap();
948    }
949
950    #[test]
951    #[allow(clippy::too_many_lines)]
952    fn validate_response_types() {
953        let mut metadata = valid_client_metadata();
954
955        // grant_type = authorization_code
956        // code - Ok
957        metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::Code.into()]);
958        metadata.clone().validate().unwrap();
959
960        // code id_token - Err
961        let response_type: ResponseType =
962            OAuthAuthorizationEndpointResponseType::CodeIdToken.into();
963        metadata.response_types = Some(vec![response_type.clone()]);
964        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
965        assert_eq!(res, response_type);
966
967        // code id_token token - Err
968        let response_type: ResponseType =
969            OAuthAuthorizationEndpointResponseType::CodeIdTokenToken.into();
970        metadata.response_types = Some(vec![response_type.clone()]);
971        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
972        assert_eq!(res, response_type);
973
974        // code token - Err
975        let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::CodeToken.into();
976        metadata.response_types = Some(vec![response_type.clone()]);
977        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
978        assert_eq!(res, response_type);
979
980        // id_token - Err
981        let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::IdToken.into();
982        metadata.response_types = Some(vec![response_type.clone()]);
983        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
984        assert_eq!(res, response_type);
985
986        // id_token token - Err
987        let response_type: ResponseType =
988            OAuthAuthorizationEndpointResponseType::IdTokenToken.into();
989        metadata.response_types = Some(vec![response_type.clone()]);
990        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
991        assert_eq!(res, response_type);
992
993        // token - Err
994        let response_type: ResponseType =
995            OAuthAuthorizationEndpointResponseType::IdTokenToken.into();
996        metadata.response_types = Some(vec![response_type.clone()]);
997        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
998        assert_eq!(res, response_type);
999
1000        // none - Ok
1001        metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::None.into()]);
1002        metadata.clone().validate().unwrap();
1003
1004        // grant_type = implicit
1005        metadata.grant_types = Some(vec![GrantType::Implicit]);
1006        // code - Err
1007        let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::Code.into();
1008        metadata.response_types = Some(vec![response_type.clone()]);
1009        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1010        assert_eq!(res, response_type);
1011
1012        // code id_token - Err
1013        let response_type: ResponseType =
1014            OAuthAuthorizationEndpointResponseType::CodeIdToken.into();
1015        metadata.response_types = Some(vec![response_type.clone()]);
1016        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1017        assert_eq!(res, response_type);
1018
1019        // code id_token token - Err
1020        let response_type: ResponseType =
1021            OAuthAuthorizationEndpointResponseType::CodeIdTokenToken.into();
1022        metadata.response_types = Some(vec![response_type.clone()]);
1023        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1024        assert_eq!(res, response_type);
1025
1026        // code token - Err
1027        let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::CodeToken.into();
1028        metadata.response_types = Some(vec![response_type.clone()]);
1029        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1030        assert_eq!(res, response_type);
1031
1032        // id_token - Ok
1033        metadata.response_types =
1034            Some(vec![OAuthAuthorizationEndpointResponseType::IdToken.into()]);
1035        metadata.clone().validate().unwrap();
1036
1037        // id_token token - Ok
1038        metadata.response_types = Some(vec![
1039            OAuthAuthorizationEndpointResponseType::IdTokenToken.into(),
1040        ]);
1041        metadata.clone().validate().unwrap();
1042
1043        // token - Ok
1044        metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::Token.into()]);
1045        metadata.clone().validate().unwrap();
1046
1047        // none - Ok
1048        metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::None.into()]);
1049        metadata.clone().validate().unwrap();
1050
1051        // grant_types = [authorization_code, implicit]
1052        metadata.grant_types = Some(vec![GrantType::AuthorizationCode, GrantType::Implicit]);
1053        // code - Ok
1054        metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::Code.into()]);
1055        metadata.clone().validate().unwrap();
1056
1057        // code id_token - Ok
1058        metadata.response_types = Some(vec![
1059            OAuthAuthorizationEndpointResponseType::CodeIdToken.into(),
1060        ]);
1061        metadata.clone().validate().unwrap();
1062
1063        // code id_token token - Ok
1064        metadata.response_types = Some(vec![
1065            OAuthAuthorizationEndpointResponseType::CodeIdTokenToken.into(),
1066        ]);
1067        metadata.clone().validate().unwrap();
1068
1069        // code token - Ok
1070        metadata.response_types = Some(vec![
1071            OAuthAuthorizationEndpointResponseType::CodeToken.into(),
1072        ]);
1073        metadata.clone().validate().unwrap();
1074
1075        // id_token - Ok
1076        metadata.response_types =
1077            Some(vec![OAuthAuthorizationEndpointResponseType::IdToken.into()]);
1078        metadata.clone().validate().unwrap();
1079
1080        // id_token token - Ok
1081        metadata.response_types = Some(vec![
1082            OAuthAuthorizationEndpointResponseType::IdTokenToken.into(),
1083        ]);
1084        metadata.clone().validate().unwrap();
1085
1086        // token - Ok
1087        metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::Token.into()]);
1088        metadata.clone().validate().unwrap();
1089
1090        // none - Ok
1091        metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::None.into()]);
1092        metadata.clone().validate().unwrap();
1093
1094        // other grant_types
1095        metadata.grant_types = Some(vec![GrantType::RefreshToken, GrantType::ClientCredentials]);
1096        // code - Err
1097        let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::Code.into();
1098        metadata.response_types = Some(vec![response_type.clone()]);
1099        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1100        assert_eq!(res, response_type);
1101
1102        // code id_token - Err
1103        let response_type: ResponseType =
1104            OAuthAuthorizationEndpointResponseType::CodeIdToken.into();
1105        metadata.response_types = Some(vec![response_type.clone()]);
1106        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1107        assert_eq!(res, response_type);
1108
1109        // code id_token token - Err
1110        let response_type: ResponseType =
1111            OAuthAuthorizationEndpointResponseType::CodeIdTokenToken.into();
1112        metadata.response_types = Some(vec![response_type.clone()]);
1113        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1114        assert_eq!(res, response_type);
1115
1116        // code token - Err
1117        let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::CodeToken.into();
1118        metadata.response_types = Some(vec![response_type.clone()]);
1119        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1120        assert_eq!(res, response_type);
1121
1122        // id_token - Err
1123        let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::IdToken.into();
1124        metadata.response_types = Some(vec![response_type.clone()]);
1125        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1126        assert_eq!(res, response_type);
1127
1128        // id_token token - Err
1129        let response_type: ResponseType =
1130            OAuthAuthorizationEndpointResponseType::IdTokenToken.into();
1131        metadata.response_types = Some(vec![response_type.clone()]);
1132        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1133        assert_eq!(res, response_type);
1134
1135        // token - Err
1136        let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::Token.into();
1137        metadata.response_types = Some(vec![response_type.clone()]);
1138        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1139        assert_eq!(res, response_type);
1140
1141        // none - Ok
1142        metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::None.into()]);
1143        metadata.validate().unwrap();
1144    }
1145
1146    #[test]
1147    fn validate_jwks() {
1148        let mut metadata = valid_client_metadata();
1149
1150        // Ok - jwks_uri is set
1151        metadata.jwks_uri = Some(Url::parse("http://localhost/jwks").unwrap());
1152        metadata.clone().validate().unwrap();
1153
1154        // Err - Both are set
1155        metadata.jwks = Some(jwks());
1156        assert_matches!(
1157            metadata.clone().validate(),
1158            Err(ClientMetadataVerificationError::JwksUriAndJwksMutuallyExclusive)
1159        );
1160
1161        // Ok - jwks is set
1162        metadata.jwks_uri = None;
1163        metadata.validate().unwrap();
1164    }
1165
1166    #[test]
1167    fn validate_sector_identifier_uri() {
1168        let mut metadata = valid_client_metadata();
1169
1170        // Err - Non-https URL
1171        let identifier_uri = Url::parse("http://localhost/").unwrap();
1172        metadata.sector_identifier_uri = Some(identifier_uri.clone());
1173        let (field, url) = assert_matches!(
1174            metadata.clone().validate(),
1175            Err(ClientMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1176        );
1177        assert_eq!(field, "sector_identifier_uri");
1178        assert_eq!(url, identifier_uri);
1179
1180        // Ok - https URL
1181        metadata.sector_identifier_uri = Some(Url::parse("https://localhost/").unwrap());
1182        metadata.validate().unwrap();
1183    }
1184
1185    #[test]
1186    fn validate_token_endpoint_auth_method() {
1187        let mut metadata = valid_client_metadata();
1188
1189        // Err - token_endpoint_auth_signing_alg is none
1190        metadata.token_endpoint_auth_signing_alg = Some(JsonWebSignatureAlg::None);
1191        let field = assert_matches!(
1192            metadata.clone().validate(),
1193            Err(ClientMetadataVerificationError::UnauthorizedSigningAlgNone(field)) => field
1194        );
1195        assert_eq!(field, "token_endpoint");
1196
1197        // private_key_jwt
1198        metadata.token_endpoint_auth_method = Some(OAuthClientAuthenticationMethod::PrivateKeyJwt);
1199        metadata.token_endpoint_auth_signing_alg = Some(JsonWebSignatureAlg::Rs256);
1200
1201        // Err - No JWKS
1202        assert_matches!(
1203            metadata.clone().validate(),
1204            Err(ClientMetadataVerificationError::MissingJwksForTokenMethod)
1205        );
1206
1207        // Ok - jwks_uri
1208        metadata.jwks_uri = Some(Url::parse("https://localhost/jwks").unwrap());
1209        metadata.clone().validate().unwrap();
1210
1211        // Ok - jwks
1212        metadata.jwks_uri = None;
1213        metadata.jwks = Some(jwks());
1214        metadata.clone().validate().unwrap();
1215
1216        // Err - No token_endpoint_auth_signing_alg
1217        metadata.token_endpoint_auth_signing_alg = None;
1218        let field = assert_matches!(
1219            metadata.clone().validate(),
1220            Err(ClientMetadataVerificationError::MissingAuthSigningAlg(field)) => field
1221        );
1222        assert_eq!(field, "token_endpoint");
1223
1224        // client_secret_jwt
1225        metadata.token_endpoint_auth_method =
1226            Some(OAuthClientAuthenticationMethod::ClientSecretJwt);
1227        metadata.jwks = None;
1228
1229        // Err - No token_endpoint_auth_signing_alg
1230        let field = assert_matches!(
1231            metadata.clone().validate(),
1232            Err(ClientMetadataVerificationError::MissingAuthSigningAlg(field)) => field
1233        );
1234        assert_eq!(field, "token_endpoint");
1235
1236        // Ok - Has token_endpoint_auth_signing_alg
1237        metadata.token_endpoint_auth_signing_alg = Some(JsonWebSignatureAlg::Rs256);
1238        metadata.validate().unwrap();
1239    }
1240
1241    #[test]
1242    fn validate_id_token_signed_response_alg() {
1243        let mut metadata = valid_client_metadata();
1244        metadata.id_token_signed_response_alg = Some(JsonWebSignatureAlg::None);
1245        metadata.grant_types = Some(vec![GrantType::AuthorizationCode, GrantType::Implicit]);
1246
1247        // Err - code id_token
1248        metadata.response_types = Some(vec![
1249            OAuthAuthorizationEndpointResponseType::CodeIdToken.into(),
1250        ]);
1251        assert_matches!(
1252            metadata.clone().validate(),
1253            Err(ClientMetadataVerificationError::IdTokenSigningAlgNone)
1254        );
1255
1256        // Err - code id_token token
1257        metadata.response_types = Some(vec![
1258            OAuthAuthorizationEndpointResponseType::CodeIdTokenToken.into(),
1259        ]);
1260        assert_matches!(
1261            metadata.clone().validate(),
1262            Err(ClientMetadataVerificationError::IdTokenSigningAlgNone)
1263        );
1264
1265        // Err - id_token
1266        metadata.response_types =
1267            Some(vec![OAuthAuthorizationEndpointResponseType::IdToken.into()]);
1268        assert_matches!(
1269            metadata.clone().validate(),
1270            Err(ClientMetadataVerificationError::IdTokenSigningAlgNone)
1271        );
1272
1273        // Err - id_token token
1274        metadata.response_types = Some(vec![
1275            OAuthAuthorizationEndpointResponseType::IdTokenToken.into(),
1276        ]);
1277        assert_matches!(
1278            metadata.clone().validate(),
1279            Err(ClientMetadataVerificationError::IdTokenSigningAlgNone)
1280        );
1281
1282        // Ok - Other response types
1283        metadata.response_types = Some(vec![
1284            OAuthAuthorizationEndpointResponseType::Code.into(),
1285            OAuthAuthorizationEndpointResponseType::CodeToken.into(),
1286            OAuthAuthorizationEndpointResponseType::Token.into(),
1287            OAuthAuthorizationEndpointResponseType::None.into(),
1288        ]);
1289        metadata.validate().unwrap();
1290    }
1291
1292    #[test]
1293    fn validate_id_token_encrypted_response() {
1294        let mut metadata = valid_client_metadata();
1295        metadata.id_token_encrypted_response_enc = Some(JsonWebEncryptionEnc::A128CbcHs256);
1296
1297        // Err - No id_token_encrypted_response_alg
1298        let field = assert_matches!(
1299            metadata.clone().validate(),
1300            Err(ClientMetadataVerificationError::MissingEncryptionAlg(field)) => field
1301        );
1302        assert_eq!(field, "id_token");
1303
1304        // Ok - Has id_token_encrypted_response_alg
1305        metadata.id_token_encrypted_response_alg = Some(JsonWebEncryptionAlg::RsaOaep);
1306        metadata.validate().unwrap();
1307    }
1308
1309    #[test]
1310    fn validate_userinfo_encrypted_response() {
1311        let mut metadata = valid_client_metadata();
1312        metadata.userinfo_encrypted_response_enc = Some(JsonWebEncryptionEnc::A128CbcHs256);
1313
1314        // Err - No userinfo_encrypted_response_alg
1315        let field = assert_matches!(
1316            metadata.clone().validate(),
1317            Err(ClientMetadataVerificationError::MissingEncryptionAlg(field)) => field
1318        );
1319        assert_eq!(field, "userinfo");
1320
1321        // Ok - Has userinfo_encrypted_response_alg
1322        metadata.userinfo_encrypted_response_alg = Some(JsonWebEncryptionAlg::RsaOaep);
1323        metadata.validate().unwrap();
1324    }
1325
1326    #[test]
1327    fn validate_request_object_encryption() {
1328        let mut metadata = valid_client_metadata();
1329        metadata.request_object_encryption_enc = Some(JsonWebEncryptionEnc::A128CbcHs256);
1330
1331        // Err - No request_object_encryption_alg
1332        let field = assert_matches!(
1333            metadata.clone().validate(),
1334            Err(ClientMetadataVerificationError::MissingEncryptionAlg(field)) => field
1335        );
1336        assert_eq!(field, "request_object");
1337
1338        // Ok - Has request_object_encryption_alg
1339        metadata.request_object_encryption_alg = Some(JsonWebEncryptionAlg::RsaOaep);
1340        metadata.validate().unwrap();
1341    }
1342
1343    #[test]
1344    fn validate_initiate_login_uri() {
1345        let mut metadata = valid_client_metadata();
1346
1347        // Err - Non-https URL
1348        let initiate_uri = Url::parse("http://localhost/").unwrap();
1349        metadata.initiate_login_uri = Some(initiate_uri.clone());
1350        let (field, url) = assert_matches!(
1351            metadata.clone().validate(),
1352            Err(ClientMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1353        );
1354        assert_eq!(field, "initiate_login_uri");
1355        assert_eq!(url, initiate_uri);
1356
1357        // Ok - https URL
1358        metadata.initiate_login_uri = Some(Url::parse("https://localhost/").unwrap());
1359        metadata.validate().unwrap();
1360    }
1361
1362    #[test]
1363    fn validate_introspection_encrypted_response() {
1364        let mut metadata = valid_client_metadata();
1365        metadata.introspection_encrypted_response_enc = Some(JsonWebEncryptionEnc::A128CbcHs256);
1366
1367        // Err - No introspection_encrypted_response_alg
1368        let field = assert_matches!(
1369            metadata.clone().validate(),
1370            Err(ClientMetadataVerificationError::MissingEncryptionAlg(field)) => field
1371        );
1372        assert_eq!(field, "introspection");
1373
1374        // Ok - Has introspection_encrypted_response_alg
1375        metadata.introspection_encrypted_response_alg = Some(JsonWebEncryptionAlg::RsaOaep);
1376        metadata.validate().unwrap();
1377    }
1378}