amazon_spapi/client/
auth.rs1use anyhow::Result;
2use reqwest::Client;
3use serde::{Deserialize, Serialize};
4use std::time::{Duration, SystemTime, UNIX_EPOCH};
5
6#[derive(Debug, Serialize)]
7pub struct LwaTokenRequest {
8 pub grant_type: String,
9 pub client_id: String,
10 pub client_secret: String,
11 pub refresh_token: String,
12}
13
14#[derive(Debug, Deserialize)]
15pub struct LwaTokenResponse {
16 pub access_token: String,
17 pub token_type: String,
18 pub expires_in: u64,
19}
20
21#[derive(Debug, Clone)]
22pub struct CachedToken {
23 pub access_token: String,
24 pub expires_at: u64, }
26
27pub struct AuthClient {
28 client: Client,
29 client_id: String,
30 client_secret: String,
31 refresh_token: String,
32 cached_token: Option<CachedToken>,
33}
34
35impl AuthClient {
36 pub fn new(client_id: String, client_secret: String, refresh_token: String, user_agent: &String) -> Result<Self> {
37 let client = Client::builder()
38 .timeout(Duration::from_secs(30))
39 .user_agent(user_agent)
40 .build()?;
41 Ok(Self {
42 client, client_id,
44 client_secret,
45 refresh_token,
46 cached_token: None,
47 })
48 }
49
50 fn get_current_timestamp() -> u64 {
51 SystemTime::now()
52 .duration_since(UNIX_EPOCH)
53 .unwrap()
54 .as_secs()
55 }
56
57 pub fn is_token_valid(&self) -> bool {
58 if let Some(ref cached) = self.cached_token {
59 let current_time = Self::get_current_timestamp();
60 let buffer_time = 300; cached.expires_at > current_time + buffer_time
63 } else {
64 false
65 }
66 }
67
68 pub async fn get_access_token(&mut self) -> Result<String> {
69 if self.is_token_valid() {
71 if let Some(ref cached) = self.cached_token {
72 return Ok(cached.access_token.clone());
73 }
74 }
75
76 self.refresh_access_token().await
78 }
79
80 pub async fn refresh_access_token(&mut self) -> Result<String> {
81 log::debug!("Refreshing access token...");
82 let lwa_url = "https://api.amazon.com/auth/o2/token";
83
84 let request_body = LwaTokenRequest {
85 grant_type: "refresh_token".to_string(),
86 client_id: self.client_id.clone(),
87 client_secret: self.client_secret.clone(),
88 refresh_token: self.refresh_token.clone(),
89 };
90
91 log::debug!("Request Body: {:?}", request_body);
92
93 let response = self
94 .client
95 .post(lwa_url)
96 .header("Content-Type", "application/x-www-form-urlencoded")
97 .form(&request_body)
98 .send()
99 .await?;
100
101 if response.status().is_success() {
102 let token_response: LwaTokenResponse = response.json().await?;
103
104 log::debug!("Response: {:?}", token_response);
105
106 let current_time = Self::get_current_timestamp();
108 let expires_at = current_time + token_response.expires_in;
109
110 self.cached_token = Some(CachedToken {
112 access_token: token_response.access_token.clone(),
113 expires_at,
114 });
115
116 Ok(token_response.access_token)
122 } else {
123 let error_text = response.text().await?;
124 Err(anyhow::anyhow!(
125 "Failed to get access token: {}",
126 error_text
127 ))
128 }
129 }
130
131 pub fn get_token_remaining_time(&self) -> Option<u64> {
133 if let Some(ref cached) = self.cached_token {
134 let current_time = Self::get_current_timestamp();
135 if cached.expires_at > current_time {
136 Some(cached.expires_at - current_time)
137 } else {
138 Some(0)
139 }
140 } else {
141 None
142 }
143 }
144}