ibm_watson/auth/
mod.rs

1mod errors;
2use reqwest::{
3    header::{HeaderValue, CONTENT_TYPE},
4    Body, ClientBuilder, Method, Request, StatusCode, Url,
5};
6use serde::{Deserialize, Serialize};
7
8pub use errors::AuthenticationError;
9
10const AUTH_URL: &str = "https://iam.cloud.ibm.com/identity/token";
11#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(rename_all = "camelCase")]
13pub(crate) struct TokenResponse {
14    #[serde(rename = "access_token")]
15    access_token: String,
16    #[serde(rename = "refresh_token")]
17    refresh_token: String,
18    #[serde(rename = "delegated_refresh_token")]
19    delegated_refresh_token: Option<String>,
20    #[serde(rename = "token_type")]
21    token_type: String,
22    #[serde(rename = "expires_in")]
23    expires_in: i64,
24    expiration: i64,
25    scope: Option<String>,
26}
27
28#[allow(dead_code)]
29impl TokenResponse {
30    pub fn access_token(&self) -> &str {
31        &self.access_token
32    }
33
34    pub fn refresh_token(&self) -> &str {
35        &self.refresh_token
36    }
37
38    pub fn token_type(&self) -> &str {
39        &self.token_type
40    }
41
42    pub fn expires_in(&self) -> i64 {
43        self.expires_in
44    }
45
46    pub fn expiration(&self) -> i64 {
47        self.expiration
48    }
49
50    pub fn scope(&self) -> Option<&String> {
51        self.scope.as_ref()
52    }
53
54    pub fn delegated_refresh_token(&self) -> Option<&String> {
55        self.delegated_refresh_token.as_ref()
56    }
57}
58
59#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
60/// Holds the IAM Access token generated by IBM Watson
61pub struct IamAuthenticator {
62    access_token: TokenResponse,
63}
64
65impl IamAuthenticator {
66    /// Get an IAM Access token from an API key
67    ///
68    /// # Parameters
69    ///
70    /// * `api_key` - The API key for your Watson service
71    ///
72    /// # Example
73    /// ``` no_run
74    /// # use ibm_watson::auth::IamAuthenticator;
75    /// # async fn foo()-> Result<(), Box<dyn std::error::Error>> {
76    /// let auth = IamAuthenticator::new("api_key").await?;
77    /// # Ok(())
78    /// # }
79    /// ```
80    pub async fn new(api_key: impl AsRef<str>) -> Result<Self, AuthenticationError> {
81        let url = Url::parse(AUTH_URL).unwrap();
82        let mut req = Request::new(Method::POST, url);
83        let headers = req.headers_mut();
84        let _ = headers.insert(
85            CONTENT_TYPE,
86            HeaderValue::from_str("application/x-www-form-urlencoded").unwrap(),
87        );
88        let body = req.body_mut();
89        *body = Some(Body::from(format!(
90            "grant_type=urn:ibm:params:oauth:grant-type:apikey&apikey={}",
91            api_key.as_ref()
92        )));
93        let client = ClientBuilder::new();
94
95        let client = client.build().unwrap();
96        let resp = client
97            .execute(req)
98            .await
99            .map_err(|e| AuthenticationError::ConnectionError(e.to_string()))?;
100        match resp.status() {
101            StatusCode::OK => {
102                // asynchronously aggregate the chunks of the body
103                let access_token: TokenResponse = resp.json().await.unwrap();
104                Ok(Self { access_token })
105            }
106            StatusCode::BAD_REQUEST => Err(AuthenticationError::ParameterValidationFailed),
107            _ => unreachable!(),
108        }
109    }
110
111    pub(crate) fn token_response(&self) -> &TokenResponse {
112        &self.access_token
113    }
114}