synd_auth/device_flow/provider/
github.rs

1use std::borrow::Cow;
2
3use reqwest::Url;
4
5use crate::{
6    config,
7    device_flow::{DeviceAccessTokenRequest, DeviceAuthorizationRequest, Provider},
8};
9
10#[derive(Clone)]
11pub struct Github {
12    client_id: Cow<'static, str>,
13    device_authorization_endpoint: Url,
14    token_endpoint: Url,
15}
16
17impl Default for Github {
18    fn default() -> Self {
19        Self::new(config::github::CLIENT_ID)
20    }
21}
22
23impl Github {
24    const DEVICE_AUTHORIZATION_ENDPOINT: &'static str = "https://github.com/login/device/code";
25    const TOKEN_ENDPOINT: &'static str = "https://github.com/login/oauth/access_token";
26    // https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps
27    const SCOPE: &'static str = "user:email";
28
29    pub fn new(client_id: impl Into<Cow<'static, str>>) -> Self {
30        Self {
31            client_id: client_id.into(),
32            device_authorization_endpoint: Url::parse(Self::DEVICE_AUTHORIZATION_ENDPOINT).unwrap(),
33            token_endpoint: Url::parse(Self::TOKEN_ENDPOINT).unwrap(),
34        }
35    }
36
37    #[must_use]
38    pub fn with_device_authorization_endpoint(self, endpoint: Url) -> Self {
39        Self {
40            device_authorization_endpoint: endpoint,
41            ..self
42        }
43    }
44
45    #[must_use]
46    pub fn with_token_endpoint(self, endpoint: Url) -> Self {
47        Self {
48            token_endpoint: endpoint,
49            ..self
50        }
51    }
52}
53
54impl Provider for Github {
55    type DeviceAccessTokenRequest<'d> = DeviceAccessTokenRequest<'d>;
56    fn device_authorization_endpoint(&self) -> Url {
57        self.device_authorization_endpoint.clone()
58    }
59
60    fn token_endpoint(&self) -> reqwest::Url {
61        self.token_endpoint.clone()
62    }
63
64    fn device_authorization_request(&self) -> DeviceAuthorizationRequest {
65        DeviceAuthorizationRequest {
66            client_id: self.client_id.clone(),
67            scope: Self::SCOPE.into(),
68        }
69    }
70
71    fn device_access_token_request<'d, 'p: 'd>(
72        &'p self,
73        device_code: &'d str,
74    ) -> DeviceAccessTokenRequest<'d> {
75        DeviceAccessTokenRequest::new(device_code, self.client_id.as_ref())
76    }
77}