1use log::debug;
2use serde::{Deserialize, Serialize};
3use super::errors::WellKnownApplicationsError;
4use super::http_client::default_client;
5use super::CoreProviderMetadata;
6
7#[derive(Serialize, Deserialize, Debug, Clone)]
8#[serde(rename_all = "snake_case")]
9pub enum ApplicationType {
10 Server,
11 ServerMatrix,
12 ServerDiscourse,
13 ServerOauth2,
14 ServerBlockchain,
15 Client,
16 ClientOidc,
17 ClientKyc,
18}
19
20fn hostname_to_url(hostname: &str, issuer_url: &str) -> String {
21 if issuer_url.starts_with("https://") {
22 format!("https://{}", hostname)
23 } else {
24 format!("http://{}", hostname)
25 }
26}
27
28#[derive(Serialize, Deserialize, Debug, Clone)]
29pub struct ApplicationsWellKnown {
30 pub id: String,
31 pub r#type: ApplicationType,
32 pub name: String,
33 pub description: Option<String>,
34 pub ipv4: Option<String>,
35 pub hostname: String,
36 #[serde(alias = "clientIdentifier")]
37 pub client_identifier: Option<String>,
38 #[serde(alias = "chainId")]
39 pub chain_id: Option<String>,
40 #[serde(alias = "contractCheckBalance")]
41 pub contract_check_balance: Option<String>,
42 #[serde(alias = "isHidden")]
43 pub is_hidden: Option<bool>,
44}
45
46impl ApplicationsWellKnown {
50 pub fn url(&self, issuer_url: &str) -> String {
51 hostname_to_url(&self.hostname, issuer_url)
52 }
53}
54
55#[derive(Serialize, Deserialize, Debug, Clone)]
56pub struct ApplicationsWellKnownResponse {
57 applications: Vec<ApplicationsWellKnown>,
58 total: u64,
59}
60
61pub async fn get_applications_well_known(
63 provider_metadata: &CoreProviderMetadata,
64 device_access_token: &str,
65) -> Result<ApplicationsWellKnownResponse, WellKnownApplicationsError> {
66 let client = default_client()?;
67 let url = provider_metadata
68 .issuer()
69 .trim_end_matches("/oidc")
70 .to_string()
71 + "/applications/.well-known?limit=1000";
72 let response = client
73 .get(url)
74 .header("Authorization", format!("Bearer {}", device_access_token))
75 .send()
76 .await?
77 .error_for_status()?
78 .json::<ApplicationsWellKnownResponse>()
79 .await?;
80
81 Ok(response)
82}
83
84pub async fn get_well_known_application_by_client_identifier(
86 provider_metadata: &CoreProviderMetadata,
87 device_access_token: &str,
88 client_identifier: &str,
89) -> Result<ApplicationsWellKnown, WellKnownApplicationsError> {
90 let response = get_applications_well_known(provider_metadata, device_access_token).await?;
91 let filtered_applications: Vec<ApplicationsWellKnown> = response
92 .applications
93 .clone()
94 .into_iter()
95 .filter(|app| {
96 app.client_identifier
97 .as_ref()
98 .map_or(false, |id| id == client_identifier)
99 })
100 .collect();
101 if filtered_applications.is_empty() {
102 debug!(
103 "No application found with client_identifier '{}'. Available applications: {:#?}",
104 client_identifier, response.applications
105 );
106 return Err(WellKnownApplicationsError::NotFound);
107 }
108
109 Ok(filtered_applications[0].clone())
110}