twapi_reqwest/
oauth.rs

1use reqwest::{Error, Response};
2use serde_json::Value;
3use std::collections::HashMap;
4use std::time::Duration;
5use twapi_oauth::calc_oauth_header;
6
7use crate::build_client;
8
9pub async fn get_bearer_token_response(
10    consumer_key: &str,
11    consumer_secret: &str,
12    timeout_sec: Option<Duration>,
13) -> Result<Response, Error> {
14    let key = base64::encode(&format!("{}:{}", consumer_key, consumer_secret));
15    let client = build_client(timeout_sec);
16    client
17        .post("https://api.twitter.com/oauth2/token")
18        .header(
19            "Content-Type",
20            "application/x-www-form-urlencoded;charset=UTF-8",
21        )
22        .header("Authorization", &format!("Basic {}", key))
23        .body("grant_type=client_credentials")
24        .send()
25        .await
26}
27
28pub async fn get_bearer_token(
29    consumer_key: &str,
30    consumer_secret: &str,
31    timeout_sec: Option<Duration>,
32) -> Result<Option<String>, Error> {
33    let json: Value = get_bearer_token_response(consumer_key, consumer_secret, timeout_sec)
34        .await?
35        .json()
36        .await?;
37    match json["access_token"].as_str() {
38        Some(access_token) => Ok(Some(access_token.to_string())),
39        None => Ok(None),
40    }
41}
42
43pub async fn request_token_response(
44    consumer_key: &str,
45    consumer_secret: &str,
46    oauth_callback: &str,
47    x_auth_access_type: Option<&str>,
48    timeout_sec: Option<Duration>,
49) -> Result<Response, Error> {
50    let uri = "https://api.twitter.com/oauth/request_token";
51    let mut header_options = vec![("oauth_callback", oauth_callback)];
52    if let Some(x_auth_access_type) = x_auth_access_type {
53        header_options.push(("x_auth_access_type", x_auth_access_type));
54    }
55    let signed = calc_oauth_header(
56        &format!("{}&", consumer_secret),
57        consumer_key,
58        &header_options,
59        "POST",
60        uri,
61        &vec![],
62    );
63    let client = build_client(timeout_sec);
64    client
65        .post(uri)
66        .header("Authorization", &format!("OAuth {}", signed))
67        .send()
68        .await
69}
70
71pub async fn request_token(
72    consumer_key: &str,
73    consumer_secret: &str,
74    oauth_callback: &str,
75    x_auth_access_type: Option<&str>,
76    timeout_sec: Option<Duration>,
77) -> Result<HashMap<String, String>, Error> {
78    let response = request_token_response(
79        consumer_key,
80        consumer_secret,
81        oauth_callback,
82        x_auth_access_type,
83        timeout_sec,
84    )
85    .await?;
86    Ok(parse_oauth_body(response).await)
87}
88
89pub async fn access_token_response(
90    consumer_key: &str,
91    consumer_secret: &str,
92    oauth_token: &str,
93    oauth_token_secret: &str,
94    oauth_verifier: &str,
95    timeout_sec: Option<Duration>,
96) -> Result<Response, Error> {
97    let uri = "https://api.twitter.com/oauth/access_token";
98    let signed = calc_oauth_header(
99        &format!("{}&{}", consumer_secret, oauth_token_secret),
100        consumer_key,
101        &vec![
102            ("oauth_token", oauth_token),
103            ("oauth_verifier", oauth_verifier),
104        ],
105        "POST",
106        uri,
107        &vec![],
108    );
109    let client = build_client(timeout_sec);
110    client
111        .post(uri)
112        .header("Authorization", &format!("OAuth {}", signed))
113        .send()
114        .await
115}
116
117pub async fn access_token(
118    consumer_key: &str,
119    consumer_secret: &str,
120    oauth_token: &str,
121    oauth_token_secret: &str,
122    oauth_verifier: &str,
123    timeout_sec: Option<Duration>,
124) -> Result<HashMap<String, String>, Error> {
125    let response = access_token_response(
126        consumer_key,
127        consumer_secret,
128        oauth_token,
129        oauth_token_secret,
130        oauth_verifier,
131        timeout_sec,
132    )
133    .await?;
134    Ok(parse_oauth_body(response).await)
135}
136
137async fn parse_oauth_body(response: Response) -> HashMap<String, String> {
138    let status = response.status();
139    let mut result = HashMap::new();
140    match response.text().await {
141        Ok(body) => {
142            if status.is_success() {
143                for item in body.split("&") {
144                    let mut pair = item.split("=");
145                    if let Some(key) = pair.next() {
146                        result.insert(key.to_owned(), pair.next().unwrap_or("").to_owned());
147                    }
148                }
149            } else {
150                result.insert("twapi_request_body".to_owned(), body.clone());
151                result.insert(
152                    "twapi_request_status_code".to_owned(),
153                    status.as_str().to_owned(),
154                );
155            }
156        }
157        Err(_) => {}
158    }
159    result
160}