use crate::errors::WellKnownApplicationsError;
use log::debug;
use openidconnect::core::CoreProviderMetadata;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub enum ApplicationType {
Server,
ServerMatrix,
ServerDiscourse,
ServerOauth2,
ServerBlockchain,
Client,
ClientOidc,
ClientKyc,
}
fn hostname_to_url(hostname: &str, issuer_url: &str) -> String {
if issuer_url.starts_with("https://") {
format!("https://{}", hostname)
} else {
format!("http://{}", hostname)
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ApplicationsWellKnown {
pub id: String,
pub r#type: ApplicationType,
pub name: String,
pub description: Option<String>,
pub ipv4: Option<String>,
pub hostname: String,
#[serde(alias = "clientIdentifier")]
pub client_identifier: Option<String>,
#[serde(alias = "chainId")]
pub chain_id: Option<String>,
#[serde(alias = "contractCheckBalance")]
pub contract_check_balance: Option<String>,
#[serde(alias = "isHidden")]
pub is_hidden: Option<bool>,
}
impl ApplicationsWellKnown {
pub fn url(&self, issuer_url: &str) -> String {
hostname_to_url(&self.hostname, issuer_url)
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ApplicationsWellKnownResponse {
applications: Vec<ApplicationsWellKnown>,
total: u64,
}
pub async fn get_applications_well_known(
provider_metadata: &CoreProviderMetadata,
device_access_token: &str,
) -> Result<ApplicationsWellKnownResponse, WellKnownApplicationsError> {
let client = reqwest::Client::new();
let url = provider_metadata
.issuer()
.trim_end_matches("/oidc")
.to_string()
+ "/applications/.well-known?limit=1000";
let response = client
.get(url)
.header("Authorization", format!("Bearer {}", device_access_token))
.send()
.await?
.error_for_status()?
.json::<ApplicationsWellKnownResponse>()
.await?;
Ok(response)
}
pub async fn get_well_known_application_by_client_identifier(
provider_metadata: &CoreProviderMetadata,
device_access_token: &str,
client_identifier: &str,
) -> Result<ApplicationsWellKnown, WellKnownApplicationsError> {
let response = get_applications_well_known(provider_metadata, device_access_token).await?;
let filtered_applications: Vec<ApplicationsWellKnown> = response
.applications
.clone()
.into_iter()
.filter(|app| {
app.client_identifier
.as_ref()
.map_or(false, |id| id == client_identifier)
})
.collect();
if filtered_applications.is_empty() {
debug!(
"No application found with client_identifier '{}'. Available applications: {:#?}",
client_identifier, response.applications
);
return Err(WellKnownApplicationsError::NotFound);
}
Ok(filtered_applications[0].clone())
}