@@ -50,11 +50,69 @@ impl AGiXTSDK {
5050
5151 // ==================== Authentication ====================
5252
53- /// Login to the AGiXT server.
54- pub async fn login ( & self , email : & str , otp : & str ) -> Result < Option < String > > {
53+ /// Login with username/password authentication.
54+ ///
55+ /// # Arguments
56+ /// * `username` - Username or email address
57+ /// * `password` - User's password
58+ /// * `mfa_token` - Optional TOTP code if MFA is enabled
59+ ///
60+ /// # Returns
61+ /// JWT token on success, or None on failure
62+ pub async fn login ( & self , username : & str , password : & str , mfa_token : Option < & str > ) -> Result < Option < String > > {
63+ let mut payload = serde_json:: json!( {
64+ "username" : username,
65+ "password" : password,
66+ } ) ;
67+ if let Some ( token) = mfa_token {
68+ payload[ "mfa_token" ] = serde_json:: json!( token) ;
69+ }
70+
5571 let response = self
5672 . client
5773 . post ( & format ! ( "{}/v1/login" , self . base_uri) )
74+ . json ( & payload)
75+ . send ( )
76+ . await ?;
77+
78+ let status = response. status ( ) ;
79+ let text = response. text ( ) . await ?;
80+
81+ if self . verbose {
82+ self . parse_response ( status, & text) . await ?;
83+ }
84+
85+ let json: serde_json:: Value = serde_json:: from_str ( & text) ?;
86+
87+ // Check for token in response (new auth flow)
88+ if status. is_success ( ) {
89+ if let Some ( token) = json. get ( "token" ) . and_then ( |t| t. as_str ( ) ) {
90+ let mut headers = self . headers . lock ( ) . await ;
91+ if let Ok ( value) = HeaderValue :: from_str ( token) {
92+ headers. insert ( AUTHORIZATION , value) ;
93+ }
94+ if self . verbose {
95+ println ! ( "Logged in successfully" ) ;
96+ }
97+ return Ok ( Some ( token. to_string ( ) ) ) ;
98+ }
99+ }
100+ Ok ( None )
101+ }
102+
103+ /// Legacy login with magic link (email + OTP token).
104+ /// Maintained for backward compatibility.
105+ ///
106+ /// # Arguments
107+ /// * `email` - User's email address
108+ /// * `otp` - TOTP code from authenticator app
109+ ///
110+ /// # Returns
111+ /// JWT token on success, or None on failure
112+ pub async fn login_magic_link ( & self , email : & str , otp : & str ) -> Result < Option < String > > {
113+ let response = self
114+ . client
115+ . post ( & format ! ( "{}/v1/login/magic-link" , self . base_uri) )
58116 . json ( & serde_json:: json!( {
59117 "email" : email,
60118 "token" : otp,
@@ -70,10 +128,7 @@ impl AGiXTSDK {
70128 }
71129
72130 let json: serde_json:: Value = serde_json:: from_str ( & text) ?;
73- self . process_login_response ( json) . await
74- }
75-
76- async fn process_login_response ( & self , json : serde_json:: Value ) -> Result < Option < String > > {
131+
77132 if let Some ( detail) = json. get ( "detail" ) . and_then ( |d| d. as_str ( ) ) {
78133 if detail. contains ( "?token=" ) {
79134 let token = detail. split ( "token=" ) . nth ( 1 ) . unwrap_or_default ( ) ;
@@ -88,15 +143,184 @@ impl AGiXTSDK {
88143 Ok ( None )
89144 }
90145
91- /// Register a new user.
92- pub async fn register_user ( & self , email : & str , first_name : & str , last_name : & str ) -> Result < String > {
146+ /// Register a new user with username/password authentication.
147+ ///
148+ /// # Arguments
149+ /// * `email` - User's email address
150+ /// * `password` - User's password
151+ /// * `confirm_password` - Password confirmation
152+ /// * `first_name` - User's first name (optional)
153+ /// * `last_name` - User's last name (optional)
154+ /// * `username` - Desired username (optional)
155+ /// * `organization_name` - Company/organization name (optional)
156+ ///
157+ /// # Returns
158+ /// Response JSON with user_id, username, token on success
159+ pub async fn register_user (
160+ & self ,
161+ email : & str ,
162+ password : & str ,
163+ confirm_password : & str ,
164+ first_name : Option < & str > ,
165+ last_name : Option < & str > ,
166+ username : Option < & str > ,
167+ organization_name : Option < & str > ,
168+ ) -> Result < serde_json:: Value > {
169+ let mut payload = serde_json:: json!( {
170+ "email" : email,
171+ "password" : password,
172+ "confirm_password" : confirm_password,
173+ "first_name" : first_name. unwrap_or( "" ) ,
174+ "last_name" : last_name. unwrap_or( "" ) ,
175+ } ) ;
176+ if let Some ( u) = username {
177+ payload[ "username" ] = serde_json:: json!( u) ;
178+ }
179+ if let Some ( org) = organization_name {
180+ payload[ "organization_name" ] = serde_json:: json!( org) ;
181+ }
182+
93183 let response = self
94184 . client
95185 . post ( & format ! ( "{}/v1/user" , self . base_uri) )
186+ . json ( & payload)
187+ . send ( )
188+ . await ?;
189+
190+ let status = response. status ( ) ;
191+ let text = response. text ( ) . await ?;
192+
193+ if self . verbose {
194+ self . parse_response ( status, & text) . await ?;
195+ }
196+
197+ let json: serde_json:: Value = serde_json:: from_str ( & text) ?;
198+
199+ // Auto-login if token is returned
200+ if status. is_success ( ) {
201+ if let Some ( token) = json. get ( "token" ) . and_then ( |t| t. as_str ( ) ) {
202+ let mut headers = self . headers . lock ( ) . await ;
203+ if let Ok ( value) = HeaderValue :: from_str ( token) {
204+ headers. insert ( AUTHORIZATION , value) ;
205+ }
206+ if self . verbose {
207+ println ! ( "Registered and logged in as {}" , json. get( "username" ) . and_then( |u| u. as_str( ) ) . unwrap_or( "" ) ) ;
208+ }
209+ }
210+ }
211+
212+ Ok ( json)
213+ }
214+
215+ /// Get MFA setup information including QR code URI.
216+ ///
217+ /// # Returns
218+ /// JSON with provisioning_uri, secret, and mfa_enabled status
219+ pub async fn get_mfa_setup ( & self ) -> Result < serde_json:: Value > {
220+ let response = self
221+ . client
222+ . get ( & format ! ( "{}/v1/user/mfa/setup" , self . base_uri) )
223+ . headers ( self . headers . lock ( ) . await . clone ( ) )
224+ . send ( )
225+ . await ?;
226+
227+ let status = response. status ( ) ;
228+ let text = response. text ( ) . await ?;
229+
230+ if self . verbose {
231+ self . parse_response ( status, & text) . await ?;
232+ }
233+
234+ let json: serde_json:: Value = serde_json:: from_str ( & text) ?;
235+ Ok ( json)
236+ }
237+
238+ /// Enable MFA for the current user.
239+ ///
240+ /// # Arguments
241+ /// * `mfa_token` - TOTP code from authenticator app to verify setup
242+ ///
243+ /// # Returns
244+ /// Response JSON with success message
245+ pub async fn enable_mfa ( & self , mfa_token : & str ) -> Result < serde_json:: Value > {
246+ let response = self
247+ . client
248+ . post ( & format ! ( "{}/v1/user/mfa/enable" , self . base_uri) )
249+ . headers ( self . headers . lock ( ) . await . clone ( ) )
250+ . json ( & serde_json:: json!( { "mfa_token" : mfa_token } ) )
251+ . send ( )
252+ . await ?;
253+
254+ let status = response. status ( ) ;
255+ let text = response. text ( ) . await ?;
256+
257+ if self . verbose {
258+ self . parse_response ( status, & text) . await ?;
259+ }
260+
261+ let json: serde_json:: Value = serde_json:: from_str ( & text) ?;
262+ Ok ( json)
263+ }
264+
265+ /// Disable MFA for the current user.
266+ ///
267+ /// # Arguments
268+ /// * `password` - User's password (optional)
269+ /// * `mfa_token` - Current TOTP code (optional)
270+ ///
271+ /// # Returns
272+ /// Response JSON with success message
273+ pub async fn disable_mfa ( & self , password : Option < & str > , mfa_token : Option < & str > ) -> Result < serde_json:: Value > {
274+ let mut payload = serde_json:: json!( { } ) ;
275+ if let Some ( p) = password {
276+ payload[ "password" ] = serde_json:: json!( p) ;
277+ }
278+ if let Some ( t) = mfa_token {
279+ payload[ "mfa_token" ] = serde_json:: json!( t) ;
280+ }
281+
282+ let response = self
283+ . client
284+ . post ( & format ! ( "{}/v1/user/mfa/disable" , self . base_uri) )
285+ . headers ( self . headers . lock ( ) . await . clone ( ) )
286+ . json ( & payload)
287+ . send ( )
288+ . await ?;
289+
290+ let status = response. status ( ) ;
291+ let text = response. text ( ) . await ?;
292+
293+ if self . verbose {
294+ self . parse_response ( status, & text) . await ?;
295+ }
296+
297+ let json: serde_json:: Value = serde_json:: from_str ( & text) ?;
298+ Ok ( json)
299+ }
300+
301+ /// Change the current user's password.
302+ ///
303+ /// # Arguments
304+ /// * `current_password` - Current password
305+ /// * `new_password` - New password
306+ /// * `confirm_password` - New password confirmation
307+ ///
308+ /// # Returns
309+ /// Response JSON with success message
310+ pub async fn change_password (
311+ & self ,
312+ current_password : & str ,
313+ new_password : & str ,
314+ confirm_password : & str ,
315+ ) -> Result < serde_json:: Value > {
316+ let response = self
317+ . client
318+ . post ( & format ! ( "{}/v1/user/password/change" , self . base_uri) )
319+ . headers ( self . headers . lock ( ) . await . clone ( ) )
96320 . json ( & serde_json:: json!( {
97- "email " : email ,
98- "first_name " : first_name ,
99- "last_name " : last_name ,
321+ "current_password " : current_password ,
322+ "new_password " : new_password ,
323+ "confirm_password " : confirm_password ,
100324 } ) )
101325 . send ( )
102326 . await ?;
@@ -109,20 +333,38 @@ impl AGiXTSDK {
109333 }
110334
111335 let json: serde_json:: Value = serde_json:: from_str ( & text) ?;
336+ Ok ( json)
337+ }
112338
113- if let Some ( otp_uri) = json. get ( "otp_uri" ) . and_then ( |u| u. as_str ( ) ) {
114- let mfa_token = otp_uri
115- . split ( "secret=" )
116- . nth ( 1 )
117- . and_then ( |s| s. split ( '&' ) . next ( ) )
118- . ok_or_else ( || crate :: Error :: Other ( "Invalid OTP URI format" . to_string ( ) ) ) ?;
339+ /// Set a password for users who don't have one (e.g., social login users).
340+ ///
341+ /// # Arguments
342+ /// * `new_password` - New password
343+ /// * `confirm_password` - New password confirmation
344+ ///
345+ /// # Returns
346+ /// Response JSON with success message
347+ pub async fn set_password ( & self , new_password : & str , confirm_password : & str ) -> Result < serde_json:: Value > {
348+ let response = self
349+ . client
350+ . post ( & format ! ( "{}/v1/user/password/set" , self . base_uri) )
351+ . headers ( self . headers . lock ( ) . await . clone ( ) )
352+ . json ( & serde_json:: json!( {
353+ "new_password" : new_password,
354+ "confirm_password" : confirm_password,
355+ } ) )
356+ . send ( )
357+ . await ?;
119358
120- self . login ( email, mfa_token) . await ?;
359+ let status = response. status ( ) ;
360+ let text = response. text ( ) . await ?;
121361
122- Ok ( otp_uri. to_string ( ) )
123- } else {
124- Ok ( text)
362+ if self . verbose {
363+ self . parse_response ( status, & text) . await ?;
125364 }
365+
366+ let json: serde_json:: Value = serde_json:: from_str ( & text) ?;
367+ Ok ( json)
126368 }
127369
128370 /// Check if a user exists.
0 commit comments