ones_oidc/
well_known.rs

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
46/**
47 * Get the url for the application
48 */
49impl 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
61/// Get the applications well-known
62pub 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
84/// Get the application well-known by client identifier
85pub 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}