1+ use base32:: Alphabet ;
2+ use vrchatapi:: models:: { EitherUserOrTwoFactor , TwoFactorAuthCode } ;
3+
4+ #[ derive( Debug , thiserror:: Error ) ]
5+ enum Error {
6+ #[ error( "Dotenv failed to initialize: {0}" ) ]
7+ DotenvInitError ( dotenv:: Error ) ,
8+ #[ error( "No Username provided or error decoding username: {0}" ) ]
9+ NoUsername ( dotenv:: Error ) ,
10+ #[ error( "No Password provided or error decoding password: {0}" ) ]
11+ NoPassword ( dotenv:: Error ) ,
12+ #[ error( "No Totp Secret provided or error decoding totp secret: {0}" ) ]
13+ NoTotpSecret ( dotenv:: Error ) ,
14+ #[ error( "We were unable to login with the provided credentials." ) ]
15+ UnableToLogin ,
16+ #[ error( "No totp 2fa variant available." ) ]
17+ NoTotp2FAAvailable ,
18+ #[ error( "Failed to get the current user: {0}" ) ]
19+ GetCurrentUser ( #[ from] vrchatapi:: apis:: Error < vrchatapi:: apis:: authentication_api:: GetCurrentUserError > ) ,
20+ #[ error( "Failed to verify with 2fa: {0}" ) ]
21+ Verify2FA ( #[ from] vrchatapi:: apis:: Error < vrchatapi:: apis:: authentication_api:: Verify2FaError > ) ,
22+ #[ error( "Failed to logout: {0}" ) ]
23+ Logout ( #[ from] vrchatapi:: apis:: Error < vrchatapi:: apis:: authentication_api:: LogoutError > ) ,
24+ #[ error( "Failed to decode Totp from base32" ) ]
25+ TOTPBase32 ,
26+ }
27+
28+ const VERSION : & str = env ! ( "CARGO_PKG_VERSION" ) ;
29+ #[ tokio:: main]
30+ async fn main ( ) {
31+ println ! ( "Hello" ) ;
32+ match test ( ) . await {
33+ Ok ( ( ) ) => { }
34+ Err ( e) => eprint ! ( "{e}" )
35+ }
36+ }
37+ async fn test ( ) -> Result < ( ) , anyhow:: Error > {
38+ dotenv:: dotenv ( ) . map_err ( |e|Error :: DotenvInitError ( e) ) ?;
39+ let username = dotenv:: var ( "USERNAME" ) . map_err ( |e|Error :: NoUsername ( e) ) ?;
40+ let password = dotenv:: var ( "PASSWORD" ) . map_err ( |e|Error :: NoPassword ( e) ) ?;
41+ let totp_secret = dotenv:: var ( "TOTP_SECRET" ) . map_err ( |e|Error :: NoTotpSecret ( e) ) ?;
42+ let totp_secret = base32:: decode ( Alphabet :: Rfc4648Lower { padding : false } , totp_secret. as_str ( ) ) . ok_or ( Error :: TOTPBase32 ) ?;
43+ let generator = totp_rfc6238:: TotpGenerator :: new ( )
44+ . build ( ) ;
45+
46+ let mut client = vrchatapi:: apis:: configuration:: Configuration :: default ( ) ;
47+ client. user_agent = Some ( format ! ( "vrchatapi-rust@{VERSION} https://github.com/vrchatapi/vrchatapi-rust/issues/new" ) ) ;
48+ client. basic_auth = Some ( ( username. clone ( ) , Some ( password) ) ) ;
49+ let u = match vrchatapi:: apis:: authentication_api:: get_current_user ( & client) . await . map_err ( |e|Error :: GetCurrentUser ( e) ) ? {
50+ EitherUserOrTwoFactor :: CurrentUser ( u) => u,
51+ EitherUserOrTwoFactor :: RequiresTwoFactorAuth ( r2fa) => {
52+ if !r2fa. requires_two_factor_auth . contains ( & "totp" . to_string ( ) ) {
53+ let _ = logout ( & client) . await ; //Ignore logout error. This is just here, to hopefully avoid session spam.
54+ return Err ( Error :: NoTotp2FAAvailable ) ?;
55+ }
56+
57+ let code = generator. get_code ( totp_secret. as_slice ( ) ) ;
58+ println ! ( "Generated code: {code}" ) ;
59+ if !vrchatapi:: apis:: authentication_api:: verify2_fa ( & client, TwoFactorAuthCode :: new ( code) ) . await . map_err ( |e|Error :: Verify2FA ( e) ) ?. verified {
60+ let _ = logout ( & client) . await ; //Ignore logout error. This is just here, to hopefully avoid session spam.
61+ return Err ( Error :: UnableToLogin ) ?;
62+ }
63+
64+ match vrchatapi:: apis:: authentication_api:: get_current_user ( & client) . await . map_err ( |e|Error :: GetCurrentUser ( e) ) ? {
65+ EitherUserOrTwoFactor :: CurrentUser ( u) => u,
66+ EitherUserOrTwoFactor :: RequiresTwoFactorAuth ( _) => return Err ( Error :: UnableToLogin ) ?,
67+ }
68+ }
69+ } ;
70+ println ! ( "Logged in as: {} (Login Name was {username}" , u. username. as_ref( ) . map( String :: as_str) . unwrap_or( "Unknown User" ) ) ;
71+
72+ logout ( & client) . await ?;
73+ Ok ( ( ) )
74+ }
75+
76+ async fn logout ( client : & vrchatapi:: apis:: configuration:: Configuration ) -> Result < ( ) , anyhow:: Error > {
77+ vrchatapi:: apis:: authentication_api:: logout ( & client) . await . map_err ( |e|Error :: Logout ( e) ) ?;
78+ Ok ( ( ) )
79+ }
0 commit comments