twitch_api_rs/auth/
mod.rs1pub mod scopes;
8
9pub trait AuthToken: crate::requests::Headers + Clone {
12 fn scopes(&self) -> &scopes::ScopeSet;
14}
15
16use reqwest::RequestBuilder;
17use std::rc::Rc;
18use std::sync::Arc;
19
20impl<H> crate::requests::Headers for Arc<H>
21where
22 H: crate::requests::Headers,
23{
24 fn write_headers(&self, req: RequestBuilder) -> RequestBuilder {
25 self.as_ref().write_headers(req)
26 }
27}
28
29impl<A> AuthToken for Arc<A>
30where
31 A: AuthToken,
32{
33 fn scopes(&self) -> &scopes::ScopeSet {
34 self.as_ref().scopes()
35 }
36}
37
38impl<H> crate::requests::Headers for Rc<H>
39where
40 H: crate::requests::Headers,
41{
42 fn write_headers(&self, req: RequestBuilder) -> RequestBuilder {
43 self.as_ref().write_headers(req)
44 }
45}
46
47impl<A> AuthToken for Rc<A>
48where
49 A: AuthToken,
50{
51 fn scopes(&self) -> &scopes::ScopeSet {
52 self.as_ref().scopes()
53 }
54}
55
56use crate::values::FieldValue;
57use crate::{field_wrapper_name, from_inner, quick_deref_into};
58use serde::{Deserialize, Serialize};
59
60#[repr(transparent)]
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
62#[serde(transparent)]
63pub struct ClientId(String);
68
69#[repr(transparent)]
70#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
71#[serde(transparent)]
72pub struct ClientSecret(String);
77
78quick_deref_into![(ClientId, String), (ClientSecret, String)];
79from_inner![(ClientId, String), (ClientSecret, String)];
80field_wrapper_name![ClientId => "client_id", ClientSecret => "client_secret"];
81
82pub mod implicit_code {}
86
87pub mod authorization_code {}
91
92pub mod client_credentials {
128
129 use super::*;
130 use crate::requests::*; use reqwest::RequestBuilder;
132 use serde::{ser::SerializeMap, Deserialize, Serialize, Serializer};
133
134 #[derive(Debug)]
135 #[doc(hidden)]
136 pub struct ClientAuthRequestParams {
138 client_id: Option<ClientId>,
139 client_secret: Option<ClientSecret>,
140 scopes: Vec<String>, }
142
143 impl ParametersExt for ClientAuthRequestParams {}
144
145 #[derive(Debug)]
146 pub struct ClientAuthRequest {
153 params: ClientAuthRequestParams,
154 }
155
156 impl Serialize for ClientAuthRequestParams {
157 fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error>
158 where
159 S: Serializer,
160 {
161 let mut map = ser.serialize_map(Some(if self.scopes.len() > 0 { 4 } else { 3 }))?;
162 map.serialize_entry("client_id", self.client_id.as_ref().unwrap())?;
163 map.serialize_entry("client_secret", self.client_secret.as_ref().unwrap())?;
164 map.serialize_entry("grant_type", "client_credentials")?;
165
166 map.end()
169 }
170 }
171
172 #[cfg_attr(feature = "nightly", doc(spotlight))]
173 impl Request for ClientAuthRequest {
174 const ENDPOINT: &'static str = "https://id.twitch.tv/oauth2/token";
175
176 type Headers = ();
177 type Parameters = ClientAuthRequestParams;
178 type Body = ();
179
180 type Response = ClientAuthResponse;
181
182 type ErrorCodes = CommonResponseCodes;
183
184 const METHOD: reqwest::Method = reqwest::Method::POST;
185
186 fn builder() -> Self {
187 Self {
188 params: ClientAuthRequestParams {
189 client_id: None,
190 client_secret: None,
191 scopes: Vec::new(),
192 },
193 }
194 }
195
196 fn headers(&self) -> &Self::Headers {
197 &()
198 }
199 fn parameters(&self) -> &Self::Parameters {
200 &self.params
201 }
202 fn body(&self) -> &Self::Body {
203 &()
204 }
205
206 fn ready(&self) -> Result<(), RequestError<Self::ErrorCodes>> {
207 if self.params.client_id.is_none() {
208 Err(RequestError::MalformedRequest(String::from(
209 "field client_id must be set",
210 )))
211 } else if self.params.client_secret.is_none() {
212 Err(RequestError::MalformedRequest(String::from(
213 "field client_secret must be set",
214 )))
215 } else {
216 Ok(())
217 }
218 }
219 }
220
221 impl ClientAuthRequest {
222 pub fn set_client_id<I: Into<ClientId>>(&mut self, client_id: I) -> &mut Self {
224 self.params.client_id.replace(client_id.into());
225 self
226 }
227
228 pub fn set_client_secret<S: Into<ClientSecret>>(&mut self, client_secret: S) -> &mut Self {
230 self.params.client_secret.replace(client_secret.into());
231 self
232 }
233 }
234
235 impl<I, S> From<(I, S)> for ClientAuthRequest
237 where
238 I: Into<ClientId>,
239 S: Into<ClientSecret>,
240 {
241 fn from((client_id, client_secret): (I, S)) -> Self {
242 Self {
243 params: ClientAuthRequestParams {
244 client_id: Some(client_id.into()),
245 client_secret: Some(client_secret.into()),
246 scopes: vec![],
247 },
248 }
249 }
250 }
251
252 #[derive(Debug, Deserialize)]
253 pub struct ClientAuthResponse {
257 pub access_token: String,
259 pub expires_in: u32,
262 }
264
265 impl Into<(String, u32)> for ClientAuthResponse {
266 fn into(self) -> (String, u32) {
267 (self.access_token, self.expires_in)
268 }
269 }
270
271 use super::scopes::ScopeSet;
272
273 #[derive(Debug, Clone)]
275 #[allow(missing_docs)]
276 pub struct ClientAuthToken {
277 scopes: ScopeSet,
278 pub token: String,
279 pub client_id: ClientId,
280 }
281
282 impl ClientAuthToken {
283 pub fn from_client<C>(auth_response: ClientAuthResponse, client_id: C) -> Self
285 where
286 C: Into<ClientId>,
287 {
288 Self {
289 scopes: ScopeSet::new(),
291 token: auth_response.access_token,
292 client_id: client_id.into(),
293 }
294 }
295 }
296
297 impl Headers for ClientAuthToken {
298 fn write_headers(&self, req: RequestBuilder) -> RequestBuilder {
299 req.header("Authorization", format!("Bearer {}", self.token))
300 .header("Client-Id", std::ops::Deref::deref(&self.client_id))
301 }
302 }
303
304 impl super::AuthToken for ClientAuthToken {
305 fn scopes(&self) -> &ScopeSet {
306 &self.scopes
307 }
308 }
309}