oauth2_types/registration/
mod.rs

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