oauth2_types/registration/
mod.rs

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