1use std::error::Error;
2use std::fmt::Error as FormatterError;
3use std::fmt::{Debug, Display, Formatter};
4use std::marker::PhantomData;
5use std::time::Duration;
67use serde::de::DeserializeOwned;
8use serde::{Deserialize, Serialize};
910use super::{
11 DeviceCode, EndUserVerificationUrl, ErrorResponse, ErrorResponseType, RequestTokenError,
12 StandardErrorResponse, TokenResponse, TokenType, UserCode,
13};
14use crate::basic::BasicErrorResponseType;
15use crate::types::VerificationUriComplete;
1617/// The minimum amount of time in seconds that the client SHOULD wait
18/// between polling requests to the token endpoint. If no value is
19/// provided, clients MUST use 5 as the default.
20fn default_devicecode_interval() -> u64 {
215
22}
2324///
25/// Trait for adding extra fields to the `DeviceAuthorizationResponse`.
26///
27pub trait ExtraDeviceAuthorizationFields: DeserializeOwned + Debug + Serialize {}
2829#[derive(Clone, Debug, Deserialize, Serialize)]
30///
31/// Empty (default) extra token fields.
32///
33pub struct EmptyExtraDeviceAuthorizationFields {}
34impl ExtraDeviceAuthorizationFields for EmptyExtraDeviceAuthorizationFields {}
3536///
37/// Standard OAuth2 device authorization response.
38///
39#[derive(Clone, Debug, Deserialize, Serialize)]
40pub struct DeviceAuthorizationResponse<EF>
41where
42EF: ExtraDeviceAuthorizationFields,
43{
44/// The device verification code.
45device_code: DeviceCode,
4647/// The end-user verification code.
48user_code: UserCode,
4950/// The end-user verification URI on the authorization The URI should be
51 /// short and easy to remember as end users will be asked to manually type
52 /// it into their user agent.
53 ///
54 /// The `verification_url` alias here is a deviation from the RFC, as
55 /// implementations of device code flow predate RFC 8628.
56#[serde(alias = "verification_url")]
57verification_uri: EndUserVerificationUrl,
5859/// A verification URI that includes the "user_code" (or other information
60 /// with the same function as the "user_code"), which is designed for
61 /// non-textual transmission.
62#[serde(skip_serializing_if = "Option::is_none")]
63verification_uri_complete: Option<VerificationUriComplete>,
6465/// The lifetime in seconds of the "device_code" and "user_code".
66expires_in: u64,
6768/// The minimum amount of time in seconds that the client SHOULD wait
69 /// between polling requests to the token endpoint. If no value is
70 /// provided, clients MUST use 5 as the default.
71#[serde(default = "default_devicecode_interval")]
72interval: u64,
7374#[serde(bound = "EF: ExtraDeviceAuthorizationFields", flatten)]
75extra_fields: EF,
76}
7778impl<EF> DeviceAuthorizationResponse<EF>
79where
80EF: ExtraDeviceAuthorizationFields,
81{
82/// The device verification code.
83pub fn device_code(&self) -> &DeviceCode {
84&self.device_code
85 }
8687/// The end-user verification code.
88pub fn user_code(&self) -> &UserCode {
89&self.user_code
90 }
9192/// The end-user verification URI on the authorization The URI should be
93 /// short and easy to remember as end users will be asked to manually type
94 /// it into their user agent.
95pub fn verification_uri(&self) -> &EndUserVerificationUrl {
96&self.verification_uri
97 }
9899/// A verification URI that includes the "user_code" (or other information
100 /// with the same function as the "user_code"), which is designed for
101 /// non-textual transmission.
102pub fn verification_uri_complete(&self) -> Option<&VerificationUriComplete> {
103self.verification_uri_complete.as_ref()
104 }
105106/// The lifetime in seconds of the "device_code" and "user_code".
107pub fn expires_in(&self) -> Duration {
108 Duration::from_secs(self.expires_in)
109 }
110111/// The minimum amount of time in seconds that the client SHOULD wait
112 /// between polling requests to the token endpoint. If no value is
113 /// provided, clients MUST use 5 as the default.
114pub fn interval(&self) -> Duration {
115 Duration::from_secs(self.interval)
116 }
117118/// Any extra fields returned on the response.
119pub fn extra_fields(&self) -> &EF {
120&self.extra_fields
121 }
122}
123124///
125/// Standard implementation of DeviceAuthorizationResponse which throws away
126/// extra received response fields.
127///
128pub type StandardDeviceAuthorizationResponse =
129 DeviceAuthorizationResponse<EmptyExtraDeviceAuthorizationFields>;
130131///
132/// Basic access token error types.
133///
134/// These error types are defined in
135/// [Section 5.2 of RFC 6749](https://tools.ietf.org/html/rfc6749#section-5.2) and
136/// [Section 3.5 of RFC 6749](https://tools.ietf.org/html/rfc8628#section-3.5)
137///
138#[derive(Clone, PartialEq)]
139pub enum DeviceCodeErrorResponseType {
140///
141 /// The authorization request is still pending as the end user hasn't
142 /// yet completed the user-interaction steps. The client SHOULD repeat the
143 /// access token request to the token endpoint. Before each new request,
144 /// the client MUST wait at least the number of seconds specified by the
145 /// "interval" parameter of the device authorization response, or 5 seconds
146 /// if none was provided, and respect any increase in the polling interval
147 /// required by the "slow_down" error.
148 ///
149AuthorizationPending,
150///
151 /// A variant of "authorization_pending", the authorization request is
152 /// still pending and polling should continue, but the interval MUST be
153 /// increased by 5 seconds for this and all subsequent requests.
154SlowDown,
155///
156 /// The authorization request was denied.
157 ///
158AccessDenied,
159///
160 /// The "device_code" has expired, and the device authorization session has
161 /// concluded. The client MAY commence a new device authorization request
162 /// but SHOULD wait for user interaction before restarting to avoid
163 /// unnecessary polling.
164ExpiredToken,
165///
166 /// A Basic response type
167 ///
168Basic(BasicErrorResponseType),
169}
170impl DeviceCodeErrorResponseType {
171fn from_str(s: &str) -> Self {
172match BasicErrorResponseType::from_str(s) {
173 BasicErrorResponseType::Extension(ext) => match ext.as_str() {
174"authorization_pending" => DeviceCodeErrorResponseType::AuthorizationPending,
175"slow_down" => DeviceCodeErrorResponseType::SlowDown,
176"access_denied" => DeviceCodeErrorResponseType::AccessDenied,
177"expired_token" => DeviceCodeErrorResponseType::ExpiredToken,
178_ => DeviceCodeErrorResponseType::Basic(BasicErrorResponseType::Extension(ext)),
179 },
180 basic => DeviceCodeErrorResponseType::Basic(basic),
181 }
182 }
183}
184impl AsRef<str> for DeviceCodeErrorResponseType {
185fn as_ref(&self) -> &str {
186match self {
187 DeviceCodeErrorResponseType::AuthorizationPending => "authorization_pending",
188 DeviceCodeErrorResponseType::SlowDown => "slow_down",
189 DeviceCodeErrorResponseType::AccessDenied => "access_denied",
190 DeviceCodeErrorResponseType::ExpiredToken => "expired_token",
191 DeviceCodeErrorResponseType::Basic(basic) => basic.as_ref(),
192 }
193 }
194}
195impl<'de> serde::Deserialize<'de> for DeviceCodeErrorResponseType {
196fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
197where
198D: serde::de::Deserializer<'de>,
199 {
200let variant_str = String::deserialize(deserializer)?;
201Ok(Self::from_str(&variant_str))
202 }
203}
204impl serde::ser::Serialize for DeviceCodeErrorResponseType {
205fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
206where
207S: serde::ser::Serializer,
208 {
209 serializer.serialize_str(self.as_ref())
210 }
211}
212impl ErrorResponseType for DeviceCodeErrorResponseType {}
213impl Debug for DeviceCodeErrorResponseType {
214fn fmt(&self, f: &mut Formatter) -> Result<(), FormatterError> {
215 Display::fmt(self, f)
216 }
217}
218219impl Display for DeviceCodeErrorResponseType {
220fn fmt(&self, f: &mut Formatter) -> Result<(), FormatterError> {
221write!(f, "{}", self.as_ref())
222 }
223}
224225///
226/// Error response specialization for device code OAuth2 implementation.
227///
228pub type DeviceCodeErrorResponse = StandardErrorResponse<DeviceCodeErrorResponseType>;
229230pub(crate) enum DeviceAccessTokenPollResult<TR, RE, TE, TT>
231where
232TE: ErrorResponse + 'static,
233 TR: TokenResponse<TT>,
234 TT: TokenType,
235 RE: Error + 'static,
236{
237 ContinueWithNewPollInterval(Duration),
238 Done(Result<TR, RequestTokenError<RE, TE>>, PhantomData<TT>),
239}