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
110
111
112
113
114
115
use crate::identity::credentials::app_config::AppConfig;
use crate::identity::credentials::application_builder::PublicClientApplicationBuilder;
use crate::identity::{
    Authority, AzureCloudInstance, DeviceCodeCredential, ResourceOwnerPasswordCredential,
    TokenCredentialExecutor,
};
use async_trait::async_trait;
use graph_core::cache::{AsBearer, TokenCache};
use graph_core::identity::{ClientApplication, ForceTokenRefresh};
use graph_error::{AuthExecutionResult, IdentityResult};
use reqwest::Response;
use std::collections::HashMap;
use std::fmt::Debug;
use url::Url;
use uuid::Uuid;

/// Clients incapable of maintaining the confidentiality of their credentials
/// (e.g., clients executing on the device used by the resource owner, such as an
/// installed native application or a web browser-based application), and incapable of
/// secure client authentication via any other means.
///
/// See [Client Types](https://datatracker.ietf.org/doc/html/rfc6749#section-2.1) in the specification.
#[derive(Clone, Debug)]
pub struct PublicClientApplication<Credential> {
    credential: Credential,
}

impl PublicClientApplication<()> {
    pub fn builder(client_id: impl AsRef<str>) -> PublicClientApplicationBuilder {
        PublicClientApplicationBuilder::new(client_id)
    }
}

impl<Credential: Clone + Debug + Send + Sync + TokenCredentialExecutor>
    PublicClientApplication<Credential>
{
    pub(crate) fn new(credential: Credential) -> PublicClientApplication<Credential> {
        PublicClientApplication { credential }
    }

    pub(crate) fn credential(credential: Credential) -> PublicClientApplication<Credential> {
        PublicClientApplication { credential }
    }
}

#[async_trait]
impl<Credential: Clone + Debug + Send + Sync + TokenCache> ClientApplication
    for PublicClientApplication<Credential>
{
    fn get_token_silent(&mut self) -> AuthExecutionResult<String> {
        let token = self.credential.get_token_silent()?;
        Ok(token.as_bearer())
    }

    async fn get_token_silent_async(&mut self) -> AuthExecutionResult<String> {
        let token = self.credential.get_token_silent_async().await?;
        Ok(token.as_bearer())
    }

    fn with_force_token_refresh(&mut self, force_token_refresh: ForceTokenRefresh) {
        self.credential
            .with_force_token_refresh(force_token_refresh);
    }
}

#[async_trait]
impl<Credential: Clone + Debug + Send + Sync + TokenCredentialExecutor> TokenCredentialExecutor
    for PublicClientApplication<Credential>
{
    fn uri(&mut self) -> IdentityResult<Url> {
        self.credential.uri()
    }

    fn form_urlencode(&mut self) -> IdentityResult<HashMap<String, String>> {
        self.credential.form_urlencode()
    }

    fn client_id(&self) -> &Uuid {
        self.credential.client_id()
    }

    fn authority(&self) -> Authority {
        self.credential.authority()
    }

    fn azure_cloud_instance(&self) -> AzureCloudInstance {
        self.credential.azure_cloud_instance()
    }

    fn app_config(&self) -> &AppConfig {
        self.credential.app_config()
    }

    fn execute(&mut self) -> AuthExecutionResult<reqwest::blocking::Response> {
        self.credential.execute()
    }

    async fn execute_async(&mut self) -> AuthExecutionResult<Response> {
        self.credential.execute_async().await
    }
}

impl From<ResourceOwnerPasswordCredential>
    for PublicClientApplication<ResourceOwnerPasswordCredential>
{
    fn from(value: ResourceOwnerPasswordCredential) -> Self {
        PublicClientApplication::credential(value)
    }
}

impl From<DeviceCodeCredential> for PublicClientApplication<DeviceCodeCredential> {
    fn from(value: DeviceCodeCredential) -> Self {
        PublicClientApplication::credential(value)
    }
}