1use std::collections::BTreeMap;
3use std::fmt;
4
5use crate::routing::Route;
6use crate::types::auth::AccessToken;
7use crate::types::{Message, Timestamp};
8use crate::Modio;
9use crate::Result;
10
11pub use crate::types::auth::{Link, Links, Terms};
12
13#[derive(Clone, Eq, PartialEq)]
15pub struct Credentials {
16 pub api_key: String,
17 pub token: Option<Token>,
18}
19
20#[derive(Clone, Eq, PartialEq)]
22pub struct Token {
23 pub value: String,
24 pub expired_at: Option<Timestamp>,
25}
26
27impl fmt::Debug for Credentials {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 if self.token.is_some() {
30 f.write_str("Credentials(apikey+token)")
31 } else {
32 f.write_str("Credentials(apikey)")
33 }
34 }
35}
36
37impl Credentials {
38 pub fn new<S: Into<String>>(api_key: S) -> Credentials {
39 Credentials {
40 api_key: api_key.into(),
41 token: None,
42 }
43 }
44
45 pub fn with_token<S: Into<String>, T: Into<String>>(api_key: S, token: T) -> Credentials {
46 Credentials {
47 api_key: api_key.into(),
48 token: Some(Token {
49 value: token.into(),
50 expired_at: None,
51 }),
52 }
53 }
54}
55
56impl From<&str> for Credentials {
57 fn from(api_key: &str) -> Credentials {
58 Credentials::new(api_key)
59 }
60}
61
62impl From<(&str, &str)> for Credentials {
63 fn from((api_key, token): (&str, &str)) -> Credentials {
64 Credentials::with_token(api_key, token)
65 }
66}
67
68impl From<String> for Credentials {
69 fn from(api_key: String) -> Credentials {
70 Credentials::new(api_key)
71 }
72}
73
74impl From<(String, String)> for Credentials {
75 fn from((api_key, token): (String, String)) -> Credentials {
76 Credentials::with_token(api_key, token)
77 }
78}
79
80#[derive(Clone)]
114pub struct Auth {
115 modio: Modio,
116}
117
118impl Auth {
119 pub(crate) fn new(modio: Modio) -> Self {
120 Self { modio }
121 }
122
123 pub async fn terms(self) -> Result<Terms> {
127 self.modio.request(Route::Terms).send().await
128 }
129
130 pub async fn request_code(self, email: &str) -> Result<()> {
132 self.modio
133 .request(Route::OAuthEmailRequest)
134 .form(&[("email", email)])
135 .send::<Message>()
136 .await?;
137
138 Ok(())
139 }
140
141 pub async fn security_code(self, code: &str) -> Result<Credentials> {
143 let t = self
144 .modio
145 .request(Route::OAuthEmailExchange)
146 .form(&[("security_code", code)])
147 .send::<AccessToken>()
148 .await?;
149
150 let token = Token {
151 value: t.value,
152 expired_at: t.expired_at,
153 };
154 Ok(Credentials {
155 api_key: self.modio.inner.credentials.api_key.clone(),
156 token: Some(token),
157 })
158 }
159
160 pub async fn external<T>(self, auth_options: T) -> Result<Credentials>
185 where
186 T: Into<AuthOptions>,
187 {
188 let AuthOptions { route, params } = auth_options.into();
189
190 let t = self
191 .modio
192 .request(route)
193 .form(¶ms)
194 .send::<AccessToken>()
195 .await?;
196
197 let token = Token {
198 value: t.value,
199 expired_at: t.expired_at,
200 };
201 Ok(Credentials {
202 api_key: self.modio.inner.credentials.api_key.clone(),
203 token: Some(token),
204 })
205 }
206
207 pub async fn logout(self) -> Result<()> {
209 self.modio
210 .request(Route::OAuthLogout)
211 .send::<Message>()
212 .await?;
213
214 Ok(())
215 }
216}
217
218pub struct AuthOptions {
220 route: Route,
221 params: BTreeMap<&'static str, String>,
222}
223
224impl From<OculusOptions> for AuthOptions {
226 fn from(options: OculusOptions) -> AuthOptions {
227 AuthOptions {
228 route: Route::ExternalAuthMeta,
229 params: options.params,
230 }
231 }
232}
233
234impl From<SteamOptions> for AuthOptions {
235 fn from(options: SteamOptions) -> AuthOptions {
236 AuthOptions {
237 route: Route::ExternalAuthSteam,
238 params: options.params,
239 }
240 }
241}
242
243impl From<SwitchOptions> for AuthOptions {
244 fn from(options: SwitchOptions) -> AuthOptions {
245 AuthOptions {
246 route: Route::ExternalAuthSwitch,
247 params: options.params,
248 }
249 }
250}
251
252impl From<XboxOptions> for AuthOptions {
253 fn from(options: XboxOptions) -> AuthOptions {
254 AuthOptions {
255 route: Route::ExternalAuthXbox,
256 params: options.params,
257 }
258 }
259}
260
261impl From<DiscordOptions> for AuthOptions {
262 fn from(options: DiscordOptions) -> AuthOptions {
263 AuthOptions {
264 route: Route::ExternalAuthDiscord,
265 params: options.params,
266 }
267 }
268}
269
270impl From<GoogleOptions> for AuthOptions {
271 fn from(options: GoogleOptions) -> AuthOptions {
272 AuthOptions {
273 route: Route::ExternalAuthGoogle,
274 params: options.params,
275 }
276 }
277}
278pub struct GalaxyOptions {
284 params: BTreeMap<&'static str, String>,
285}
286
287impl GalaxyOptions {
288 pub fn new<T>(ticket: T) -> Self
289 where
290 T: Into<String>,
291 {
292 let mut params = BTreeMap::new();
293 params.insert("appdata", ticket.into());
294 Self { params }
295 }
296
297 option!(email >> "email");
298 option!(
299 expired_at u64 >> "date_expires"
302 );
303 option!(terms_agreed bool >> "terms_agreed");
304}
305
306pub struct ItchioOptions {
310 params: BTreeMap<&'static str, String>,
311}
312
313impl ItchioOptions {
314 pub fn new<T>(token: T) -> Self
315 where
316 T: Into<String>,
317 {
318 let mut params = BTreeMap::new();
319 params.insert("itchio_token", token.into());
320 Self { params }
321 }
322
323 option!(email >> "email");
324 option!(
325 expired_at u64 >> "date_expires"
328 );
329 option!(terms_agreed bool >> "terms_agreed");
330}
331
332pub struct OculusOptions {
336 params: BTreeMap<&'static str, String>,
337}
338
339impl OculusOptions {
340 pub fn new_for_quest<T>(nonce: T, user_id: u64, auth_token: T) -> Self
341 where
342 T: Into<String>,
343 {
344 OculusOptions::new("quest".to_owned(), nonce.into(), user_id, auth_token.into())
345 }
346
347 pub fn new_for_rift<T>(nonce: T, user_id: u64, auth_token: T) -> Self
348 where
349 T: Into<String>,
350 {
351 OculusOptions::new("rift".to_owned(), nonce.into(), user_id, auth_token.into())
352 }
353
354 fn new(device: String, nonce: String, user_id: u64, auth_token: String) -> Self {
355 let mut params = BTreeMap::new();
356 params.insert("device", device);
357 params.insert("nonce", nonce);
358 params.insert("user_id", user_id.to_string());
359 params.insert("auth_token", auth_token);
360 Self { params }
361 }
362
363 option!(email >> "email");
364 option!(
365 expired_at u64 >> "date_expires"
368 );
369 option!(terms_agreed bool >> "terms_agreed");
370}
371
372pub struct SteamOptions {
376 params: BTreeMap<&'static str, String>,
377}
378
379impl SteamOptions {
380 pub fn new<T>(ticket: T) -> Self
381 where
382 T: Into<String>,
383 {
384 let mut params = BTreeMap::new();
385 params.insert("appdata", ticket.into());
386 Self { params }
387 }
388
389 option!(email >> "email");
390 option!(
391 expired_at u64 >> "date_expires"
394 );
395 option!(terms_agreed bool >> "terms_agreed");
396}
397
398pub struct SwitchOptions {
402 params: BTreeMap<&'static str, String>,
403}
404
405impl SwitchOptions {
406 pub fn new<T>(id_token: T) -> Self
407 where
408 T: Into<String>,
409 {
410 let mut params = BTreeMap::new();
411 params.insert("id_token", id_token.into());
412 Self { params }
413 }
414
415 option!(email >> "email");
416 option!(
417 expired_at u64 >> "date_expires"
420 );
421 option!(terms_agreed bool >> "terms_agreed");
422}
423
424pub struct XboxOptions {
428 params: BTreeMap<&'static str, String>,
429}
430
431impl XboxOptions {
432 pub fn new<T>(token: T) -> Self
433 where
434 T: Into<String>,
435 {
436 let mut params = BTreeMap::new();
437 params.insert("xbox_token", token.into());
438 Self { params }
439 }
440
441 option!(email >> "email");
442 option!(
443 expired_at u64 >> "date_expires"
446 );
447 option!(terms_agreed bool >> "terms_agreed");
448}
449
450pub struct DiscordOptions {
454 params: BTreeMap<&'static str, String>,
455}
456
457impl DiscordOptions {
458 pub fn new<T>(token: T) -> Self
459 where
460 T: Into<String>,
461 {
462 let mut params = BTreeMap::new();
463 params.insert("discord_token", token.into());
464 Self { params }
465 }
466
467 option!(email >> "email");
468 option!(
469 expired_at u64 >> "date_expires"
472 );
473 option!(terms_agreed bool >> "terms_agreed");
474}
475
476pub struct GoogleOptions {
480 params: BTreeMap<&'static str, String>,
481}
482
483impl GoogleOptions {
484 pub fn new<T>(token: T) -> Self
485 where
486 T: Into<String>,
487 {
488 let mut params = BTreeMap::new();
489 params.insert("id_token", token.into());
490 Self { params }
491 }
492
493 option!(email >> "email");
494 option!(
495 expired_at u64 >> "date_expires"
498 );
499 option!(terms_agreed bool >> "terms_agreed");
500}
501
502