Skip to content

make getting expiration better and fix new rustdoc tool lint #40

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest]
rust: [1.45.2, nightly]
rust: [1.48, nightly]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* Made scope take `Cow<&'static str>`
* Made fields `access_token` and `refresh_token` `pub` on `UserToken`
* Fixed wrong scope `user:read:stream_key` -> `channel:read:stream_key`
* BREAKING: changed `TwitchToken::expires` -> `TwitchToken::expires_in` to calculate current lifetime of token

## End of Changelog

Expand Down
11 changes: 5 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![allow(unknown_lints)] // remove once broken_intra_doc_links is on stable
#![deny(missing_docs, broken_intra_doc_links)]
#![allow(unknown_lints, renamed_and_removed_lints)]
#![deny(missing_docs, broken_intra_doc_links)] // This will be weird until 1.52, see https://github.com/rust-lang/rust/pull/80527
#![cfg_attr(nightly, deny(rustdoc::broken_intra_doc_links))]
#![cfg_attr(nightly, feature(doc_cfg))]
#![doc(html_root_url = "https://docs.rs/twitch_oauth2/0.4.1")]
//! [![github]](https://github.com/emilgardis/twitch_oauth2)&ensp;[![crates-io]](https://crates.io/crates/twitch_oauth2)&ensp;[![docs-rs]](https://docs.rs/twitch_oauth2/0.4.1/twitch_oauth2)
Expand Down Expand Up @@ -164,7 +165,7 @@ pub async fn refresh_token<RE, C, F>(
) -> Result<
(
AccessToken,
Option<std::time::Instant>,
Option<std::time::Duration>,
Option<RefreshToken>,
),
RefreshTokenError<RE>,
Expand All @@ -174,8 +175,6 @@ where
C: FnOnce(HttpRequest) -> F,
F: Future<Output = Result<HttpResponse, RE>>,
{
let now = std::time::Instant::now();

let client = TwitchClient::new(
client_id.clone(),
Some(client_secret.clone()),
Expand All @@ -190,7 +189,7 @@ where
.await
.map_err(RefreshTokenError::RequestError)?;
let refresh_token = res.refresh_token().cloned();
let expires = res.expires_in().map(|dur| now + dur);
let expires = res.expires_in();
let access_token = res.access_token;
Ok((access_token, expires, refresh_token))
}
16 changes: 13 additions & 3 deletions src/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ pub trait TwitchToken {
RE: std::error::Error + Send + Sync + 'static,
C: FnOnce(HttpRequest) -> F,
F: Future<Output = Result<HttpResponse, RE>>;
/// Get instant when token will expire.
fn expires(&self) -> Option<std::time::Instant>;
/// Get current lifetime of token.
fn expires_in(&self) -> Option<std::time::Duration>;
/// Retrieve scopes attached to the token
fn scopes(&self) -> Option<&[Scope]>;
/// Validate this token. Should be checked on regularly, according to <https://dev.twitch.tv/docs/authentication#validating-requests>
Expand Down Expand Up @@ -81,7 +81,7 @@ impl<T: TwitchToken> TwitchToken for Box<T> {
(**self).refresh_token(http_client).await
}

fn expires(&self) -> Option<std::time::Instant> { (**self).expires() }
fn expires_in(&self) -> Option<std::time::Duration> { (**self).expires_in() }

fn scopes(&self) -> Option<&[Scope]> { (**self).scopes() }
}
Expand All @@ -99,4 +99,14 @@ pub struct ValidatedToken {
pub user_id: Option<String>,
/// Scopes attached to the token.
pub scopes: Option<Vec<Scope>>,
/// Lifetime of the token
#[serde(deserialize_with = "seconds_to_duration")]
pub expires_in: Option<std::time::Duration>,
}

fn seconds_to_duration<'a, D: serde::de::Deserializer<'a>>(
d: D,
) -> Result<Option<std::time::Duration>, D::Error> {
let seconds = Option::<u64>::deserialize(d)?;
Ok(seconds.map(std::time::Duration::from_secs))
}
27 changes: 18 additions & 9 deletions src/tokens/app_access_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ use std::future::Future;
/// An App Access Token from the [OAuth client credentials flow](https://dev.twitch.tv/docs/authentication/getting-tokens-oauth#oauth-client-credentials-flow)
#[derive(Debug, Clone)]
pub struct AppAccessToken {
access_token: AccessToken,
refresh_token: Option<RefreshToken>,
expires: Option<std::time::Instant>,
/// The access token used to authenticate requests with
pub access_token: AccessToken,
/// The refresh token used to extend the life of this user token
pub refresh_token: Option<RefreshToken>,
/// Expiration from when the response was generated.
expires_in: Option<std::time::Duration>,
/// When this struct was created, not when token was created.
struct_created: std::time::Instant,
client_id: ClientId,
client_secret: ClientSecret,
login: Option<String>,
Expand All @@ -36,19 +41,22 @@ impl TwitchToken for AppAccessToken {
C: FnOnce(HttpRequest) -> F,
F: Future<Output = Result<HttpResponse, RE>>,
{
let (access_token, expires, refresh_token) = if let Some(token) = self.refresh_token.take()
let (access_token, expires_in, refresh_token) = if let Some(token) =
self.refresh_token.take()
{
crate::refresh_token(http_client, token, &self.client_id, &self.client_secret).await?
} else {
return Err(RefreshTokenError::NoRefreshToken);
};
self.access_token = access_token;
self.expires = expires;
self.expires_in = expires_in;
self.refresh_token = refresh_token;
Ok(())
}

fn expires(&self) -> Option<std::time::Instant> { self.expires }
fn expires_in(&self) -> Option<std::time::Duration> {
self.expires_in.map(|e| e - self.struct_created.elapsed())
}

fn scopes(&self) -> Option<&[Scope]> { self.scopes.as_deref() }
}
Expand All @@ -68,7 +76,8 @@ impl AppAccessToken {
client_id: client_id.into(),
client_secret: client_secret.into(),
login,
expires: None,
expires_in: None,
struct_created: std::time::Instant::now(),
scopes,
}
}
Expand Down Expand Up @@ -107,7 +116,6 @@ impl AppAccessToken {
C: Fn(HttpRequest) -> F,
F: Future<Output = Result<HttpResponse, RE>>,
{
let now = std::time::Instant::now();
let client = TwitchClient::new(
client_id.clone(),
Some(client_secret.clone()),
Expand All @@ -128,7 +136,8 @@ impl AppAccessToken {
let app_access = AppAccessToken {
access_token: response.access_token().clone(),
refresh_token: response.refresh_token().cloned(),
expires: response.expires_in().map(|dur| now + dur),
expires_in: response.expires_in(),
struct_created: std::time::Instant::now(),
client_id,
client_secret,
login: None,
Expand Down
16 changes: 12 additions & 4 deletions src/tokens/user_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ pub struct UserToken {
login: Option<String>,
/// The refresh token used to extend the life of this user token
pub refresh_token: Option<RefreshToken>,
expires: Option<std::time::Instant>,
/// Expiration from when the response was generated.
expires_in: Option<std::time::Duration>,
/// When this struct was created, not when token was created.
struct_created: std::time::Instant,
scopes: Vec<Scope>,
}

Expand All @@ -34,14 +37,16 @@ impl UserToken {
client_secret: impl Into<Option<ClientSecret>>,
login: Option<String>,
scopes: Option<Vec<Scope>>,
expires_in: Option<std::time::Duration>,
) -> UserToken {
UserToken {
access_token: access_token.into(),
client_id: client_id.into(),
client_secret: client_secret.into(),
login,
refresh_token: refresh_token.into(),
expires: None,
expires_in,
struct_created: std::time::Instant::now(),
scopes: scopes.unwrap_or_else(Vec::new),
}
}
Expand All @@ -66,6 +71,7 @@ impl UserToken {
client_secret,
validated.login,
validated.scopes,
validated.expires_in,
))
}

Expand Down Expand Up @@ -105,15 +111,17 @@ impl TwitchToken for UserToken {
return Err(RefreshTokenError::NoRefreshToken);
};
self.access_token = access_token;
self.expires = expires;
self.expires_in = expires;
self.refresh_token = refresh_token;
Ok(())
} else {
return Err(RefreshTokenError::NoClientSecretFound);
}
}

fn expires(&self) -> Option<std::time::Instant> { None }
fn expires_in(&self) -> Option<std::time::Duration> {
self.expires_in.map(|e| e - self.struct_created.elapsed())
}

fn scopes(&self) -> Option<&[Scope]> { Some(self.scopes.as_slice()) }
}
Expand Down