oauth2_core/device_authorization_grant/
device_authorization_response.rs

1//! https://datatracker.ietf.org/doc/html/rfc8628#section-3.2
2
3use core::time::Duration;
4
5use mime::Mime;
6use serde::{Deserialize, Serialize};
7use serde_json::{Map, Value};
8use url::Url;
9
10pub const CONTENT_TYPE: Mime = mime::APPLICATION_JSON;
11pub const INTERVAL_DEFAULT: usize = 5;
12pub type DeviceCode = String;
13pub type UserCode = String;
14pub type VerificationUri = Url;
15pub type VerificationUriComplete = Url;
16
17//
18//
19//
20#[derive(Serialize, Deserialize, Debug, Clone)]
21pub struct SuccessfulBody {
22    pub device_code: DeviceCode,
23    pub user_code: UserCode,
24    // e.g. google
25    #[serde(alias = "verification_url")]
26    pub verification_uri: VerificationUri,
27    #[serde(
28        alias = "verification_url_complete",
29        skip_serializing_if = "Option::is_none"
30    )]
31    pub verification_uri_complete: Option<VerificationUriComplete>,
32    pub expires_in: usize,
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub interval: Option<usize>,
35
36    #[serde(flatten, skip_serializing_if = "Option::is_none")]
37    _extra: Option<Map<String, Value>>,
38}
39
40impl SuccessfulBody {
41    pub fn new(
42        device_code: DeviceCode,
43        user_code: UserCode,
44        verification_uri: VerificationUri,
45        verification_uri_complete: Option<VerificationUriComplete>,
46        expires_in: usize,
47        interval: Option<usize>,
48    ) -> Self {
49        Self {
50            device_code,
51            user_code,
52            verification_uri,
53            verification_uri_complete,
54            expires_in,
55            interval,
56            _extra: None,
57        }
58    }
59
60    pub fn interval(&self) -> Duration {
61        Duration::from_secs(self.interval.unwrap_or(INTERVAL_DEFAULT) as u64)
62    }
63
64    pub fn set_extra(&mut self, extra: Map<String, Value>) {
65        self._extra = Some(extra);
66    }
67    pub fn extra(&self) -> Option<&Map<String, Value>> {
68        self._extra.as_ref()
69    }
70}
71
72//
73//
74//
75pub type ErrorBody = crate::access_token_response::ErrorBody;
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn de() {
83        let body_str = r#"
84        {
85            "device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS",
86            "user_code": "WDJB-MJHT",
87            "verification_uri": "https://example.com/device",
88            "verification_uri_complete":
89                "https://example.com/device?user_code=WDJB-MJHT",
90            "expires_in": 1800,
91            "interval": 5
92        }
93        "#;
94        match serde_json::from_str::<SuccessfulBody>(body_str) {
95            Ok(_) => {}
96            Err(err) => panic!("{err}"),
97        }
98    }
99}