1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use crate::tokens::{
errors::{RefreshTokenError, ValidationError},
Scope, TwitchToken,
};
use crate::ClientSecret;
use oauth2::{AccessToken, ClientId, RefreshToken};
use oauth2::{HttpRequest, HttpResponse};
use std::future::Future;
#[derive(Debug, Clone)]
pub struct UserToken {
pub access_token: AccessToken,
client_id: ClientId,
client_secret: Option<ClientSecret>,
login: Option<String>,
pub refresh_token: Option<RefreshToken>,
expires: Option<std::time::Instant>,
scopes: Vec<Scope>,
}
impl UserToken {
pub fn from_existing_unchecked(
access_token: impl Into<AccessToken>,
refresh_token: impl Into<Option<RefreshToken>>,
client_id: impl Into<ClientId>,
client_secret: impl Into<Option<ClientSecret>>,
login: Option<String>,
scopes: Option<Vec<Scope>>,
) -> 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,
scopes: scopes.unwrap_or_else(Vec::new),
}
}
pub async fn from_existing<RE, C, F>(
http_client: C,
access_token: AccessToken,
refresh_token: impl Into<Option<RefreshToken>>,
client_secret: impl Into<Option<ClientSecret>>,
) -> Result<UserToken, ValidationError<RE>>
where
RE: std::error::Error + Send + Sync + 'static,
C: FnOnce(HttpRequest) -> F,
F: Future<Output = Result<HttpResponse, RE>>,
{
let validated = crate::validate_token(http_client, &access_token).await?;
Ok(Self::from_existing_unchecked(
access_token,
refresh_token.into(),
validated.client_id,
client_secret,
validated.login,
validated.scopes,
))
}
}
#[async_trait::async_trait(?Send)]
impl TwitchToken for UserToken {
fn client_id(&self) -> &ClientId { &self.client_id }
fn token(&self) -> &AccessToken { &self.access_token }
fn login(&self) -> Option<&str> { self.login.as_deref() }
async fn refresh_token<RE, C, F>(
&mut self,
http_client: C,
) -> Result<(), RefreshTokenError<RE>>
where
RE: std::error::Error + Send + Sync + 'static,
C: FnOnce(HttpRequest) -> F,
F: Future<Output = Result<HttpResponse, RE>>,
{
if let Some(client_secret) = self.client_secret.clone() {
let (access_token, expires, refresh_token) = if let Some(token) =
self.refresh_token.take()
{
crate::refresh_token(http_client, token, &self.client_id, &client_secret).await?
} else {
return Err(RefreshTokenError::NoRefreshToken);
};
self.access_token = access_token;
self.expires = expires;
self.refresh_token = refresh_token;
Ok(())
} else {
return Err(RefreshTokenError::NoClientSecretFound);
}
}
fn expires(&self) -> Option<std::time::Instant> { None }
fn scopes(&self) -> Option<&[Scope]> { Some(self.scopes.as_slice()) }
}