summaryrefslogtreecommitdiffstats
path: root/src/auth/device_code.rs
blob: 087ff27472f6734d9ce06b871201e66a73a3ba7c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use std::ops::Add;
use std::time::Duration;
use futures::TryFutureExt;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use tokio::time::{Instant, MissedTickBehavior};
use super::AuthError;
use crate::util::USER_AGENT;

pub struct DeviceCodeAuthBuilder {
    client_id: Option<String>,
    scope: Option<String>,
    url: Option<String>
}

#[derive(Serialize, Debug)]
struct DeviceCodeRequest {
    client_id: String,
    scope: String,
    response_type: String
}

#[derive(Deserialize, Debug)]
struct DeviceCodeResponse {
    device_code: String,
    user_code: String,
    verification_uri: String,
    expires_in: u64,
    interval: u64,
    message: Option<String>
}

impl DeviceCodeAuthBuilder {
    pub fn new() -> DeviceCodeAuthBuilder {
        DeviceCodeAuthBuilder {
            client_id: None,
            scope: None,
            url: None
        }
    }

    pub fn client_id(mut self, client_id: &str) -> Self {
        self.client_id = Some(client_id.to_owned());
        self
    }

    pub fn scope(mut self, scope: &str) -> Self {
        self.scope = Some(scope.to_owned());
        self
    }

    pub fn url(mut self, url: &str) -> Self {
        self.url = Some(url.to_owned());
        self
    }

    pub async fn begin(self, client: Client) -> Result<DeviceCodeAuth, AuthError> {
        let scope = self.scope.expect("scope is not optional");
        let client_id = self.client_id.expect("client_id is not optional");
        let url = self.url.expect("url is not optional");

        let device_code: DeviceCodeResponse = client.post(&url)
            .header(reqwest::header::USER_AGENT, USER_AGENT)
            .header(reqwest::header::ACCEPT, "application/json")
            .form(&DeviceCodeRequest {
                client_id,
                scope,
                response_type: "device_code".into()
            })
            .send().await
            .and_then(|r| r.error_for_status())
            .map_err(|e| AuthError::Request { what: "requesting device code auth", error: e })?
            .json().await.map_err(|e| AuthError::Request { what: "receiving device code auth", error: e })?;

        let now = Instant::now();
        Ok(DeviceCodeAuth {
            client,
            start: now,
            interval: Duration::from_secs(device_code.interval + 1),
            expire_time: now.add(Duration::from_secs(device_code.expires_in)),
            info: dbg!(device_code)
        })
    }
}

pub struct DeviceCodeAuth {
    client: Client,
    start: Instant,
    interval: Duration,
    expire_time: Instant,
    info: DeviceCodeResponse
}

impl DeviceCodeAuth {
    async fn drive(&self) {
        let mut i = tokio::time::interval_at(self.start, self.interval);
        i.set_missed_tick_behavior(MissedTickBehavior::Skip);

        while self.expire_time.elapsed().is_zero() {
            
        }
    }
}