using System.Text.Json; using System.Text.Json.Nodes; using FsToolbox.Cli.Tasks; namespace FsToolbox.Cli.Helper { /// /// Provides helper methods to manage authentication and cached CSRF tokens. /// public static class AuthHelper { #region Public Method EnsureAuthenticationAsync /// /// Ensures the HTTP client carries a valid CSRF token, logging in when needed. /// /// The HTTP client whose headers should be updated. public static async Task EnsureAuthenticationAsync(HttpClient httpClient) { // Check if header already contains a CSRF token if (httpClient.DefaultRequestHeaders.TryGetValues("X-CSRF-Token", out var existingTokens)) { var existingToken = existingTokens.FirstOrDefault(); if (!string.IsNullOrWhiteSpace(existingToken)) return; } // Try to load CSRF token from file var csrfToken = await LoadCsrfTokenAsync(); csrfToken = null; // If no valid token found, call login endpoint to get a new one if (string.IsNullOrWhiteSpace(csrfToken)) csrfToken = await UserTasks.CallLoginEndpointAsync(httpClient); // Set CSRF token in HTTP client headers if (!string.IsNullOrWhiteSpace(csrfToken)) { csrfToken = csrfToken.ReplaceLineEndings(string.Empty); httpClient.DefaultRequestHeaders.Remove("X-CSRF-Token"); httpClient.DefaultRequestHeaders.Add("X-CSRF-Token", csrfToken); } } #endregion #region Public Method StoreCsrfTokenAsync /// /// Persists the CSRF token to the local cache file until it expires. /// /// The token to store; ignored when null or whitespace. /// The UTC expiration timestamp received from the server. public static async Task StoreCsrfTokenAsync(string? csrfToken, DateTime expiration) { if (string.IsNullOrWhiteSpace(csrfToken)) return; var tokenInfo = new { token = csrfToken, expiresAt = expiration }; var json = JsonSerializer.Serialize(tokenInfo, new JsonSerializerOptions { WriteIndented = true }); await File.WriteAllTextAsync("csrf_token.json", json); } #endregion #region Private Method LoadCsrfTokenAsync private static async Task LoadCsrfTokenAsync() { if (!File.Exists("csrf_token.json")) return null; var json = await File.ReadAllTextAsync("csrf_token.json"); var tokenInfo = JsonSerializer.Deserialize(json); if (tokenInfo == null) return null; var token = tokenInfo["token"]?.GetValue(); var expiresAtString = tokenInfo["expiresAt"]?.GetValue(); if (string.IsNullOrWhiteSpace(token) || string.IsNullOrWhiteSpace(expiresAtString)) return null; if (DateTime.TryParse(expiresAtString, out var expiresAt)) if (DateTime.UtcNow < expiresAt.ToUniversalTime()) return token; return null; } #endregion } }