1use crate::helix::Client;
2use crate::helix::User;
3
4use anyhow::Result;
5use async_trait::async_trait;
6use chrono::DateTime;
7use chrono::Utc;
8use serde::Deserialize;
9use serde::Serialize;
10
11#[async_trait]
12pub trait TokenStorage {
13 async fn save(&mut self, token: &Token) -> Result<()>;
14}
15
16#[derive(Debug, Default, Clone, PartialEq)]
17pub enum TokenType {
18 #[default]
19 UserAccessToken,
20 AppAccessToken,
21}
22
23#[derive(Debug, Default, Clone, Serialize, Deserialize)]
24pub struct Token {
25 #[serde(skip)]
26 pub token_type: TokenType,
27 #[serde(default)]
28 pub refresh_token: String,
29 pub access_token: String,
30 pub expires_in: i64,
31 #[serde(default = "Utc::now")]
32 pub created_at: DateTime<Utc>,
33 #[serde(skip)]
34 pub user: Option<User>,
35}
36
37#[derive(Debug, Clone)]
38pub struct VoidStorage {}
39#[async_trait]
40impl TokenStorage for VoidStorage {
41 async fn save(&mut self, _token: &Token) -> Result<()> {
42 Ok(())
43 }
44}
45
46#[derive(Deserialize)]
47struct ValidateToken {
48 pub expires_in: i64,
49}
50
51impl<T: TokenStorage> Client<T> {
52 pub async fn validate_token(&mut self) -> Result<()> {
53 let token = match self
54 .get::<ValidateToken>("https://id.twitch.tv/oauth2/validate".to_string())
55 .await
56 {
57 Ok(r) => r,
58 Err(..) => {
59 self.refresh_token().await?;
60 return Ok(());
61 }
62 };
63
64 if token.expires_in < 3600 {
65 self.refresh_token().await?;
66 }
67
68 Ok(())
69 }
70
71 pub async fn refresh_token(&mut self) -> Result<()> {
72 if self.token.token_type == TokenType::AppAccessToken {
73 self.get_app_token().await?;
74 return Ok(());
75 }
76
77 let res = self
78 .http_request::<()>(
79 reqwest::Method::POST,
80 "https://id.twitch.tv/oauth2/token".to_string(),
81 None,
82 Some(format!(
83 "client_id={0}&client_secret={1}&grant_type=refresh_token&refresh_token={2}",
84 self.client_id, self.client_secret, self.token.refresh_token
85 )),
86 )
87 .await?;
88
89 self.token = res.json::<Token>().await?;
90 self.token_storage.save(&self.token).await?;
91
92 Ok(())
93 }
94
95 pub fn from_token_no_validation(
96 client_id: String,
97 client_secret: String,
98 token_storage: T,
99 token: Token,
100 ) -> Client<T> {
101 Client {
102 client_id: client_id,
103 client_secret: client_secret,
104 token: token,
105 http_client: reqwest::Client::new(),
106 token_storage: token_storage,
107 }
108 }
109
110 pub async fn from_token(
111 client_id: String,
112 client_secret: String,
113 token_storage: T,
114 token: Token,
115 ) -> Result<Client<T>> {
116 let mut client =
117 Self::from_token_no_validation(client_id, client_secret, token_storage, token);
118 client.token.user = Some(client.get_user().await?);
119 Ok(client)
120 }
121
122 async fn get_app_token(&mut self) -> Result<()> {
123 let token = self
124 .http_client
125 .post("https://id.twitch.tv/oauth2/token")
126 .body(format!(
127 "client_id={0}&client_secret={1}&grant_type=client_credentials",
128 self.client_id, self.client_secret
129 ))
130 .send()
131 .await?
132 .json::<Token>()
133 .await?;
134
135 self.token = token;
136 self.token.token_type = TokenType::AppAccessToken;
137 self.token_storage.save(&self.token).await?;
138
139 Ok(())
140 }
141
142 pub async fn from_get_app_token(
143 client_id: String,
144 client_secret: String,
145 token_storage: T,
146 ) -> Result<Client<T>> {
147 let http_client = reqwest::Client::new();
148 let mut client = Client {
149 client_id: client_id,
150 client_secret: client_secret,
151 http_client: http_client,
152 token_storage: token_storage,
153 token: Token::default(),
154 };
155 client.get_app_token().await?;
156 Ok(client)
157 }
158
159 pub async fn from_authorization(
160 client_id: String,
161 client_secret: String,
162 token_storage: T,
163 code: String,
164 redirect_uri: String,
165 ) -> Result<Client<T>> {
166 let http_client = reqwest::Client::new();
167 let token = http_client.post("https://id.twitch.tv/oauth2/token")
168 .body(format!("client_id={client_id}&client_secret={client_secret}&code={code}&grant_type=authorization_code&redirect_uri={redirect_uri}"))
169 .send()
170 .await?
171 .json::<Token>()
172 .await?;
173 let mut client = Client {
174 client_id: client_id,
175 client_secret: client_secret,
176 token: token,
177 http_client: http_client,
178 token_storage: token_storage,
179 };
180 client.token.user = Some(client.get_user().await?);
181 client.token_storage.save(&client.token).await?;
182 Ok(client)
183 }
184}