steamkit/steam/authentication/
user_console_authenticator.rs1use async_trait::async_trait;
7use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, Stdin};
8use tokio::sync::Mutex;
9
10use crate::error::SteamError;
11
12use super::i_authenticator::IAuthenticator;
13
14#[derive(Debug)]
16pub struct UserConsoleAuthenticator {
17 reader: Mutex<BufReader<Stdin>>,
18}
19
20impl Default for UserConsoleAuthenticator {
21 fn default() -> Self {
22 Self::new()
23 }
24}
25
26impl UserConsoleAuthenticator {
27 pub fn new() -> Self {
29 UserConsoleAuthenticator {
30 reader: Mutex::new(BufReader::new(tokio::io::stdin())),
31 }
32 }
33
34 async fn read_line(&self) -> Result<Option<String>, SteamError> {
36 let mut line = String::new();
37
38 let read = {
39 let mut reader = self.reader.lock().await;
40 reader.read_line(&mut line).await?
41 };
42
43 if read == 0 {
44 return Ok(None);
45 }
46
47 Ok(Some(line.trim().to_string()))
48 }
49
50 async fn write_prompt(prompt: &str) -> Result<(), SteamError> {
52 let mut stderr = tokio::io::stderr();
53 stderr.write_all(prompt.as_bytes()).await?;
54 stderr.flush().await?;
55
56 Ok(())
57 }
58}
59
60#[async_trait]
61impl IAuthenticator for UserConsoleAuthenticator {
62 async fn get_device_code(
64 &self,
65 previous_code_was_incorrect: bool,
66 ) -> Result<String, SteamError> {
67 if previous_code_was_incorrect {
68 Self::write_prompt("The previous 2-factor auth code you have provided is incorrect.\n")
69 .await?;
70 }
71
72 loop {
73 Self::write_prompt(
74 "STEAM GUARD! Please enter your 2-factor auth code from your authenticator app: ",
75 )
76 .await?;
77
78 let Some(code) = self.read_line().await? else {
79 return Ok(String::new());
80 };
81
82 if !code.is_empty() {
83 return Ok(code);
84 }
85 }
86 }
87
88 async fn get_email_code(
90 &self,
91 email: &str,
92 previous_code_was_incorrect: bool,
93 ) -> Result<String, SteamError> {
94 if previous_code_was_incorrect {
95 Self::write_prompt("The previous 2-factor auth code you have provided is incorrect.\n")
96 .await?;
97 }
98
99 loop {
100 Self::write_prompt(&format!(
101 "STEAM GUARD! Please enter the auth code sent to the email at {email}: "
102 ))
103 .await?;
104
105 let Some(code) = self.read_line().await? else {
106 return Ok(String::new());
107 };
108
109 if !code.is_empty() {
110 return Ok(code);
111 }
112 }
113 }
114
115 async fn accept_device_confirmation(&self) -> Result<bool, SteamError> {
117 Self::write_prompt("STEAM GUARD! Use the Steam Mobile App to confirm your sign in...\n")
118 .await?;
119
120 Ok(true)
121 }
122}