ones_oidc/
well_known.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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>,
}

/**
 * Get the url for the application
 */
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,
}

/// Get the applications well-known
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)
}

/// Get the application well-known by client identifier
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())
}