twitch_oauth2/
id.rs

1//! Representation of oauth2 flow in `id.twitch.tv`
2
3use serde_derive::{Deserialize, Serialize};
4
5use crate::{AccessToken, RequestParseError};
6use std::time::Duration;
7/// Twitch's representation of the oauth flow.
8///
9/// Retrieve with
10///
11/// * [`UserTokenBuilder::get_user_token_request`](crate::tokens::UserTokenBuilder::get_user_token_request)
12/// * [`AppAccessToken::::get_app_access_token_request`](crate::tokens::AppAccessToken::get_app_access_token_request)
13#[derive(Clone, Debug, Deserialize, Serialize)]
14pub struct TwitchTokenResponse {
15    /// Access token
16    pub access_token: AccessToken,
17    /// Time (in seconds) until token expires
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub expires_in: Option<u64>,
20    /// Token that can be used to refresh
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub refresh_token: Option<crate::RefreshToken>,
23    /// Scopes attached to token
24    #[serde(rename = "scope", deserialize_with = "scope::deserialize")]
25    #[serde(default)]
26    pub scopes: Option<Vec<crate::Scope>>,
27}
28
29impl TwitchTokenResponse {
30    /// Create a [TwitchTokenResponse] from a [http::Response]
31    pub fn from_response<B: AsRef<[u8]>>(
32        response: &http::Response<B>,
33    ) -> Result<TwitchTokenResponse, RequestParseError> {
34        crate::parse_response(response)
35    }
36
37    /// Get the access token from this response
38    pub fn access_token(&self) -> &crate::AccessTokenRef { &self.access_token }
39
40    /// Get the expires in from this response
41    pub fn expires_in(&self) -> Option<Duration> { self.expires_in.map(Duration::from_secs) }
42
43    /// Get the refresh token from this response
44    pub fn refresh_token(&self) -> Option<&crate::RefreshTokenRef> { self.refresh_token.as_deref() }
45
46    /// Get the scopes from this response
47    pub fn scopes(&self) -> Option<&[crate::Scope]> { self.scopes.as_deref() }
48}
49
50/// Twitch's representation of the oauth flow for errors
51#[derive(Clone, Debug, Deserialize, Serialize, thiserror::Error)]
52pub struct TwitchTokenErrorResponse {
53    /// Status code of error
54    #[serde(with = "status_code")]
55    pub status: http::StatusCode,
56    /// Message attached to error
57    pub message: String,
58    /// Description of the error message.
59    pub error: Option<String>,
60}
61
62impl std::fmt::Display for TwitchTokenErrorResponse {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        write!(
65            f,
66            "{error} - {message}",
67            error = self
68                .error
69                .as_deref()
70                .unwrap_or_else(|| self.status.canonical_reason().unwrap_or("Error")),
71            message = self.message
72        )
73    }
74}
75/// Response from the device code flow
76#[derive(Clone, Debug, Deserialize, Serialize)]
77pub struct DeviceCodeResponse {
78    /// The identifier for a given user.
79    pub device_code: String,
80    /// Time until the code is no longer valid
81    pub expires_in: u64,
82    /// Time until another valid code can be requested
83    pub interval: u64,
84    /// The code that the user will use to authenticate
85    pub user_code: String,
86    /// The address you will send users to, to authenticate
87    pub verification_uri: String,
88}
89
90#[doc(hidden)]
91pub mod status_code {
92    use http::StatusCode;
93    use serde::{
94        de::{Deserialize, Error, Unexpected},
95        Deserializer, Serializer,
96    };
97
98    pub fn deserialize<'de, D>(de: D) -> Result<StatusCode, D::Error>
99    where D: Deserializer<'de> {
100        let code: u16 = Deserialize::deserialize(de)?;
101        match StatusCode::from_u16(code) {
102            Ok(code) => Ok(code),
103            Err(_) => Err(Error::invalid_value(
104                Unexpected::Unsigned(code as u64),
105                &"a value between 100 and 600",
106            )),
107        }
108    }
109
110    pub fn serialize<S>(status: &StatusCode, ser: S) -> Result<S::Ok, S::Error>
111    where S: Serializer {
112        ser.serialize_u16(status.as_u16())
113    }
114}
115
116#[doc(hidden)]
117pub mod scope {
118    use serde::{de::Deserialize, Deserializer};
119
120    pub fn deserialize<'de, D>(de: D) -> Result<Option<Vec<crate::Scope>>, D::Error>
121    where D: Deserializer<'de> {
122        let scopes: Option<Vec<crate::Scope>> = Deserialize::deserialize(de)?;
123        if let Some(scopes) = scopes {
124            match scopes {
125                scopes if scopes.is_empty() || scopes.len() > 1 => Ok(Some(scopes)),
126                scopes if scopes.len() == 1 && scopes.first().unwrap().as_str() == "" => Ok(None),
127                _ => Ok(Some(scopes)),
128            }
129        } else {
130            Ok(None)
131        }
132    }
133}