1#![allow(dead_code, clippy::missing_errors_doc)]
5#![warn(missing_docs, missing_doc_code_examples)]
6#![deny(
7 missing_debug_implementations,
8 missing_copy_implementations,
9 trivial_casts,
10 trivial_numeric_casts,
11 unsafe_code,
12 unused_import_braces,
13 unused_qualifications
14)]
15
16use std::fmt;
17use std::fmt::Debug;
18use std::fmt::Formatter;
19use std::path::PathBuf;
20use std::sync::Arc;
21
22pub use client::Authenticated;
23pub use client::SteamAuthenticator;
24pub use client::Unauthenticated;
25use const_format::concatcp;
26use parking_lot::RwLock;
27pub use reqwest::header::HeaderMap;
28pub use reqwest::Error as HttpError;
29pub use reqwest::Method;
30pub use reqwest::Url;
31use serde::Deserialize;
32use serde::Serialize;
33use steamid_parser::SteamID;
34pub use utils::format_captcha_url;
35use uuid::Uuid;
36pub use web_handler::confirmation::Confirmation;
37pub use web_handler::confirmation::ConfirmationAction;
38pub use web_handler::confirmation::Confirmations;
39pub use web_handler::confirmation::EConfirmationType;
40pub use web_handler::steam_guard_linker::AddAuthenticatorStep;
41
42use crate::errors::AuthError;
43use crate::errors::InternalError;
44use crate::errors::MobileAuthFileError;
45use crate::utils::read_from_disk;
46
47mod adapter;
48pub(crate) mod client;
49pub mod errors;
50mod page_scraper;
51pub(crate) mod retry;
52mod types;
53pub mod user;
54pub(crate) mod utils;
55mod web_handler;
56
57const STEAM_DELAY_MS: u64 = 350;
59const MA_FILE_EXT: &str = ".maFile";
61
62pub const STEAM_COMMUNITY_HOST: &str = "steamcommunity.com";
66pub const STEAM_HELP_HOST: &str = ".help.steampowered.com";
68pub const STEAM_STORE_HOST: &str = ".store.steampowered.com";
70
71pub(crate) const STEAM_COMMUNITY_BASE: &str = "https://steamcommunity.com";
73pub(crate) const STEAM_STORE_BASE: &str = "https://store.steampowered.com";
75pub(crate) const STEAM_API_BASE: &str = "https://api.steampowered.com";
77
78pub(crate) const STEAM_LOGIN_BASE: &str = "https://login.steampowered.com";
79
80const MOBILE_REFERER: &str = concatcp!(
81 STEAM_COMMUNITY_BASE,
82 "/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client"
83);
84
85#[allow(missing_docs)]
86pub type AuthResult<T> = Result<T, AuthError>;
87
88#[derive(Debug, Clone)]
90struct SteamCache {
91 steamid: SteamID,
92 api_key: Option<String>,
93 oauth_token: String,
95 access_token: String,
96}
97
98pub(crate) type CacheGuard = Arc<RwLock<SteamCache>>;
99
100impl SteamCache {
101 fn query_tokens(&self) -> Vec<(&'static str, String)> {
102 [("access_token", self.access_token.clone())].to_vec()
103 }
104
105 fn with_login_data(steamid: &str, access_token: String, refresh_token: String) -> Result<Self, InternalError> {
106 let parsed_steamid = SteamID::parse(steamid).ok_or_else(|| {
107 let err_str = format!("Failed to parse {steamid} as SteamID.");
108 InternalError::GeneralFailure(err_str)
109 })?;
110
111 Ok(Self {
112 steamid: parsed_steamid,
113 api_key: None,
114 oauth_token: refresh_token,
115 access_token,
116 })
117 }
118
119 fn set_api_key(&mut self, api_key: Option<String>) {
120 self.api_key = api_key;
121 }
122
123 fn api_key(&self) -> Option<&str> {
124 self.api_key.as_deref()
125 }
126
127 fn steam_id(&self) -> u64 {
128 self.steamid.to_steam64()
129 }
130
131 fn oauth_token(&self) -> &str {
132 &self.oauth_token
133 }
134}
135
136#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
153pub struct MobileAuthFile {
154 identity_secret: String,
157 shared_secret: String,
159 device_id: Option<String>,
163 revocation_code: Option<String>,
165 pub account_name: Option<String>,
167}
168
169impl Debug for MobileAuthFile {
170 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
171 f.debug_struct("MobileAuthFile")
172 .field("AccountName", &self.account_name)
173 .finish()
174 }
175}
176
177impl MobileAuthFile {
178 fn set_device_id(&mut self, device_id: String) {
179 self.device_id = Some(device_id);
180 }
181
182 pub fn new<T>(identity_secret: String, shared_secret: String, device_id: T) -> Self
184 where
185 T: Into<Option<String>>,
186 {
187 Self {
188 identity_secret,
189 shared_secret,
190 device_id: device_id.into(),
191 revocation_code: None,
192 account_name: None,
193 }
194 }
195
196 pub fn from_json(string: &str) -> Result<Self, MobileAuthFileError> {
198 serde_json::from_str::<Self>(string)
199 .map_err(|e| MobileAuthFileError::InternalError(InternalError::DeserializationError(e)))
200 }
201
202 pub fn from_disk<T>(path: T) -> Result<Self, MobileAuthFileError>
207 where
208 T: Into<PathBuf>,
209 {
210 let buffer = read_from_disk(path);
211 Self::from_json(&buffer)
212 }
213}
214
215#[derive(Serialize, Deserialize, Debug)]
216struct DeviceId(String);
220
221impl DeviceId {
222 const PREFIX: &'static str = "android:";
223
224 pub fn generate() -> Self {
227 Self(Self::PREFIX.to_owned() + &Uuid::new_v4().to_string())
228 }
229 pub fn validate() {}
230}