modio/request/auth/
external_auth.rs

1use std::future::IntoFuture;
2
3use serde::ser::Serialize;
4use serde_derive::Serialize;
5
6use crate::client::Client;
7use crate::request::{Output, RequestBuilder, Route};
8use crate::response::ResponseFuture;
9use crate::types::auth::AccessToken;
10
11/// Request an access token for currently supported providers.
12pub struct ExternalAuth<'a, T> {
13    http: &'a Client,
14    fields: ExternalAuthFields<'a, T>,
15}
16
17#[derive(Serialize)]
18struct ExternalAuthFields<'a, T> {
19    #[serde(skip_serializing_if = "Option::is_none")]
20    email: Option<&'a str>,
21    #[serde(rename = "date_expires", skip_serializing_if = "Option::is_none")]
22    expired_at: Option<u64>,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    terms_agreed: Option<bool>,
25    #[serde(flatten)]
26    others: T,
27}
28
29#[derive(Serialize)]
30pub struct Discord<'a> {
31    #[serde(rename = "discord_token")]
32    token: &'a str,
33}
34
35impl<'a> Discord<'a> {
36    pub const fn new(token: &'a str) -> Self {
37        Self { token }
38    }
39}
40
41#[derive(Serialize)]
42pub struct EpicGames<'a> {
43    #[serde(rename = "id_token")]
44    token: &'a str,
45}
46
47impl<'a> EpicGames<'a> {
48    pub const fn new(token: &'a str) -> Self {
49        Self { token }
50    }
51}
52
53#[derive(Serialize)]
54pub struct Google<'a> {
55    #[serde(rename = "id_token")]
56    token: &'a str,
57}
58
59impl<'a> Google<'a> {
60    pub const fn new(token: &'a str) -> Self {
61        Self { token }
62    }
63}
64
65#[derive(Serialize)]
66pub struct MetaQuest<'a> {
67    device: &'a str,
68    nonce: &'a str,
69    user_id: u64,
70    access_token: &'a str,
71}
72
73impl<'a> MetaQuest<'a> {
74    pub const fn new(device: &'a str, nonce: &'a str, user_id: u64, access_token: &'a str) -> Self {
75        Self {
76            device,
77            nonce,
78            user_id,
79            access_token,
80        }
81    }
82}
83
84#[derive(Serialize)]
85pub struct OpenID<'a> {
86    #[serde(rename = "id_token")]
87    token: &'a str,
88}
89
90impl<'a> OpenID<'a> {
91    pub fn new(token: &'a str) -> Self {
92        Self { token }
93    }
94}
95
96#[derive(Serialize)]
97pub struct PSN<'a> {
98    auth_code: &'a str,
99    #[serde(skip_serializing_if = "Option::is_none")]
100    env: Option<u32>,
101}
102
103impl<'a> PSN<'a> {
104    pub fn new(auth_code: &'a str) -> Self {
105        Self {
106            auth_code,
107            env: None,
108        }
109    }
110}
111
112#[derive(Serialize)]
113pub struct Steam<'a> {
114    appdata: &'a str,
115}
116
117impl<'a> Steam<'a> {
118    pub const fn new(appdata: &'a str) -> Self {
119        Self { appdata }
120    }
121}
122
123#[derive(Serialize)]
124pub struct Switch<'a> {
125    #[serde(rename = "id_token")]
126    token: &'a str,
127}
128
129impl<'a> Switch<'a> {
130    pub const fn new(token: &'a str) -> Self {
131        Self { token }
132    }
133}
134
135#[derive(Serialize)]
136pub struct Xbox<'a> {
137    #[serde(rename = "xbox_token")]
138    token: &'a str,
139}
140
141impl<'a> Xbox<'a> {
142    pub const fn new(token: &'a str) -> Self {
143        Self { token }
144    }
145}
146
147impl<'a, T> ExternalAuth<'a, T> {
148    pub(crate) const fn new(http: &'a Client, others: T) -> Self {
149        Self {
150            http,
151            fields: ExternalAuthFields {
152                email: None,
153                expired_at: None,
154                terms_agreed: None,
155                others,
156            },
157        }
158    }
159
160    pub const fn email(mut self, email: &'a str) -> Self {
161        self.fields.email = Some(email);
162        self
163    }
164
165    pub const fn expired_at(mut self, expired_at: u64) -> Self {
166        self.fields.expired_at = Some(expired_at);
167        self
168    }
169
170    pub const fn terms_agreed(mut self, terms_agreed: bool) -> Self {
171        self.fields.terms_agreed = Some(terms_agreed);
172        self
173    }
174}
175
176impl<'a> ExternalAuth<'a, PSN<'a>> {
177    pub const fn env(mut self, env: u32) -> Self {
178        self.fields.others.env = Some(env);
179        self
180    }
181}
182
183pub trait Provider {
184    fn route() -> Route;
185}
186
187macro_rules! impl_provider {
188    ($($Type:ident: $route:expr)*) => {
189        $(impl<'a> Provider for $Type<'a> {
190            fn route() -> Route {
191                $route
192            }
193        })*
194    };
195}
196
197impl_provider! {
198    Discord: Route::ExternalAuthDiscord
199    EpicGames: Route::ExternalAuthEpic
200    Google: Route::ExternalAuthGoogle
201    MetaQuest: Route::ExternalAuthMeta
202    OpenID: Route::ExternalAuthOpenID
203    PSN: Route::ExternalAuthPSN
204    Steam: Route::ExternalAuthSteam
205    Switch: Route::ExternalAuthSwitch
206    Xbox: Route::ExternalAuthXbox
207}
208
209impl<T: Serialize + Provider> IntoFuture for ExternalAuth<'_, T> {
210    type Output = Output<AccessToken>;
211    type IntoFuture = ResponseFuture<AccessToken>;
212
213    fn into_future(self) -> Self::IntoFuture {
214        let route = T::route();
215        match RequestBuilder::from_route(&route).form(&self.fields) {
216            Ok(req) => self.http.request(req),
217            Err(err) => ResponseFuture::failed(err),
218        }
219    }
220}