yubico/
async_verifier.rs

1use reqwest::header::USER_AGENT;
2use reqwest::Client;
3
4use crate::config::Config;
5use crate::yubicoerror::YubicoError;
6use crate::{build_request, Request, Result};
7use futures::stream::FuturesUnordered;
8use futures::StreamExt;
9use std::sync::Arc;
10
11pub async fn verify_async<S>(otp: S, config: Config) -> Result<()>
12where
13    S: Into<String>,
14{
15    AsyncVerifier::new(config)?.verify(otp).await
16}
17
18pub struct AsyncVerifier {
19    client: Client,
20    config: Config,
21}
22
23impl AsyncVerifier {
24    pub fn new(config: Config) -> Result<AsyncVerifier> {
25        let client = 
26            if config.proxy_url != "" && config.proxy_username == "" {
27                AsyncVerifier::get_client_proxy(config.clone())?
28            } else if config.proxy_url != "" && config.proxy_username != "" {
29                AsyncVerifier::get_client_proxy_with_auth(config.clone())?
30            } else {
31                Client::builder().timeout(config.request_timeout).build()?
32            };
33
34        Ok(AsyncVerifier { client, config })
35    }
36
37    pub async fn verify<S>(&self, otp: S) -> Result<()>
38    where
39        S: Into<String>,
40    {
41        let request = Arc::new(build_request(otp, &self.config)?); // Arc because we need the future to be Send.
42
43        let mut responses = FuturesUnordered::new();
44        self.config
45            .api_hosts
46            .iter()
47            .for_each(|api_host| responses.push(self.request(request.clone(), api_host)));
48
49        let mut errors = vec![];
50
51        while let Some(response) = responses.next().await {
52            match response {
53                Ok(()) => return Ok(()),
54                Err(err @ YubicoError::ReplayedRequest) => errors.push(err),
55                Err(YubicoError::HTTPStatusCode(code)) => {
56                    errors.push(YubicoError::HTTPStatusCode(code))
57                }
58                Err(err) => return Err(err),
59            }
60        }
61
62        Err(YubicoError::MultipleErrors(errors))
63    }
64
65    async fn request(&self, request: Arc<Request>, api_host: &str) -> Result<()> {
66        let url = request.build_url(api_host);
67        let http_request = self
68            .client
69            .get(&url)
70            .header(USER_AGENT, self.config.user_agent.clone());
71
72        let response = http_request.send().await?;
73        let status_code = response.status();
74
75        if !status_code.is_success() {
76            return Err(YubicoError::HTTPStatusCode(status_code));
77        }
78
79        let text = response.text().await?;
80
81        request.response_verifier.verify_response(text)
82    }
83
84    fn get_client_proxy(config: Config) -> Result<Client> {
85        Ok(Client::builder()
86            .timeout(config.request_timeout)
87            .proxy(reqwest::Proxy::all(&config.proxy_url)?).build()?)
88    }
89
90    fn get_client_proxy_with_auth(config: Config) -> Result<Client> {
91        let proxy = reqwest::Proxy::all(&config.proxy_url)?
92            .basic_auth(&config.proxy_username, &config.proxy_password);
93        Ok(Client::builder().timeout(config.request_timeout).proxy(proxy).build()?)
94    }
95}