You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-29 22:01:14 +03:00
Allow to validate client metadata
According to OpenID Connect Dynamic Client Registration Spec 1.0. Introduce VerifiedClientMetadata.
This commit is contained in:
committed by
Quentin Gliech
parent
a543af4de3
commit
e202c3dd6d
@ -15,6 +15,7 @@
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::all, clippy::str_to_string, rustdoc::broken_intra_doc_links)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
use mas_iana::oauth::OAuthAuthorizationEndpointResponseType;
|
||||
|
||||
|
@ -778,7 +778,7 @@ pub enum ProviderMetadataVerificationError {
|
||||
/// The given endpoint is missing auth signing algorithm values, but they
|
||||
/// are required because it supports at least one of the `client_secret_jwt`
|
||||
/// or `private_key_jwt` authentication methods.
|
||||
#[error("{0} auth signing algorithm values contain `none`")]
|
||||
#[error("{0} missing auth signing algorithm values")]
|
||||
MissingAuthSigningAlgValues(&'static str),
|
||||
|
||||
/// `none` is in the given endpoint's signing algorithm values, but is not
|
||||
|
@ -1,168 +0,0 @@
|
||||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use mas_iana::{
|
||||
jose::{JsonWebEncryptionAlg, JsonWebSignatureAlg},
|
||||
oauth::{OAuthAuthorizationEndpointResponseType, OAuthClientAuthenticationMethod},
|
||||
};
|
||||
use mas_jose::JsonWebKeySet;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::{serde_as, skip_serializing_none, DurationSeconds, TimestampSeconds};
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
oidc::{ApplicationType, SubjectType},
|
||||
requests::GrantType,
|
||||
};
|
||||
|
||||
fn default_response_types() -> Vec<OAuthAuthorizationEndpointResponseType> {
|
||||
vec![OAuthAuthorizationEndpointResponseType::Code]
|
||||
}
|
||||
|
||||
fn default_grant_types() -> Vec<GrantType> {
|
||||
vec![GrantType::AuthorizationCode]
|
||||
}
|
||||
|
||||
const fn default_application_type() -> ApplicationType {
|
||||
ApplicationType::Web
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[skip_serializing_none]
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||
pub struct ClientMetadata {
|
||||
pub redirect_uris: Vec<Url>,
|
||||
|
||||
#[serde(default = "default_response_types")]
|
||||
pub response_types: Vec<OAuthAuthorizationEndpointResponseType>,
|
||||
|
||||
#[serde(default = "default_grant_types")]
|
||||
pub grant_types: Vec<GrantType>,
|
||||
|
||||
#[serde(default = "default_application_type")]
|
||||
pub application_type: ApplicationType,
|
||||
|
||||
#[serde(default)]
|
||||
pub contacts: Vec<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub client_name: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub logo_uri: Option<Url>,
|
||||
|
||||
#[serde(default)]
|
||||
pub client_uri: Option<Url>,
|
||||
|
||||
#[serde(default)]
|
||||
pub policy_uri: Option<Url>,
|
||||
|
||||
#[serde(default)]
|
||||
pub tos_uri: Option<Url>,
|
||||
|
||||
#[serde(default)]
|
||||
pub jwks_uri: Option<Url>,
|
||||
|
||||
#[serde(default)]
|
||||
pub jwks: Option<JsonWebKeySet>,
|
||||
|
||||
#[serde(default)]
|
||||
pub sector_identifier_uri: Option<Url>,
|
||||
|
||||
#[serde(default)]
|
||||
pub subject_type: Option<SubjectType>,
|
||||
|
||||
#[serde(default)]
|
||||
pub token_endpoint_auth_method: Option<OAuthClientAuthenticationMethod>,
|
||||
|
||||
#[serde(default)]
|
||||
pub token_endpoint_auth_signing_alg: Option<JsonWebSignatureAlg>,
|
||||
|
||||
#[serde(default)]
|
||||
pub id_token_signed_response_alg: Option<JsonWebSignatureAlg>,
|
||||
|
||||
#[serde(default)]
|
||||
pub id_token_encrypted_response_alg: Option<JsonWebEncryptionAlg>,
|
||||
|
||||
#[serde(default)]
|
||||
pub id_token_encrypted_response_enc: Option<JsonWebEncryptionAlg>,
|
||||
|
||||
#[serde(default)]
|
||||
pub userinfo_signed_response_alg: Option<JsonWebSignatureAlg>,
|
||||
|
||||
#[serde(default)]
|
||||
pub userinfo_encrypted_response_alg: Option<JsonWebEncryptionAlg>,
|
||||
|
||||
#[serde(default)]
|
||||
pub userinfo_encrypted_response_enc: Option<JsonWebEncryptionAlg>,
|
||||
|
||||
#[serde(default)]
|
||||
pub request_object_signing_alg: Option<JsonWebSignatureAlg>,
|
||||
|
||||
#[serde(default)]
|
||||
pub request_object_encryption_alg: Option<JsonWebEncryptionAlg>,
|
||||
|
||||
#[serde(default)]
|
||||
pub request_object_encryption_enc: Option<JsonWebEncryptionAlg>,
|
||||
|
||||
#[serde(default)]
|
||||
#[serde_as(as = "Option<DurationSeconds<i64>>")]
|
||||
pub default_max_age: Option<Duration>,
|
||||
|
||||
#[serde(default)]
|
||||
pub require_auth_time: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub default_acr_values: Vec<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub initiate_login_uri: Option<Url>,
|
||||
|
||||
#[serde(default)]
|
||||
pub request_uris: Option<Vec<Url>>,
|
||||
|
||||
#[serde(default)]
|
||||
pub require_signed_request_object: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub require_pushed_authorization_requests: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub introspection_signed_response_alg: Option<JsonWebSignatureAlg>,
|
||||
|
||||
#[serde(default)]
|
||||
pub introspection_encrypted_response_alg: Option<JsonWebEncryptionAlg>,
|
||||
|
||||
#[serde(default)]
|
||||
pub introspection_encrypted_response_enc: Option<JsonWebEncryptionAlg>,
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[skip_serializing_none]
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||
pub struct ClientRegistrationResponse {
|
||||
pub client_id: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub client_secret: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
#[serde_as(as = "Option<TimestampSeconds<i64>>")]
|
||||
pub client_id_issued_at: Option<DateTime<Utc>>,
|
||||
|
||||
#[serde(default)]
|
||||
#[serde_as(as = "Option<TimestampSeconds<i64>>")]
|
||||
pub client_secret_expires_at: Option<DateTime<Utc>>,
|
||||
}
|
469
crates/oauth2-types/src/registration/client_metadata_serde.rs
Normal file
469
crates/oauth2-types/src/registration/client_metadata_serde.rs
Normal file
@ -0,0 +1,469 @@
|
||||
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
use chrono::Duration;
|
||||
use language_tags::LanguageTag;
|
||||
use mas_iana::{
|
||||
jose::{JsonWebEncryptionAlg, JsonWebEncryptionEnc, JsonWebSignatureAlg},
|
||||
oauth::{OAuthAuthorizationEndpointResponseType, OAuthClientAuthenticationMethod},
|
||||
};
|
||||
use mas_jose::JsonWebKeySet;
|
||||
use serde::{
|
||||
de::{DeserializeOwned, Error},
|
||||
ser::SerializeMap,
|
||||
Deserialize, Serialize,
|
||||
};
|
||||
use serde_json::Value;
|
||||
use serde_with::{serde_as, skip_serializing_none, DurationSeconds};
|
||||
use url::Url;
|
||||
|
||||
use super::{ClientMetadata, Localized, VerifiedClientMetadata};
|
||||
use crate::{
|
||||
oidc::{ApplicationType, SubjectType},
|
||||
requests::GrantType,
|
||||
};
|
||||
|
||||
impl<T> Localized<T> {
|
||||
fn serialize<M>(&self, map: &mut M, field_name: &str) -> Result<(), M::Error>
|
||||
where
|
||||
M: SerializeMap,
|
||||
T: Serialize,
|
||||
{
|
||||
map.serialize_entry(field_name, &self.non_localized)?;
|
||||
|
||||
for (lang, localized) in &self.localized {
|
||||
map.serialize_entry(&format!("{field_name}#{lang}"), localized)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(
|
||||
map: &mut HashMap<String, HashMap<Option<LanguageTag>, Value>>,
|
||||
field_name: &'static str,
|
||||
) -> Result<Option<Self>, serde_json::Error>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let map = match map.remove(field_name) {
|
||||
Some(map) => map,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
let mut non_localized = None;
|
||||
let mut localized = HashMap::with_capacity(map.len() - 1);
|
||||
|
||||
for (k, v) in map {
|
||||
let value = serde_json::from_value(v)?;
|
||||
|
||||
if let Some(lang) = k {
|
||||
localized.insert(lang, value);
|
||||
} else {
|
||||
non_localized = Some(value);
|
||||
}
|
||||
}
|
||||
|
||||
let non_localized = non_localized.ok_or_else(|| {
|
||||
serde_json::Error::custom(format!(
|
||||
"missing non-localized variant of field '{field_name}'"
|
||||
))
|
||||
})?;
|
||||
|
||||
Ok(Some(Localized {
|
||||
non_localized,
|
||||
localized,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[skip_serializing_none]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ClientMetadataSerdeHelper {
|
||||
redirect_uris: Option<Vec<Url>>,
|
||||
response_types: Option<Vec<OAuthAuthorizationEndpointResponseType>>,
|
||||
grant_types: Option<Vec<GrantType>>,
|
||||
application_type: Option<ApplicationType>,
|
||||
contacts: Option<Vec<String>>,
|
||||
jwks_uri: Option<Url>,
|
||||
jwks: Option<JsonWebKeySet>,
|
||||
sector_identifier_uri: Option<Url>,
|
||||
subject_type: Option<SubjectType>,
|
||||
token_endpoint_auth_method: Option<OAuthClientAuthenticationMethod>,
|
||||
token_endpoint_auth_signing_alg: Option<JsonWebSignatureAlg>,
|
||||
id_token_signed_response_alg: Option<JsonWebSignatureAlg>,
|
||||
id_token_encrypted_response_alg: Option<JsonWebEncryptionAlg>,
|
||||
id_token_encrypted_response_enc: Option<JsonWebEncryptionEnc>,
|
||||
userinfo_signed_response_alg: Option<JsonWebSignatureAlg>,
|
||||
userinfo_encrypted_response_alg: Option<JsonWebEncryptionAlg>,
|
||||
userinfo_encrypted_response_enc: Option<JsonWebEncryptionEnc>,
|
||||
request_object_signing_alg: Option<JsonWebSignatureAlg>,
|
||||
request_object_encryption_alg: Option<JsonWebEncryptionAlg>,
|
||||
request_object_encryption_enc: Option<JsonWebEncryptionEnc>,
|
||||
#[serde_as(as = "Option<DurationSeconds<i64>>")]
|
||||
default_max_age: Option<Duration>,
|
||||
require_auth_time: Option<bool>,
|
||||
default_acr_values: Option<Vec<String>>,
|
||||
initiate_login_uri: Option<Url>,
|
||||
request_uris: Option<Vec<Url>>,
|
||||
require_signed_request_object: Option<bool>,
|
||||
require_pushed_authorization_requests: Option<bool>,
|
||||
introspection_signed_response_alg: Option<JsonWebSignatureAlg>,
|
||||
introspection_encrypted_response_alg: Option<JsonWebEncryptionAlg>,
|
||||
introspection_encrypted_response_enc: Option<JsonWebEncryptionEnc>,
|
||||
#[serde(flatten)]
|
||||
extra: ClientMetadataLocalizedFields,
|
||||
}
|
||||
|
||||
impl From<VerifiedClientMetadata> for ClientMetadataSerdeHelper {
|
||||
fn from(metadata: VerifiedClientMetadata) -> Self {
|
||||
let VerifiedClientMetadata {
|
||||
inner:
|
||||
ClientMetadata {
|
||||
redirect_uris,
|
||||
response_types,
|
||||
grant_types,
|
||||
application_type,
|
||||
contacts,
|
||||
client_name,
|
||||
logo_uri,
|
||||
client_uri,
|
||||
policy_uri,
|
||||
tos_uri,
|
||||
jwks_uri,
|
||||
jwks,
|
||||
sector_identifier_uri,
|
||||
subject_type,
|
||||
token_endpoint_auth_method,
|
||||
token_endpoint_auth_signing_alg,
|
||||
id_token_signed_response_alg,
|
||||
id_token_encrypted_response_alg,
|
||||
id_token_encrypted_response_enc,
|
||||
userinfo_signed_response_alg,
|
||||
userinfo_encrypted_response_alg,
|
||||
userinfo_encrypted_response_enc,
|
||||
request_object_signing_alg,
|
||||
request_object_encryption_alg,
|
||||
request_object_encryption_enc,
|
||||
default_max_age,
|
||||
require_auth_time,
|
||||
default_acr_values,
|
||||
initiate_login_uri,
|
||||
request_uris,
|
||||
require_signed_request_object,
|
||||
require_pushed_authorization_requests,
|
||||
introspection_signed_response_alg,
|
||||
introspection_encrypted_response_alg,
|
||||
introspection_encrypted_response_enc,
|
||||
},
|
||||
} = metadata;
|
||||
|
||||
ClientMetadataSerdeHelper {
|
||||
redirect_uris,
|
||||
response_types,
|
||||
grant_types,
|
||||
application_type,
|
||||
contacts,
|
||||
jwks_uri,
|
||||
jwks,
|
||||
sector_identifier_uri,
|
||||
subject_type,
|
||||
token_endpoint_auth_method,
|
||||
token_endpoint_auth_signing_alg,
|
||||
id_token_signed_response_alg,
|
||||
id_token_encrypted_response_alg,
|
||||
id_token_encrypted_response_enc,
|
||||
userinfo_signed_response_alg,
|
||||
userinfo_encrypted_response_alg,
|
||||
userinfo_encrypted_response_enc,
|
||||
request_object_signing_alg,
|
||||
request_object_encryption_alg,
|
||||
request_object_encryption_enc,
|
||||
default_max_age,
|
||||
require_auth_time,
|
||||
default_acr_values,
|
||||
initiate_login_uri,
|
||||
request_uris,
|
||||
require_signed_request_object,
|
||||
require_pushed_authorization_requests,
|
||||
introspection_signed_response_alg,
|
||||
introspection_encrypted_response_alg,
|
||||
introspection_encrypted_response_enc,
|
||||
extra: ClientMetadataLocalizedFields {
|
||||
client_name,
|
||||
logo_uri,
|
||||
client_uri,
|
||||
policy_uri,
|
||||
tos_uri,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ClientMetadataSerdeHelper> for ClientMetadata {
|
||||
fn from(metadata: ClientMetadataSerdeHelper) -> Self {
|
||||
let ClientMetadataSerdeHelper {
|
||||
redirect_uris,
|
||||
response_types,
|
||||
grant_types,
|
||||
application_type,
|
||||
contacts,
|
||||
jwks_uri,
|
||||
jwks,
|
||||
sector_identifier_uri,
|
||||
subject_type,
|
||||
token_endpoint_auth_method,
|
||||
token_endpoint_auth_signing_alg,
|
||||
id_token_signed_response_alg,
|
||||
id_token_encrypted_response_alg,
|
||||
id_token_encrypted_response_enc,
|
||||
userinfo_signed_response_alg,
|
||||
userinfo_encrypted_response_alg,
|
||||
userinfo_encrypted_response_enc,
|
||||
request_object_signing_alg,
|
||||
request_object_encryption_alg,
|
||||
request_object_encryption_enc,
|
||||
default_max_age,
|
||||
require_auth_time,
|
||||
default_acr_values,
|
||||
initiate_login_uri,
|
||||
request_uris,
|
||||
require_signed_request_object,
|
||||
require_pushed_authorization_requests,
|
||||
introspection_signed_response_alg,
|
||||
introspection_encrypted_response_alg,
|
||||
introspection_encrypted_response_enc,
|
||||
extra:
|
||||
ClientMetadataLocalizedFields {
|
||||
client_name,
|
||||
logo_uri,
|
||||
client_uri,
|
||||
policy_uri,
|
||||
tos_uri,
|
||||
},
|
||||
} = metadata;
|
||||
|
||||
ClientMetadata {
|
||||
redirect_uris,
|
||||
response_types,
|
||||
grant_types,
|
||||
application_type,
|
||||
contacts,
|
||||
client_name,
|
||||
logo_uri,
|
||||
client_uri,
|
||||
policy_uri,
|
||||
tos_uri,
|
||||
jwks_uri,
|
||||
jwks,
|
||||
sector_identifier_uri,
|
||||
subject_type,
|
||||
token_endpoint_auth_method,
|
||||
token_endpoint_auth_signing_alg,
|
||||
id_token_signed_response_alg,
|
||||
id_token_encrypted_response_alg,
|
||||
id_token_encrypted_response_enc,
|
||||
userinfo_signed_response_alg,
|
||||
userinfo_encrypted_response_alg,
|
||||
userinfo_encrypted_response_enc,
|
||||
request_object_signing_alg,
|
||||
request_object_encryption_alg,
|
||||
request_object_encryption_enc,
|
||||
default_max_age,
|
||||
require_auth_time,
|
||||
default_acr_values,
|
||||
initiate_login_uri,
|
||||
request_uris,
|
||||
require_signed_request_object,
|
||||
require_pushed_authorization_requests,
|
||||
introspection_signed_response_alg,
|
||||
introspection_encrypted_response_alg,
|
||||
introspection_encrypted_response_enc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ClientMetadataLocalizedFields {
|
||||
client_name: Option<Localized<String>>,
|
||||
logo_uri: Option<Localized<Url>>,
|
||||
client_uri: Option<Localized<Url>>,
|
||||
policy_uri: Option<Localized<Url>>,
|
||||
tos_uri: Option<Localized<Url>>,
|
||||
}
|
||||
|
||||
impl Serialize for ClientMetadataLocalizedFields {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
|
||||
if let Some(client_name) = &self.client_name {
|
||||
client_name.serialize(&mut map, "client_name")?;
|
||||
}
|
||||
|
||||
if let Some(logo_uri) = &self.logo_uri {
|
||||
logo_uri.serialize(&mut map, "logo_uri")?;
|
||||
}
|
||||
|
||||
if let Some(client_uri) = &self.client_uri {
|
||||
client_uri.serialize(&mut map, "client_uri")?;
|
||||
}
|
||||
|
||||
if let Some(policy_uri) = &self.policy_uri {
|
||||
policy_uri.serialize(&mut map, "policy_uri")?;
|
||||
}
|
||||
|
||||
if let Some(tos_uri) = &self.tos_uri {
|
||||
tos_uri.serialize(&mut map, "tos_uri")?;
|
||||
}
|
||||
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ClientMetadataLocalizedFields {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let map = HashMap::<Cow<'de, str>, Value>::deserialize(deserializer)?;
|
||||
let mut new_map: HashMap<String, HashMap<Option<LanguageTag>, Value>> = HashMap::new();
|
||||
|
||||
for (k, v) in map {
|
||||
let (prefix, lang) = if let Some((prefix, lang)) = k.split_once('#') {
|
||||
let lang = LanguageTag::parse(lang).map_err(|_| {
|
||||
D::Error::invalid_value(serde::de::Unexpected::Str(lang), &"language tag")
|
||||
})?;
|
||||
(prefix.to_owned(), Some(lang))
|
||||
} else {
|
||||
(k.into_owned(), None)
|
||||
};
|
||||
|
||||
new_map.entry(prefix).or_default().insert(lang, v);
|
||||
}
|
||||
|
||||
let client_name =
|
||||
Localized::deserialize(&mut new_map, "client_name").map_err(D::Error::custom)?;
|
||||
|
||||
let logo_uri =
|
||||
Localized::deserialize(&mut new_map, "logo_uri").map_err(D::Error::custom)?;
|
||||
|
||||
let client_uri =
|
||||
Localized::deserialize(&mut new_map, "client_uri").map_err(D::Error::custom)?;
|
||||
|
||||
let policy_uri =
|
||||
Localized::deserialize(&mut new_map, "policy_uri").map_err(D::Error::custom)?;
|
||||
|
||||
let tos_uri = Localized::deserialize(&mut new_map, "tos_uri").map_err(D::Error::custom)?;
|
||||
|
||||
Ok(Self {
|
||||
client_name,
|
||||
logo_uri,
|
||||
client_uri,
|
||||
policy_uri,
|
||||
tos_uri,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn deserialize_localized_fields() {
|
||||
let metadata = serde_json::json!({
|
||||
"redirect_uris": ["http://localhost/oidc"],
|
||||
"client_name": "Postbox",
|
||||
"client_name#fr": "Boîte à lettres",
|
||||
"client_uri": "https://localhost/",
|
||||
"client_uri#fr": "https://localhost/fr",
|
||||
"client_uri#de": "https://localhost/de",
|
||||
});
|
||||
|
||||
let metadata: ClientMetadata = serde_json::from_value(metadata).unwrap();
|
||||
|
||||
let name = metadata.client_name.unwrap();
|
||||
assert_eq!(name.non_localized(), "Postbox");
|
||||
assert_eq!(
|
||||
name.get(Some(&LanguageTag::parse("fr").unwrap())).unwrap(),
|
||||
"Boîte à lettres"
|
||||
);
|
||||
assert_eq!(name.get(Some(&LanguageTag::parse("de").unwrap())), None);
|
||||
|
||||
let client_uri = metadata.client_uri.unwrap();
|
||||
assert_eq!(client_uri.non_localized().as_ref(), "https://localhost/");
|
||||
assert_eq!(
|
||||
client_uri
|
||||
.get(Some(&LanguageTag::parse("fr").unwrap()))
|
||||
.unwrap()
|
||||
.as_ref(),
|
||||
"https://localhost/fr"
|
||||
);
|
||||
assert_eq!(
|
||||
client_uri
|
||||
.get(Some(&LanguageTag::parse("de").unwrap()))
|
||||
.unwrap()
|
||||
.as_ref(),
|
||||
"https://localhost/de"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_localized_fields() {
|
||||
let client_name = Localized::new(
|
||||
"Postbox".to_owned(),
|
||||
[(
|
||||
LanguageTag::parse("fr").unwrap(),
|
||||
"Boîte à lettres".to_owned(),
|
||||
)],
|
||||
);
|
||||
let client_uri = Localized::new(
|
||||
Url::parse("https://localhost").unwrap(),
|
||||
[
|
||||
(
|
||||
LanguageTag::parse("fr").unwrap(),
|
||||
Url::parse("https://localhost/fr").unwrap(),
|
||||
),
|
||||
(
|
||||
LanguageTag::parse("de").unwrap(),
|
||||
Url::parse("https://localhost/de").unwrap(),
|
||||
),
|
||||
],
|
||||
);
|
||||
let metadata = ClientMetadata {
|
||||
redirect_uris: Some(vec![Url::parse("http://localhost/oidc").unwrap()]),
|
||||
client_name: Some(client_name),
|
||||
client_uri: Some(client_uri),
|
||||
..Default::default()
|
||||
}
|
||||
.validate()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
serde_json::to_value(metadata).unwrap(),
|
||||
serde_json::json!({
|
||||
"redirect_uris": ["http://localhost/oidc"],
|
||||
"client_name": "Postbox",
|
||||
"client_name#fr": "Boîte à lettres",
|
||||
"client_uri": "https://localhost/",
|
||||
"client_uri#fr": "https://localhost/fr",
|
||||
"client_uri#de": "https://localhost/de",
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
1334
crates/oauth2-types/src/registration/mod.rs
Normal file
1334
crates/oauth2-types/src/registration/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user