Skip to content

Commit

Permalink
✨ feature(token.rs): add refresh_token
Browse files Browse the repository at this point in the history
Signed-off-by: xtrm <[email protected]>
  • Loading branch information
xtrm-en committed Jan 31, 2024
1 parent 6d6fd9d commit 752c290
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 29 deletions.
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ reqwest = { version = "0.11", features = ["json"] }
thiserror = "1.0"
chrono = "0.4.33"
urlencoding = "2.1.3"
secrecy = { version = "0.8.0", features = ["serde"] }

[dev-dependencies]
once_cell = "1.19.0"
Expand Down
2 changes: 1 addition & 1 deletion examples/oauth2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ async fn index(cookies: &CookieJar<'_>) -> RawHtml<String> {
</div>
})
} else {
let auth_url = CLIENT.get_authorization_url(CALLBACK_URL, SCOPES);
let auth_url = CLIENT.get_authorization_url(CALLBACK_URL, SCOPES).unwrap();
RawHtml(html! {
<div>
<h1>{"Hello, world!"}</h1>
Expand Down
62 changes: 36 additions & 26 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use reqwest::Client;
use rocket::time::ext;
use std::time::Duration;

use crate::{models::{self, token::AccessToken}, FtError, Result};
Expand Down Expand Up @@ -164,39 +163,50 @@ impl FtClient {
///
/// This method is called automatically by the API Client when making a request, so there is no need to call it manually.
pub async fn ensure_app_token(&mut self) -> Result<()> {
self.auth_type.try_refresh_token(self).await?;
// self.auth_type.try_refresh_token(self).await?;
Ok(())
}

pub fn get_authorization_url(&self, callback_url: &str, scopes: &[&str]) -> String {
format!(
"{}/oauth/authorize?client_id={}&redirect_uri={}&scope={}&response_type=code",
API_URL,
self.app_uid,
urlencoding::encode(callback_url),
scopes.join(" ")
)
pub fn get_authorization_url(&self, callback_url: &str, scopes: &[&str]) -> Result<String> {
if let AuthType::App { uid, .. } = &self.auth_type {
Ok(format!(
"{}/oauth/authorize?client_id={}&redirect_uri={}&scope={}&response_type=code",
API_URL,
uid,
urlencoding::encode(callback_url),
scopes.join(" ")
))
} else {
Err(FtError::InvalidAuthType)
}
}

pub async fn fetch_access_token(&self, code: &str, callback_url: &str) -> Result<AccessToken> {
let res = self.client
.post(endpoint!("/oauth/token"))
.form(&[
("grant_type", "authorization_code"),
("client_id", &self.app_uid),
("client_secret", &self.app_secret),
("code", code),
("redirect_uri", callback_url),
])
.send()
.await?;
if let AuthType::App { uid, secret, .. } = &self.auth_type {
let res = self.client
.post(endpoint!("/oauth/token"))
.form(&[
("grant_type", "authorization_code"),
("client_id", &uid),
("client_secret", &secret),
("code", code),
("redirect_uri", callback_url),
])
.send()
.await?;

if res.status().is_success() {
let token = res.json::<AccessToken>().await?;
Ok(token)
if res.status().is_success() {
let text = res.text().await?;
println!("User Token: {:?}", text);
// let token = res.json::<AccessToken>().await?;
let token = serde_json::from_str::<AccessToken>(&text)?;
Ok(token)
} else {
self.handle_error(res).await?;
unreachable!()
}
} else {
self.handle_error(res).await?;
unreachable!()
Err(FtError::InvalidAuthType)
}
}

Expand Down
23 changes: 21 additions & 2 deletions src/models/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ pub struct AccessToken {
expires_in: u64,
scope: String,
created_at: u64,

token_info: Option<TokenInfo>
pub(crate) refresh_token: Option<String>,
pub(crate) secret_valid_until: Option<u64>,
}

#[derive(Deserialize, Serialize, Debug)]
Expand Down Expand Up @@ -38,4 +38,23 @@ impl AccessToken {
pub fn is_expired(&self) -> bool {
self.created_at + self.expires_in <= (chrono::Utc::now().timestamp() as u64 + 5)
}

/// Checks if the token can be renewed.
///
/// This method will return true if the token can be renewed, and false otherwise.
///
/// # Panics
///
/// This method will panic if the system's time is not set correctly.
pub fn can_renew(&self) -> bool {
if self.refresh_token.is_some() {
if let Some(secret_valid_until) = self.secret_valid_until {
secret_valid_until <= (chrono::Utc::now().timestamp() as u64 + 5)
} else {
true
}
} else {
false
}
}
}

0 comments on commit 752c290

Please sign in to comment.