license_api/auth/
authenticator.rs1use std::collections::HashMap;
2use std::error::Error;
3use log::debug;
4use reqwest::StatusCode;
5use crate::auth::models::{ErrorResponse, LoginRequest, LoginResponse, MeResponse};
6use crate::auth::traits::Authenticator;
7
8pub const NOT_LINKED: &str = "not_linked";
9
10pub struct BasicAuthenticator {
11 pub base_url: String,
12 pub client: reqwest::Client,
13}
14
15impl BasicAuthenticator {
16 pub fn new(base_url: impl Into<String>) -> Self {
17 BasicAuthenticator {
18 base_url: base_url.into(),
19 client: reqwest::Client::new(),
20 }
21 }
22}
23
24#[async_trait::async_trait]
25impl Authenticator for BasicAuthenticator {
26 async fn login(
27 &self,
28 creds: &LoginRequest,
29 hwid: &str,
30 ) -> Result<LoginResponse, Box<dyn Error + Send + Sync>> {
31 let url = format!("{}/auth/login", self.base_url.trim_end_matches('/'));
32
33 let resp = self.client.post(&url).form(&creds).send().await?;
34
35 match resp.status() {
36 StatusCode::OK | StatusCode::CREATED => {
37 let lr = resp.json::<LoginResponse>().await?;
38
39 let me = &self.me(&lr.access_token).await?;
40
41 match me.hwid.as_str() {
42 NOT_LINKED => {
43 let _ = &self
44 .link_hwid(&hwid, &lr.access_token)
45 .await?;
46 debug!("hwid successfully linked {}", me.username);
47 }
48 h if h == hwid => {
49 debug!("welcome back, {}!", me.username);
50 }
51 _ => {
52 debug!("hwid mismatch! access denied.");
53 }
54 }
55
56 debug!("login successful");
57
58 Ok(lr)
59 }
60 StatusCode::UNAUTHORIZED | StatusCode::BAD_REQUEST => {
61 let err = resp.json::<ErrorResponse>().await?;
62 Err(format!("{}", err.detail).into())
63 }
64 other => Err(format!("unexpected response status: {}", other).into()),
65 }
66 }
67
68 async fn me(&self, access_token: &str) -> Result<MeResponse, Box<dyn Error + Send + Sync>> {
69 let url = format!("{}/users/me", self.base_url.trim_end_matches('/'));
70
71 let resp = self
72 .client
73 .get(&url)
74 .bearer_auth(access_token)
75 .send()
76 .await?;
77
78 match resp.status() {
79 StatusCode::OK => {
80 let mr = resp.json::<MeResponse>().await?;
81 debug!("getting user info...");
82
83 Ok(mr)
84 }
85 StatusCode::UNAUTHORIZED | StatusCode::BAD_REQUEST => {
86 let err = resp.json::<ErrorResponse>().await?;
87 Err(format!("{}", err.detail).into())
88 }
89 other => Err(format!("unexpected response status: {}", other).into()),
90 }
91 }
92
93 async fn link_hwid(
94 &self,
95 hwid: &str,
96 access_token: &str,
97 ) -> Result<String, Box<dyn Error + Send + Sync>> {
98 let url = format!("{}/users/hwid", self.base_url.trim_end_matches('/'));
99
100 let mut map = HashMap::new();
101 map.insert("value", hwid);
102
103 let resp = self
104 .client
105 .patch(&url)
106 .json(&map)
107 .bearer_auth(access_token)
108 .send()
109 .await?;
110
111 match resp.status() {
112 StatusCode::OK => {
113 debug!("hwid successfully linked");
114
115 Ok(resp.text().await?)
116 },
117 StatusCode::UNAUTHORIZED | StatusCode::BAD_REQUEST => {
118 let err = resp.json::<ErrorResponse>().await?;
119 Err(format!("link failed: {}", err.detail).into())
120 }
121 other => Err(format!("unexpected response status: {}", other).into()),
122 }
123 }
124}