ironservices-dotnet/ITokenStorage.cs

122 lines
3.1 KiB
C#

namespace IronServices.Client;
/// <summary>
/// Interface for storing authentication tokens.
/// Implement this for platform-specific secure storage (e.g., MAUI SecureStorage, Windows Credential Manager).
/// </summary>
public interface ITokenStorage
{
/// <summary>
/// Save the session token.
/// </summary>
Task SaveTokenAsync(string token, DateTime? expiresAt);
/// <summary>
/// Get the stored session token.
/// </summary>
Task<(string? Token, DateTime? ExpiresAt)> GetTokenAsync();
/// <summary>
/// Clear the stored token.
/// </summary>
Task ClearTokenAsync();
}
/// <summary>
/// In-memory token storage (not persisted across app restarts).
/// </summary>
public class InMemoryTokenStorage : ITokenStorage
{
private string? _token;
private DateTime? _expiresAt;
public Task SaveTokenAsync(string token, DateTime? expiresAt)
{
_token = token;
_expiresAt = expiresAt;
return Task.CompletedTask;
}
public Task<(string? Token, DateTime? ExpiresAt)> GetTokenAsync()
{
return Task.FromResult((_token, _expiresAt));
}
public Task ClearTokenAsync()
{
_token = null;
_expiresAt = null;
return Task.CompletedTask;
}
}
/// <summary>
/// File-based token storage (for console apps, testing).
/// </summary>
public class FileTokenStorage : ITokenStorage
{
private readonly string _filePath;
public FileTokenStorage(string? filePath = null)
{
_filePath = filePath ?? Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"IronServices",
"session.json");
}
public async Task SaveTokenAsync(string token, DateTime? expiresAt)
{
var directory = Path.GetDirectoryName(_filePath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
var data = new { token, expiresAt = expiresAt?.ToString("O") };
var json = System.Text.Json.JsonSerializer.Serialize(data);
await File.WriteAllTextAsync(_filePath, json);
}
public async Task<(string? Token, DateTime? ExpiresAt)> GetTokenAsync()
{
if (!File.Exists(_filePath))
{
return (null, null);
}
try
{
var json = await File.ReadAllTextAsync(_filePath);
var data = System.Text.Json.JsonSerializer.Deserialize<TokenData>(json);
DateTime? expiresAt = null;
if (!string.IsNullOrEmpty(data?.ExpiresAt))
{
expiresAt = DateTime.Parse(data.ExpiresAt, null, System.Globalization.DateTimeStyles.RoundtripKind);
}
return (data?.Token, expiresAt);
}
catch
{
return (null, null);
}
}
public Task ClearTokenAsync()
{
if (File.Exists(_filePath))
{
File.Delete(_filePath);
}
return Task.CompletedTask;
}
private class TokenData
{
public string? Token { get; set; }
public string? ExpiresAt { get; set; }
}
}