Skip to content

Commit 120eafe

Browse files
authored
Merge pull request #7 from xdevplatform/santiagomed/refresh-token
Add OAuth2 refactoring and refresh token
2 parents e1939e2 + bbff701 commit 120eafe

5 files changed

Lines changed: 289 additions & 102 deletions

File tree

.vscode/launch.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"type": "lldb",
9+
"request": "launch",
10+
"name": "Debug",
11+
"program": "${workspaceFolder}/<executable file>",
12+
"args": [],
13+
"cwd": "${workspaceFolder}"
14+
}
15+
]
16+
}

src/api/client.rs

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use reqwest::RequestBuilder;
66
use reqwest::{Client, Method};
77
use serde_json::Value;
88
use std::cell::RefCell;
9-
9+
use std::time::{SystemTime, UNIX_EPOCH};
1010
pub struct ApiClient {
1111
url: String,
1212
client: Client,
@@ -33,30 +33,57 @@ impl ApiClient {
3333
self
3434
}
3535

36+
/// Validate the OAuth2 token and refresh it if it is expired
37+
async fn validate_and_refresh_oauth2_token(
38+
&self,
39+
auth: &RefCell<Auth>,
40+
token: Token,
41+
username: Option<&str>,
42+
) -> Result<String, Error> {
43+
match token {
44+
Token::OAuth2(token) => {
45+
let current_time = SystemTime::now()
46+
.duration_since(UNIX_EPOCH)
47+
.unwrap()
48+
.as_secs();
49+
50+
if current_time > token.expiration_time {
51+
let new_token = auth.borrow_mut().oauth2_refresh_token(username).await?;
52+
Ok(format!("Bearer {}", new_token))
53+
} else {
54+
Ok(format!("Bearer {}", token.access_token))
55+
}
56+
}
57+
_ => Err(Error::AuthError(AuthError::WrongTokenFoundInStore)),
58+
}
59+
}
60+
61+
/// Get the OAuth2 token from the token store, validate it and refresh it if it is expired
3662
async fn get_oauth2_token(
3763
&self,
3864
auth: &RefCell<Auth>,
3965
username: Option<&str>,
4066
) -> Result<String, Error> {
41-
match username {
42-
Some(username) => {
43-
let token = auth.borrow_mut().oauth2(Some(username)).await?;
44-
Ok(format!("Bearer {}", token))
67+
let token = {
68+
let mut auth_ref = auth.borrow_mut();
69+
match username {
70+
Some(username) => auth_ref.get_token_store().get_oauth2_token(username),
71+
None => auth_ref.get_token_store().get_first_oauth2_token(),
72+
}
73+
};
74+
match token {
75+
Some(token) => {
76+
self.validate_and_refresh_oauth2_token(auth, token, username)
77+
.await
4578
}
4679
None => {
47-
if let Some(token) = auth.borrow_mut().get_token_store().get_first_oauth2_token() {
48-
match token {
49-
Token::OAuth2(token) => Ok(format!("Bearer {}", token)),
50-
_ => Err(Error::AuthError(AuthError::WrongTokenFoundInStore)),
51-
}
52-
} else {
53-
let token = auth.borrow_mut().oauth2(None).await?;
54-
Ok(format!("Bearer {}", token))
55-
}
80+
let token = auth.borrow_mut().oauth2(username).await?;
81+
Ok(format!("Bearer {}", token))
5682
}
5783
}
5884
}
5985

86+
/// Get the auth header for the request
6087
async fn get_auth_header(
6188
&self,
6289
method: &str,
@@ -69,7 +96,7 @@ impl ApiClient {
6996
None => return Ok("".to_string()),
7097
};
7198

72-
match auth_type.as_deref() {
99+
match auth_type {
73100
Some("app") => {
74101
if let Some(token) = auth.borrow().bearer_token() {
75102
Ok(format!("Bearer {}", token))
@@ -90,16 +117,16 @@ impl ApiClient {
90117
let token = {
91118
let mut auth_ref = auth.borrow_mut();
92119
if let Some(username) = username {
120+
// Username passed, we need to get the token for the specific username
93121
auth_ref.get_token_store().get_oauth2_token(username)
94122
} else {
123+
// No username passed, we need to get the first oauth2 token
95124
auth_ref.get_token_store().get_first_oauth2_token()
96125
}
97126
};
98127
if let Some(token) = token {
99-
match token {
100-
Token::OAuth2(token) => Ok(format!("Bearer {}", token)),
101-
_ => Err(Error::AuthError(AuthError::WrongTokenFoundInStore)),
102-
}
128+
self.validate_and_refresh_oauth2_token(auth, token, username)
129+
.await
103130
} else {
104131
let oauth1_result = {
105132
let auth_ref = auth.borrow();
@@ -121,7 +148,7 @@ impl ApiClient {
121148
}
122149
}
123150

124-
pub async fn build_request(
151+
pub async fn build_request(
125152
&self,
126153
method: &str,
127154
endpoint: &str,
@@ -188,8 +215,8 @@ pub async fn build_request(
188215
let response = request_builder.send().await?;
189216

190217
if verbose {
191-
println!("Request: {:#?}", req);
192-
println!("Response: {:#?}", response)
218+
println!("{:#?}", req);
219+
println!("{:#?}", response)
193220
}
194221

195222
let status = response.status();
@@ -201,7 +228,7 @@ pub async fn build_request(
201228
} else {
202229
Ok(res)
203230
}
204-
},
231+
}
205232
Err(_) => {
206233
let status = status.to_string();
207234
Err(Error::ApiError(serde_json::json!({
@@ -226,15 +253,22 @@ mod tests {
226253

227254
fn mock_auth() -> Auth {
228255
let config = Config::from_env();
229-
let auth = Auth::new(config)
230-
.with_token_store(TokenStore::from_file_path(".xurl_test".into()));
256+
let auth =
257+
Auth::new(config).with_token_store(TokenStore::from_file_path(".xurl_test".into()));
231258
auth
232259
}
233260

234261
fn setup_tests_with_mock_oauth2_token() -> Auth {
235262
let mut auth = mock_auth();
236263
let token_store = auth.get_token_store();
237-
token_store.save_oauth2_token("test", "fake_token").unwrap();
264+
let current_time = SystemTime::now()
265+
.duration_since(UNIX_EPOCH)
266+
.unwrap()
267+
.as_secs()
268+
+ 7200;
269+
token_store
270+
.save_oauth2_token("test", "fake_token", "fake_refresh_token", current_time)
271+
.unwrap();
238272

239273
auth
240274
}

0 commit comments

Comments
 (0)