use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::{
jwk::{Jwk, Jwks},
types::ClientRegistrationResponse,
};
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ClientMetadata {
pub client_id: String,
pub post_logout_redirect_uri: Option<String>,
pub tls_client_certificate_bound_access_tokens: Option<bool>,
pub authorization_signed_response_alg: Option<String>,
pub id_token_signed_response_alg: Option<String>,
pub id_token_encrypted_response_alg: Option<String>,
pub id_token_encrypted_response_enc: Option<String>,
pub default_max_age: Option<u64>,
pub require_auth_time: Option<bool>,
pub userinfo_signed_response_alg: Option<String>,
pub userinfo_encrypted_response_alg: Option<String>,
pub userinfo_encrypted_response_enc: Option<String>,
pub request_object_signing_alg: Option<String>,
pub request_object_encryption_alg: Option<String>,
pub request_object_encryption_enc: Option<String>,
pub jwks: Option<Jwks>,
pub jwks_uri: Option<String>,
#[serde(flatten, skip_serializing_if = "HashMap::is_empty")]
pub additional_data: HashMap<String, Value>,
}
impl From<ClientRegistrationResponse> for ClientMetadata {
fn from(value: ClientRegistrationResponse) -> Self {
let mut metadata = value.metadata;
Self {
client_id: value.client_id,
post_logout_redirect_uri: take_string(&mut metadata, "post_logout_redirect_uri")
.or_else(|| take_first_string(&mut metadata, "post_logout_redirect_uris")),
tls_client_certificate_bound_access_tokens: take_bool(
&mut metadata,
"tls_client_certificate_bound_access_tokens",
),
authorization_signed_response_alg: take_string(
&mut metadata,
"authorization_signed_response_alg",
),
id_token_signed_response_alg: take_string(
&mut metadata,
"id_token_signed_response_alg",
),
id_token_encrypted_response_alg: take_string(
&mut metadata,
"id_token_encrypted_response_alg",
),
id_token_encrypted_response_enc: take_string(
&mut metadata,
"id_token_encrypted_response_enc",
),
default_max_age: take_u64(&mut metadata, "default_max_age"),
require_auth_time: take_bool(&mut metadata, "require_auth_time"),
userinfo_signed_response_alg: take_string(
&mut metadata,
"userinfo_signed_response_alg",
),
userinfo_encrypted_response_alg: take_string(
&mut metadata,
"userinfo_encrypted_response_alg",
),
userinfo_encrypted_response_enc: take_string(
&mut metadata,
"userinfo_encrypted_response_enc",
),
request_object_signing_alg: take_string(&mut metadata, "request_object_signing_alg"),
request_object_encryption_alg: take_string(
&mut metadata,
"request_object_encryption_alg",
),
request_object_encryption_enc: take_string(
&mut metadata,
"request_object_encryption_enc",
),
jwks: take_jwks(&mut metadata, "jwks"),
jwks_uri: take_string(&mut metadata, "jwks_uri"),
additional_data: metadata,
}
}
}
#[inline]
fn take_string(metadata: &mut HashMap<String, Value>, key: &str) -> Option<String> {
match metadata.remove(key) {
Some(Value::String(value)) => Some(value),
Some(value) => {
metadata.insert(key.to_owned(), value);
None
}
None => None,
}
}
#[inline]
fn take_first_string(metadata: &mut HashMap<String, Value>, key: &str) -> Option<String> {
match metadata.remove(key) {
Some(Value::String(value)) => Some(value),
Some(Value::Array(values)) => {
let mut values = values.into_iter();
match values.next() {
Some(Value::String(value)) => Some(value),
Some(value) => {
let mut original = Vec::with_capacity(values.size_hint().0 + 1);
original.push(value);
original.extend(values);
metadata.insert(key.to_owned(), Value::Array(original));
None
}
None => {
metadata.insert(key.to_owned(), Value::Array(Vec::new()));
None
}
}
}
Some(value) => {
metadata.insert(key.to_owned(), value);
None
}
None => None,
}
}
#[inline]
fn take_bool(metadata: &mut HashMap<String, Value>, key: &str) -> Option<bool> {
match metadata.remove(key) {
Some(Value::Bool(value)) => Some(value),
Some(value) => {
metadata.insert(key.to_owned(), value);
None
}
None => None,
}
}
#[inline]
fn take_u64(metadata: &mut HashMap<String, Value>, key: &str) -> Option<u64> {
match metadata.remove(key) {
Some(Value::Number(value)) => match value.as_u64() {
Some(value) => Some(value),
None => {
metadata.insert(key.to_owned(), Value::Number(value));
None
}
},
Some(value) => {
metadata.insert(key.to_owned(), value);
None
}
None => None,
}
}
#[inline]
fn take_jwks(metadata: &mut HashMap<String, Value>, key: &str) -> Option<Jwks> {
match metadata.remove(key) {
Some(Value::Object(mut object)) => match object.remove("keys") {
Some(Value::Array(values)) => values_to_jwks(values),
Some(value) => {
object.insert("keys".to_owned(), value);
metadata.insert(key.to_owned(), Value::Object(object));
None
}
None => {
metadata.insert(key.to_owned(), Value::Object(object));
None
}
},
Some(value) => {
metadata.insert(key.to_owned(), value);
None
}
None => None,
}
}
#[inline]
fn values_to_jwks(values: Vec<Value>) -> Option<Jwks> {
let mut jwks = Vec::with_capacity(values.len());
for value in values {
match value {
Value::Object(params) => jwks.push(Jwk { params }),
_ => return None,
}
}
Some(Jwks { keys: jwks })
}