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::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    #[allow(clippy::too_many_lines)]
444    pub fn validate(self) -> Result<VerifiedClientMetadata, ClientMetadataVerificationError> {
445        let grant_types = self.grant_types();
446        let has_implicit = grant_types.contains(&GrantType::Implicit);
447        let has_authorization_code = grant_types.contains(&GrantType::AuthorizationCode);
448        let has_both = has_implicit && has_authorization_code;
449
450        if let Some(uris) = &self.redirect_uris {
451            if let Some(uri) = uris.iter().find(|uri| uri.fragment().is_some()) {
452                return Err(ClientMetadataVerificationError::RedirectUriWithFragment(
453                    uri.clone(),
454                ));
455            }
456        } else if has_authorization_code || has_implicit {
457            // Required for authorization code and implicit flows
458            return Err(ClientMetadataVerificationError::MissingRedirectUris);
459        }
460
461        let response_type_code = [OAuthAuthorizationEndpointResponseType::Code.into()];
462        let response_types = match &self.response_types {
463            Some(types) => &types[..],
464            // Default to code only if the client uses the authorization code or implicit flow
465            None if has_authorization_code || has_implicit => &response_type_code[..],
466            None => &[],
467        };
468
469        for response_type in response_types {
470            let has_code = response_type.has_code();
471            let has_id_token = response_type.has_id_token();
472            let has_token = response_type.has_token();
473            let is_ok = has_code && has_both
474                || !has_code && has_implicit
475                || has_authorization_code && !has_id_token && !has_token
476                || !has_code && !has_id_token && !has_token;
477
478            if !is_ok {
479                return Err(ClientMetadataVerificationError::IncoherentResponseType(
480                    response_type.clone(),
481                ));
482            }
483        }
484
485        if self.jwks_uri.is_some() && self.jwks.is_some() {
486            return Err(ClientMetadataVerificationError::JwksUriAndJwksMutuallyExclusive);
487        }
488
489        if let Some(url) = self
490            .sector_identifier_uri
491            .as_ref()
492            .filter(|url| url.scheme() != "https")
493        {
494            return Err(ClientMetadataVerificationError::UrlNonHttpsScheme(
495                "sector_identifier_uri",
496                url.clone(),
497            ));
498        }
499
500        if *self.token_endpoint_auth_method() == OAuthClientAuthenticationMethod::PrivateKeyJwt
501            && self.jwks_uri.is_none()
502            && self.jwks.is_none()
503        {
504            return Err(ClientMetadataVerificationError::MissingJwksForTokenMethod);
505        }
506
507        if let Some(alg) = &self.token_endpoint_auth_signing_alg {
508            if *alg == JsonWebSignatureAlg::None {
509                return Err(ClientMetadataVerificationError::UnauthorizedSigningAlgNone(
510                    "token_endpoint",
511                ));
512            }
513        } else if matches!(
514            self.token_endpoint_auth_method(),
515            OAuthClientAuthenticationMethod::PrivateKeyJwt
516                | OAuthClientAuthenticationMethod::ClientSecretJwt
517        ) {
518            return Err(ClientMetadataVerificationError::MissingAuthSigningAlg(
519                "token_endpoint",
520            ));
521        }
522
523        if *self.id_token_signed_response_alg() == JsonWebSignatureAlg::None
524            && response_types.iter().any(ResponseType::has_id_token)
525        {
526            return Err(ClientMetadataVerificationError::IdTokenSigningAlgNone);
527        }
528
529        if self.id_token_encrypted_response_enc.is_some() {
530            self.id_token_encrypted_response_alg.as_ref().ok_or(
531                ClientMetadataVerificationError::MissingEncryptionAlg("id_token"),
532            )?;
533        }
534
535        if self.userinfo_encrypted_response_enc.is_some() {
536            self.userinfo_encrypted_response_alg.as_ref().ok_or(
537                ClientMetadataVerificationError::MissingEncryptionAlg("userinfo"),
538            )?;
539        }
540
541        if self.request_object_encryption_enc.is_some() {
542            self.request_object_encryption_alg.as_ref().ok_or(
543                ClientMetadataVerificationError::MissingEncryptionAlg("request_object"),
544            )?;
545        }
546
547        if let Some(url) = self
548            .initiate_login_uri
549            .as_ref()
550            .filter(|url| url.scheme() != "https")
551        {
552            return Err(ClientMetadataVerificationError::UrlNonHttpsScheme(
553                "initiate_login_uri",
554                url.clone(),
555            ));
556        }
557
558        if self.introspection_encrypted_response_enc.is_some() {
559            self.introspection_encrypted_response_alg.as_ref().ok_or(
560                ClientMetadataVerificationError::MissingEncryptionAlg("introspection"),
561            )?;
562        }
563
564        Ok(VerifiedClientMetadata { inner: self })
565    }
566
567    /// Sort the properties. This is inteded to ensure a stable serialization
568    /// order when needed.
569    #[must_use]
570    pub fn sorted(mut self) -> Self {
571        // This sorts all the Vec<T> and Localized<T> fields
572        if let Some(redirect_uris) = &mut self.redirect_uris {
573            redirect_uris.sort();
574        }
575        if let Some(response_types) = &mut self.response_types {
576            response_types.sort();
577        }
578        if let Some(grant_types) = &mut self.grant_types {
579            grant_types.sort();
580        }
581        if let Some(contacts) = &mut self.contacts {
582            contacts.sort();
583        }
584        if let Some(client_name) = &mut self.client_name {
585            client_name.sort();
586        }
587        if let Some(logo_uri) = &mut self.logo_uri {
588            logo_uri.sort();
589        }
590        if let Some(client_uri) = &mut self.client_uri {
591            client_uri.sort();
592        }
593        if let Some(policy_uri) = &mut self.policy_uri {
594            policy_uri.sort();
595        }
596        if let Some(tos_uri) = &mut self.tos_uri {
597            tos_uri.sort();
598        }
599        if let Some(default_acr_values) = &mut self.default_acr_values {
600            default_acr_values.sort();
601        }
602        if let Some(request_uris) = &mut self.request_uris {
603            request_uris.sort();
604        }
605        if let Some(post_logout_redirect_uris) = &mut self.post_logout_redirect_uris {
606            post_logout_redirect_uris.sort();
607        }
608
609        self
610    }
611
612    /// Array of the [OAuth 2.0 `response_type` values] that the client can use
613    /// at the [authorization endpoint].
614    ///
615    /// All the types used by the client in an authorization request's
616    /// `response_type` field must appear in this list.
617    ///
618    /// Defaults to [`DEFAULT_RESPONSE_TYPES`].
619    ///
620    /// [OAuth 2.0 `response_type` values]: https://www.rfc-editor.org/rfc/rfc7591#page-9
621    /// [authorization endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.1
622    #[must_use]
623    pub fn response_types(&self) -> Vec<ResponseType> {
624        self.response_types.clone().unwrap_or_else(|| {
625            DEFAULT_RESPONSE_TYPES
626                .into_iter()
627                .map(ResponseType::from)
628                .collect()
629        })
630    }
631
632    /// Array of [OAuth 2.0 `grant_type` values] that the client can use at the
633    /// [token endpoint].
634    ///
635    /// Note that the possible grant types depend on the response types.
636    ///
637    /// All the types used by the client in a token request's `grant_type` field
638    /// must appear in this list.
639    ///
640    /// Defaults to [`DEFAULT_GRANT_TYPES`].
641    ///
642    /// [OAuth 2.0 `grant_type` values]: https://www.rfc-editor.org/rfc/rfc7591#page-9
643    /// [token endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.2
644    #[must_use]
645    pub fn grant_types(&self) -> &[GrantType] {
646        self.grant_types.as_deref().unwrap_or(DEFAULT_GRANT_TYPES)
647    }
648
649    /// The kind of the application.
650    ///
651    /// Defaults to [`DEFAULT_APPLICATION_TYPE`].
652    #[must_use]
653    pub fn application_type(&self) -> ApplicationType {
654        self.application_type
655            .clone()
656            .unwrap_or(DEFAULT_APPLICATION_TYPE)
657    }
658
659    /// Requested client authentication method for the [token endpoint].
660    ///
661    /// Defaults to [`DEFAULT_TOKEN_AUTH_METHOD`].
662    ///
663    /// [token endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.2
664    #[must_use]
665    pub fn token_endpoint_auth_method(&self) -> &OAuthClientAuthenticationMethod {
666        self.token_endpoint_auth_method
667            .as_ref()
668            .unwrap_or(DEFAULT_TOKEN_AUTH_METHOD)
669    }
670
671    /// [JWS] `alg` algorithm required for signing the ID Token issued to this
672    /// client.
673    ///
674    /// If this field is present, it must not be
675    /// [`JsonWebSignatureAlg::None`], unless the client uses only response
676    /// types that return no ID Token from the authorization endpoint.
677    ///
678    /// Defaults to [`DEFAULT_SIGNING_ALGORITHM`].
679    ///
680    /// [JWS]: http://tools.ietf.org/html/draft-ietf-jose-json-web-signature
681    #[must_use]
682    pub fn id_token_signed_response_alg(&self) -> &JsonWebSignatureAlg {
683        self.id_token_signed_response_alg
684            .as_ref()
685            .unwrap_or(DEFAULT_SIGNING_ALGORITHM)
686    }
687
688    /// [JWE] `alg` and `enc` algorithms required for encrypting the ID Token
689    /// issued to this client.
690    ///
691    /// Always returns `Some` if `id_token_encrypted_response_alg` is provided,
692    /// using the default of [`DEFAULT_ENCRYPTION_ENC_ALGORITHM`] for the `enc`
693    /// value if needed.
694    ///
695    /// [JWE]: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
696    #[must_use]
697    pub fn id_token_encrypted_response(
698        &self,
699    ) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> {
700        self.id_token_encrypted_response_alg.as_ref().map(|alg| {
701            (
702                alg,
703                self.id_token_encrypted_response_enc
704                    .as_ref()
705                    .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
706            )
707        })
708    }
709
710    /// [JWE] `alg` and `enc` algorithms required for encrypting user info
711    /// responses.
712    ///
713    /// Always returns `Some` if `userinfo_encrypted_response_alg` is provided,
714    /// using the default of [`DEFAULT_ENCRYPTION_ENC_ALGORITHM`] for the `enc`
715    /// value if needed.
716    ///
717    /// [JWE]: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
718    #[must_use]
719    pub fn userinfo_encrypted_response(
720        &self,
721    ) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> {
722        self.userinfo_encrypted_response_alg.as_ref().map(|alg| {
723            (
724                alg,
725                self.userinfo_encrypted_response_enc
726                    .as_ref()
727                    .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
728            )
729        })
730    }
731
732    /// [JWE] `alg` and `enc` algorithms the client is declaring that it may use
733    /// for encrypting Request Objects sent to the provider.
734    ///
735    /// Always returns `Some` if `request_object_encryption_alg` is provided,
736    /// using the default of [`DEFAULT_ENCRYPTION_ENC_ALGORITHM`] for the `enc`
737    /// value if needed.
738    ///
739    /// [JWE]: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
740    #[must_use]
741    pub fn request_object_encryption(
742        &self,
743    ) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> {
744        self.request_object_encryption_alg.as_ref().map(|alg| {
745            (
746                alg,
747                self.request_object_encryption_enc
748                    .as_ref()
749                    .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
750            )
751        })
752    }
753
754    /// Whether the `auth_time` Claim in the ID Token is required.
755    ///
756    /// Defaults to `false`.
757    #[must_use]
758    pub fn require_auth_time(&self) -> bool {
759        self.require_auth_time.unwrap_or_default()
760    }
761
762    /// Whether the client will only send authorization requests as [Request
763    /// Objects].
764    ///
765    /// Defaults to `false`.
766    ///
767    /// [Request Object]: https://www.rfc-editor.org/rfc/rfc9101.html
768    #[must_use]
769    pub fn require_signed_request_object(&self) -> bool {
770        self.require_signed_request_object.unwrap_or_default()
771    }
772
773    /// Whether the client will only send authorization requests via the [pushed
774    /// authorization request endpoint].
775    ///
776    /// Defaults to `false`.
777    ///
778    /// [pushed authorization request endpoint]: https://www.rfc-editor.org/rfc/rfc9126.html
779    #[must_use]
780    pub fn require_pushed_authorization_requests(&self) -> bool {
781        self.require_pushed_authorization_requests
782            .unwrap_or_default()
783    }
784
785    /// [JWE] `alg` and `enc` algorithms for encrypting responses of the
786    /// [introspection endpoint].
787    ///
788    /// Always returns `Some` if `introspection_encrypted_response_alg` is
789    /// provided, using the default of [`DEFAULT_ENCRYPTION_ENC_ALGORITHM`] for
790    /// the `enc` value if needed.
791    ///
792    /// [JWE]: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption
793    /// [introspection endpoint]: https://www.rfc-editor.org/info/rfc7662
794    #[must_use]
795    pub fn introspection_encrypted_response(
796        &self,
797    ) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> {
798        self.introspection_encrypted_response_alg
799            .as_ref()
800            .map(|alg| {
801                (
802                    alg,
803                    self.introspection_encrypted_response_enc
804                        .as_ref()
805                        .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM),
806                )
807            })
808    }
809}
810
811/// The verified client metadata.
812///
813/// All the fields required by the [OpenID Connect Dynamic Client Registration
814/// Spec 1.0] or with a default value are accessible via methods.
815///
816/// To access other fields, use this type's `Deref` implementation.
817///
818/// [OpenID Connect Dynamic Client Registration Spec 1.0]: https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata
819#[derive(Serialize, Debug, PartialEq, Eq, Clone)]
820#[serde(into = "ClientMetadataSerdeHelper")]
821pub struct VerifiedClientMetadata {
822    inner: ClientMetadata,
823}
824
825impl VerifiedClientMetadata {
826    /// Array of redirection URIs for use in redirect-based flows such as the
827    /// [authorization code flow].
828    ///
829    /// All the URIs used by the client in an authorization request's
830    /// `redirect_uri` field must appear in this list.
831    ///
832    /// [authorization code flow]: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
833    #[must_use]
834    pub fn redirect_uris(&self) -> &[Url] {
835        match &self.redirect_uris {
836            Some(v) => v,
837            None => &[],
838        }
839    }
840}
841
842impl Deref for VerifiedClientMetadata {
843    type Target = ClientMetadata;
844
845    fn deref(&self) -> &Self::Target {
846        &self.inner
847    }
848}
849
850/// All errors that can happen when verifying [`ClientMetadata`].
851#[derive(Debug, Error)]
852pub enum ClientMetadataVerificationError {
853    /// The redirect URIs are missing.
854    #[error("redirect URIs are missing")]
855    MissingRedirectUris,
856
857    /// The redirect URI has a fragment, which is not allowed.
858    #[error("redirect URI with fragment: {0}")]
859    RedirectUriWithFragment(Url),
860
861    /// The given response type is not compatible with the grant types.
862    #[error("'{0}' response type not compatible with grant types")]
863    IncoherentResponseType(ResponseType),
864
865    /// Both the `jwks_uri` and `jwks` fields are present but only one is
866    /// allowed.
867    #[error("jwks_uri and jwks are mutually exclusive")]
868    JwksUriAndJwksMutuallyExclusive,
869
870    /// The URL of the given field doesn't use a `https` scheme.
871    #[error("{0}'s URL doesn't use a https scheme: {1}")]
872    UrlNonHttpsScheme(&'static str, Url),
873
874    /// No JWK Set was provided but one is required for the token auth method.
875    #[error("missing JWK Set for token auth method")]
876    MissingJwksForTokenMethod,
877
878    /// The given endpoint doesn't allow `none` as a signing algorithm.
879    #[error("none signing alg unauthorized for {0}")]
880    UnauthorizedSigningAlgNone(&'static str),
881
882    /// The given endpoint is missing an auth signing algorithm, but it is
883    /// required because it uses one of the `client_secret_jwt` or
884    /// `private_key_jwt` authentication methods.
885    #[error("{0} missing auth signing algorithm")]
886    MissingAuthSigningAlg(&'static str),
887
888    /// `none` is used as the signing algorithm for ID Tokens, but is not
889    /// allowed.
890    #[error("ID Token signing alg is none")]
891    IdTokenSigningAlgNone,
892
893    /// The given encryption field has an `enc` value but not `alg` value.
894    #[error("{0} missing encryption alg value")]
895    MissingEncryptionAlg(&'static str),
896}
897
898/// The issuer response to dynamic client registration.
899#[serde_as]
900#[skip_serializing_none]
901#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
902pub struct ClientRegistrationResponse {
903    /// A unique client identifier.
904    pub client_id: String,
905
906    /// A client secret, if the `token_endpoint_auth_method` requires one.
907    #[serde(default)]
908    pub client_secret: Option<String>,
909
910    /// Time at which the Client Identifier was issued.
911    #[serde(default)]
912    #[serde_as(as = "Option<TimestampSeconds<i64>>")]
913    pub client_id_issued_at: Option<DateTime<Utc>>,
914
915    /// Time at which the client_secret will expire or 0 if it will not expire.
916    ///
917    /// Required if `client_secret` is issued.
918    #[serde(default)]
919    #[serde_as(as = "Option<TimestampSeconds<i64>>")]
920    pub client_secret_expires_at: Option<DateTime<Utc>>,
921}
922
923#[cfg(test)]
924mod tests {
925    use assert_matches::assert_matches;
926    use mas_iana::{
927        jose::{JsonWebEncryptionAlg, JsonWebEncryptionEnc, JsonWebSignatureAlg},
928        oauth::{OAuthAuthorizationEndpointResponseType, OAuthClientAuthenticationMethod},
929    };
930    use mas_jose::jwk::PublicJsonWebKeySet;
931    use url::Url;
932
933    use super::{ClientMetadata, ClientMetadataVerificationError};
934    use crate::{requests::GrantType, response_type::ResponseType};
935
936    fn valid_client_metadata() -> ClientMetadata {
937        ClientMetadata {
938            redirect_uris: Some(vec![Url::parse("http://localhost/oidc").unwrap()]),
939            ..Default::default()
940        }
941    }
942
943    fn jwks() -> PublicJsonWebKeySet {
944        serde_json::from_value(serde_json::json!({
945            "keys": [
946                {
947                    "alg": "RS256",
948                    "kty": "RSA",
949                    "n": "tCwhHOxX_ylh5kVwfVqW7QIBTIsPjkjCjVCppDrynuF_3msEdtEaG64eJUz84ODFNMCC0BQ57G7wrKQVWkdSDxWUEqGk2BixBiHJRWZdofz1WOBTdPVicvHW5Zl_aIt7uXWMdOp_SODw-O2y2f05EqbFWFnR2-1y9K8KbiOp82CD72ny1Jbb_3PxTs2Z0F4ECAtTzpDteaJtjeeueRjr7040JAjQ-5fpL5D1g8x14LJyVIo-FL_y94NPFbMp7UCi69CIfVHXFO8WYFz949og-47mWRrID5lS4zpx-QLuvNhUb_lSqmylUdQB3HpRdOcYdj3xwy4MHJuu7tTaf0AmCQ",
950                    "use": "sig",
951                    "kid": "d98f49bc6ca4581eae8dfadd494fce10ea23aab0",
952                    "e": "AQAB"
953                }
954            ]
955        })).unwrap()
956    }
957
958    #[test]
959    fn validate_required_metadata() {
960        let metadata = valid_client_metadata();
961        metadata.validate().unwrap();
962    }
963
964    #[test]
965    fn validate_redirect_uris() {
966        let mut metadata = ClientMetadata::default();
967
968        // Err - Missing
969        assert_matches!(
970            metadata.clone().validate(),
971            Err(ClientMetadataVerificationError::MissingRedirectUris)
972        );
973
974        // Err - Fragment
975        let wrong_uri = Url::parse("http://localhost/#fragment").unwrap();
976        metadata.redirect_uris = Some(vec![
977            Url::parse("http://localhost/").unwrap(),
978            wrong_uri.clone(),
979        ]);
980        let uri = assert_matches!(
981            metadata.clone().validate(),
982            Err(ClientMetadataVerificationError::RedirectUriWithFragment(uri)) => uri
983        );
984        assert_eq!(uri, wrong_uri);
985
986        // Ok - Path & Query
987        metadata.redirect_uris = Some(vec![
988            Url::parse("http://localhost/").unwrap(),
989            Url::parse("http://localhost/oidc").unwrap(),
990            Url::parse("http://localhost/?oidc").unwrap(),
991            Url::parse("http://localhost/my-client?oidc").unwrap(),
992        ]);
993        metadata.validate().unwrap();
994    }
995
996    #[test]
997    #[allow(clippy::too_many_lines)]
998    fn validate_response_types() {
999        let mut metadata = valid_client_metadata();
1000
1001        // grant_type = authorization_code
1002        // code - Ok
1003        metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::Code.into()]);
1004        metadata.clone().validate().unwrap();
1005
1006        // code id_token - Err
1007        let response_type: ResponseType =
1008            OAuthAuthorizationEndpointResponseType::CodeIdToken.into();
1009        metadata.response_types = Some(vec![response_type.clone()]);
1010        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1011        assert_eq!(res, response_type);
1012
1013        // code id_token token - Err
1014        let response_type: ResponseType =
1015            OAuthAuthorizationEndpointResponseType::CodeIdTokenToken.into();
1016        metadata.response_types = Some(vec![response_type.clone()]);
1017        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1018        assert_eq!(res, response_type);
1019
1020        // code token - Err
1021        let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::CodeToken.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        // id_token - Err
1027        let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::IdToken.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 token - Err
1033        let response_type: ResponseType =
1034            OAuthAuthorizationEndpointResponseType::IdTokenToken.into();
1035        metadata.response_types = Some(vec![response_type.clone()]);
1036        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1037        assert_eq!(res, response_type);
1038
1039        // token - Err
1040        let response_type: ResponseType =
1041            OAuthAuthorizationEndpointResponseType::IdTokenToken.into();
1042        metadata.response_types = Some(vec![response_type.clone()]);
1043        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1044        assert_eq!(res, response_type);
1045
1046        // none - Ok
1047        metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::None.into()]);
1048        metadata.clone().validate().unwrap();
1049
1050        // grant_type = implicit
1051        metadata.grant_types = Some(vec![GrantType::Implicit]);
1052        // code - Err
1053        let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::Code.into();
1054        metadata.response_types = Some(vec![response_type.clone()]);
1055        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1056        assert_eq!(res, response_type);
1057
1058        // code id_token - Err
1059        let response_type: ResponseType =
1060            OAuthAuthorizationEndpointResponseType::CodeIdToken.into();
1061        metadata.response_types = Some(vec![response_type.clone()]);
1062        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1063        assert_eq!(res, response_type);
1064
1065        // code id_token token - Err
1066        let response_type: ResponseType =
1067            OAuthAuthorizationEndpointResponseType::CodeIdTokenToken.into();
1068        metadata.response_types = Some(vec![response_type.clone()]);
1069        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1070        assert_eq!(res, response_type);
1071
1072        // code token - Err
1073        let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::CodeToken.into();
1074        metadata.response_types = Some(vec![response_type.clone()]);
1075        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1076        assert_eq!(res, response_type);
1077
1078        // id_token - Ok
1079        metadata.response_types =
1080            Some(vec![OAuthAuthorizationEndpointResponseType::IdToken.into()]);
1081        metadata.clone().validate().unwrap();
1082
1083        // id_token token - Ok
1084        metadata.response_types = Some(vec![
1085            OAuthAuthorizationEndpointResponseType::IdTokenToken.into(),
1086        ]);
1087        metadata.clone().validate().unwrap();
1088
1089        // token - Ok
1090        metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::Token.into()]);
1091        metadata.clone().validate().unwrap();
1092
1093        // none - Ok
1094        metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::None.into()]);
1095        metadata.clone().validate().unwrap();
1096
1097        // grant_types = [authorization_code, implicit]
1098        metadata.grant_types = Some(vec![GrantType::AuthorizationCode, GrantType::Implicit]);
1099        // code - Ok
1100        metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::Code.into()]);
1101        metadata.clone().validate().unwrap();
1102
1103        // code id_token - Ok
1104        metadata.response_types = Some(vec![
1105            OAuthAuthorizationEndpointResponseType::CodeIdToken.into(),
1106        ]);
1107        metadata.clone().validate().unwrap();
1108
1109        // code id_token token - Ok
1110        metadata.response_types = Some(vec![
1111            OAuthAuthorizationEndpointResponseType::CodeIdTokenToken.into(),
1112        ]);
1113        metadata.clone().validate().unwrap();
1114
1115        // code token - Ok
1116        metadata.response_types = Some(vec![
1117            OAuthAuthorizationEndpointResponseType::CodeToken.into(),
1118        ]);
1119        metadata.clone().validate().unwrap();
1120
1121        // id_token - Ok
1122        metadata.response_types =
1123            Some(vec![OAuthAuthorizationEndpointResponseType::IdToken.into()]);
1124        metadata.clone().validate().unwrap();
1125
1126        // id_token token - Ok
1127        metadata.response_types = Some(vec![
1128            OAuthAuthorizationEndpointResponseType::IdTokenToken.into(),
1129        ]);
1130        metadata.clone().validate().unwrap();
1131
1132        // token - Ok
1133        metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::Token.into()]);
1134        metadata.clone().validate().unwrap();
1135
1136        // none - Ok
1137        metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::None.into()]);
1138        metadata.clone().validate().unwrap();
1139
1140        // other grant_types
1141        metadata.grant_types = Some(vec![GrantType::RefreshToken, GrantType::ClientCredentials]);
1142        // code - Err
1143        let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::Code.into();
1144        metadata.response_types = Some(vec![response_type.clone()]);
1145        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1146        assert_eq!(res, response_type);
1147
1148        // code id_token - Err
1149        let response_type: ResponseType =
1150            OAuthAuthorizationEndpointResponseType::CodeIdToken.into();
1151        metadata.response_types = Some(vec![response_type.clone()]);
1152        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1153        assert_eq!(res, response_type);
1154
1155        // code id_token token - Err
1156        let response_type: ResponseType =
1157            OAuthAuthorizationEndpointResponseType::CodeIdTokenToken.into();
1158        metadata.response_types = Some(vec![response_type.clone()]);
1159        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1160        assert_eq!(res, response_type);
1161
1162        // code token - Err
1163        let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::CodeToken.into();
1164        metadata.response_types = Some(vec![response_type.clone()]);
1165        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1166        assert_eq!(res, response_type);
1167
1168        // id_token - Err
1169        let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::IdToken.into();
1170        metadata.response_types = Some(vec![response_type.clone()]);
1171        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1172        assert_eq!(res, response_type);
1173
1174        // id_token token - Err
1175        let response_type: ResponseType =
1176            OAuthAuthorizationEndpointResponseType::IdTokenToken.into();
1177        metadata.response_types = Some(vec![response_type.clone()]);
1178        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1179        assert_eq!(res, response_type);
1180
1181        // token - Err
1182        let response_type: ResponseType = OAuthAuthorizationEndpointResponseType::Token.into();
1183        metadata.response_types = Some(vec![response_type.clone()]);
1184        let res = assert_matches!(metadata.clone().validate(), Err(ClientMetadataVerificationError::IncoherentResponseType(res)) => res);
1185        assert_eq!(res, response_type);
1186
1187        // none - Ok
1188        metadata.response_types = Some(vec![OAuthAuthorizationEndpointResponseType::None.into()]);
1189        metadata.validate().unwrap();
1190    }
1191
1192    #[test]
1193    fn validate_jwks() {
1194        let mut metadata = valid_client_metadata();
1195
1196        // Ok - jwks_uri is set
1197        metadata.jwks_uri = Some(Url::parse("http://localhost/jwks").unwrap());
1198        metadata.clone().validate().unwrap();
1199
1200        // Err - Both are set
1201        metadata.jwks = Some(jwks());
1202        assert_matches!(
1203            metadata.clone().validate(),
1204            Err(ClientMetadataVerificationError::JwksUriAndJwksMutuallyExclusive)
1205        );
1206
1207        // Ok - jwks is set
1208        metadata.jwks_uri = None;
1209        metadata.validate().unwrap();
1210    }
1211
1212    #[test]
1213    fn validate_sector_identifier_uri() {
1214        let mut metadata = valid_client_metadata();
1215
1216        // Err - Non-https URL
1217        let identifier_uri = Url::parse("http://localhost/").unwrap();
1218        metadata.sector_identifier_uri = Some(identifier_uri.clone());
1219        let (field, url) = assert_matches!(
1220            metadata.clone().validate(),
1221            Err(ClientMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1222        );
1223        assert_eq!(field, "sector_identifier_uri");
1224        assert_eq!(url, identifier_uri);
1225
1226        // Ok - https URL
1227        metadata.sector_identifier_uri = Some(Url::parse("https://localhost/").unwrap());
1228        metadata.validate().unwrap();
1229    }
1230
1231    #[test]
1232    fn validate_token_endpoint_auth_method() {
1233        let mut metadata = valid_client_metadata();
1234
1235        // Err - token_endpoint_auth_signing_alg is none
1236        metadata.token_endpoint_auth_signing_alg = Some(JsonWebSignatureAlg::None);
1237        let field = assert_matches!(
1238            metadata.clone().validate(),
1239            Err(ClientMetadataVerificationError::UnauthorizedSigningAlgNone(field)) => field
1240        );
1241        assert_eq!(field, "token_endpoint");
1242
1243        // private_key_jwt
1244        metadata.token_endpoint_auth_method = Some(OAuthClientAuthenticationMethod::PrivateKeyJwt);
1245        metadata.token_endpoint_auth_signing_alg = Some(JsonWebSignatureAlg::Rs256);
1246
1247        // Err - No JWKS
1248        assert_matches!(
1249            metadata.clone().validate(),
1250            Err(ClientMetadataVerificationError::MissingJwksForTokenMethod)
1251        );
1252
1253        // Ok - jwks_uri
1254        metadata.jwks_uri = Some(Url::parse("https://localhost/jwks").unwrap());
1255        metadata.clone().validate().unwrap();
1256
1257        // Ok - jwks
1258        metadata.jwks_uri = None;
1259        metadata.jwks = Some(jwks());
1260        metadata.clone().validate().unwrap();
1261
1262        // Err - No token_endpoint_auth_signing_alg
1263        metadata.token_endpoint_auth_signing_alg = None;
1264        let field = assert_matches!(
1265            metadata.clone().validate(),
1266            Err(ClientMetadataVerificationError::MissingAuthSigningAlg(field)) => field
1267        );
1268        assert_eq!(field, "token_endpoint");
1269
1270        // client_secret_jwt
1271        metadata.token_endpoint_auth_method =
1272            Some(OAuthClientAuthenticationMethod::ClientSecretJwt);
1273        metadata.jwks = None;
1274
1275        // Err - No token_endpoint_auth_signing_alg
1276        let field = assert_matches!(
1277            metadata.clone().validate(),
1278            Err(ClientMetadataVerificationError::MissingAuthSigningAlg(field)) => field
1279        );
1280        assert_eq!(field, "token_endpoint");
1281
1282        // Ok - Has token_endpoint_auth_signing_alg
1283        metadata.token_endpoint_auth_signing_alg = Some(JsonWebSignatureAlg::Rs256);
1284        metadata.validate().unwrap();
1285    }
1286
1287    #[test]
1288    fn validate_id_token_signed_response_alg() {
1289        let mut metadata = valid_client_metadata();
1290        metadata.id_token_signed_response_alg = Some(JsonWebSignatureAlg::None);
1291        metadata.grant_types = Some(vec![GrantType::AuthorizationCode, GrantType::Implicit]);
1292
1293        // Err - code id_token
1294        metadata.response_types = Some(vec![
1295            OAuthAuthorizationEndpointResponseType::CodeIdToken.into(),
1296        ]);
1297        assert_matches!(
1298            metadata.clone().validate(),
1299            Err(ClientMetadataVerificationError::IdTokenSigningAlgNone)
1300        );
1301
1302        // Err - code id_token token
1303        metadata.response_types = Some(vec![
1304            OAuthAuthorizationEndpointResponseType::CodeIdTokenToken.into(),
1305        ]);
1306        assert_matches!(
1307            metadata.clone().validate(),
1308            Err(ClientMetadataVerificationError::IdTokenSigningAlgNone)
1309        );
1310
1311        // Err - id_token
1312        metadata.response_types =
1313            Some(vec![OAuthAuthorizationEndpointResponseType::IdToken.into()]);
1314        assert_matches!(
1315            metadata.clone().validate(),
1316            Err(ClientMetadataVerificationError::IdTokenSigningAlgNone)
1317        );
1318
1319        // Err - id_token token
1320        metadata.response_types = Some(vec![
1321            OAuthAuthorizationEndpointResponseType::IdTokenToken.into(),
1322        ]);
1323        assert_matches!(
1324            metadata.clone().validate(),
1325            Err(ClientMetadataVerificationError::IdTokenSigningAlgNone)
1326        );
1327
1328        // Ok - Other response types
1329        metadata.response_types = Some(vec![
1330            OAuthAuthorizationEndpointResponseType::Code.into(),
1331            OAuthAuthorizationEndpointResponseType::CodeToken.into(),
1332            OAuthAuthorizationEndpointResponseType::Token.into(),
1333            OAuthAuthorizationEndpointResponseType::None.into(),
1334        ]);
1335        metadata.validate().unwrap();
1336    }
1337
1338    #[test]
1339    fn validate_id_token_encrypted_response() {
1340        let mut metadata = valid_client_metadata();
1341        metadata.id_token_encrypted_response_enc = Some(JsonWebEncryptionEnc::A128CbcHs256);
1342
1343        // Err - No id_token_encrypted_response_alg
1344        let field = assert_matches!(
1345            metadata.clone().validate(),
1346            Err(ClientMetadataVerificationError::MissingEncryptionAlg(field)) => field
1347        );
1348        assert_eq!(field, "id_token");
1349
1350        // Ok - Has id_token_encrypted_response_alg
1351        metadata.id_token_encrypted_response_alg = Some(JsonWebEncryptionAlg::RsaOaep);
1352        metadata.validate().unwrap();
1353    }
1354
1355    #[test]
1356    fn validate_userinfo_encrypted_response() {
1357        let mut metadata = valid_client_metadata();
1358        metadata.userinfo_encrypted_response_enc = Some(JsonWebEncryptionEnc::A128CbcHs256);
1359
1360        // Err - No userinfo_encrypted_response_alg
1361        let field = assert_matches!(
1362            metadata.clone().validate(),
1363            Err(ClientMetadataVerificationError::MissingEncryptionAlg(field)) => field
1364        );
1365        assert_eq!(field, "userinfo");
1366
1367        // Ok - Has userinfo_encrypted_response_alg
1368        metadata.userinfo_encrypted_response_alg = Some(JsonWebEncryptionAlg::RsaOaep);
1369        metadata.validate().unwrap();
1370    }
1371
1372    #[test]
1373    fn validate_request_object_encryption() {
1374        let mut metadata = valid_client_metadata();
1375        metadata.request_object_encryption_enc = Some(JsonWebEncryptionEnc::A128CbcHs256);
1376
1377        // Err - No request_object_encryption_alg
1378        let field = assert_matches!(
1379            metadata.clone().validate(),
1380            Err(ClientMetadataVerificationError::MissingEncryptionAlg(field)) => field
1381        );
1382        assert_eq!(field, "request_object");
1383
1384        // Ok - Has request_object_encryption_alg
1385        metadata.request_object_encryption_alg = Some(JsonWebEncryptionAlg::RsaOaep);
1386        metadata.validate().unwrap();
1387    }
1388
1389    #[test]
1390    fn validate_initiate_login_uri() {
1391        let mut metadata = valid_client_metadata();
1392
1393        // Err - Non-https URL
1394        let initiate_uri = Url::parse("http://localhost/").unwrap();
1395        metadata.initiate_login_uri = Some(initiate_uri.clone());
1396        let (field, url) = assert_matches!(
1397            metadata.clone().validate(),
1398            Err(ClientMetadataVerificationError::UrlNonHttpsScheme(field, url)) => (field, url)
1399        );
1400        assert_eq!(field, "initiate_login_uri");
1401        assert_eq!(url, initiate_uri);
1402
1403        // Ok - https URL
1404        metadata.initiate_login_uri = Some(Url::parse("https://localhost/").unwrap());
1405        metadata.validate().unwrap();
1406    }
1407
1408    #[test]
1409    fn validate_introspection_encrypted_response() {
1410        let mut metadata = valid_client_metadata();
1411        metadata.introspection_encrypted_response_enc = Some(JsonWebEncryptionEnc::A128CbcHs256);
1412
1413        // Err - No introspection_encrypted_response_alg
1414        let field = assert_matches!(
1415            metadata.clone().validate(),
1416            Err(ClientMetadataVerificationError::MissingEncryptionAlg(field)) => field
1417        );
1418        assert_eq!(field, "introspection");
1419
1420        // Ok - Has introspection_encrypted_response_alg
1421        metadata.introspection_encrypted_response_alg = Some(JsonWebEncryptionAlg::RsaOaep);
1422        metadata.validate().unwrap();
1423    }
1424}