using System; using System.Linq; using System.Net.Http.Json; using FsToolbox.Cli.Helper; using NLog; namespace FsToolbox.Cli.Tasks { public static class UserTasks { private static readonly Logger Logger = LoggingService.GetLogger(nameof(UserTasks)); /// /// Performs a login request using configured credentials and optional two-factor authentication. /// /// The HTTP client used to send the request. /// The CSRF token when login succeeds; otherwise, null. public static async Task CallLoginEndpointAsync(HttpClient httpClient) { var credentials = SettingsProvider.Current.Credentials; if (string.IsNullOrWhiteSpace(credentials.Email) || string.IsNullOrWhiteSpace(credentials.Password)) { Logger.Warn("Email and password must be configured in appsettings.json."); return null; } string? authCode = null; if (credentials.TwoFactorEnabled) { Logger.Info("Enter 2FA code: "); authCode = Console.ReadLine()?.Trim(); if (string.IsNullOrWhiteSpace(authCode)) { Logger.Warn("A valid 2FA code is required when two-factor authentication is enabled."); return null; } } string? csrfToken = null; object payload = credentials.TwoFactorEnabled ? new { email = credentials.Email, password = credentials.Password, code = authCode, remember_me = true } : new { email = credentials.Email, password = credentials.Password, remember_me = true }; var response = await httpClient.PostAsJsonAsync(Endpoints.UserLogin, payload); // handle unsuccessful response if (!response.IsSuccessStatusCode) { var responseBody = await response.Content.ReadAsStringAsync(); Logger.Error("Login failed ({Status} {Reason}): {Body}", (int)response.StatusCode, response.ReasonPhrase, responseBody); return null; } // Read headers as dictionary var headers = response.Headers.ToDictionary(h => h.Key, h => string.Join(", ", h.Value)); if (headers.TryGetValue("Set-Cookie", out var setCookieHeader)) { // Split cookies by comma and semicolon var cookies = setCookieHeader.Split([";"], StringSplitOptions.RemoveEmptyEntries); var csrfTokenEntry = cookies.FirstOrDefault(c => c.Trim().StartsWith("FS_CSRF_TOKEN=")); csrfToken = csrfTokenEntry?.Split('=')[1]; if (string.IsNullOrWhiteSpace(csrfToken)) return null; var expiryEntry = cookies.FirstOrDefault(c => c.Trim().StartsWith("expires=")); var expireString = expiryEntry?.Split('=')[1]; if (DateTime.TryParse(expireString, out var expiration)) await AuthHelper.StoreCsrfTokenAsync(csrfToken, expiration); } return csrfToken; } /// /// Retrieves information about the current authenticated user. /// /// The HTTP client used to send the request. /// A task that represents the asynchronous operation. public static async Task GetCurrentUserAsync(HttpClient httpClient) { await AuthHelper.EnsureAuthenticationAsync(httpClient); var response = await httpClient.GetAsync(Endpoints.UserCurrent); // handle unsuccessful response if (!response.IsSuccessStatusCode) { var responseBody = await response.Content.ReadAsStringAsync(); Logger.Error("Get current user failed ({Status} {Reason}): {Body}", (int)response.StatusCode, response.ReasonPhrase, responseBody); return; } var content = await response.Content.ReadAsStringAsync(); Logger.Info("Current User Info:"); Logger.Info(content); } } }