using StackExchange.Redis; using System.Text.Json; using SequenceAuth.Lib; namespace SequenceAuth.Example; public class RedisSequenceStore(IConnectionMultiplexer redis) : ISequenceStore { private readonly IDatabase _db = redis.GetDatabase(); public async Task> GetSequenceAsync(string sequenceId) { var value = await _db.StringGetAsync(sequenceId); return ((string?)value) switch { null => Option.None(), string str => ParseData(str) }; } private static Option ParseData(string str) { var data = JsonSerializer.Deserialize(str); return data switch { null => Option.None(), _ => Option.Some(data) }; } public async Task SaveSequenceAsync(string sequenceId, SequenceData data) { var json = JsonSerializer.Serialize(data); _ = await _db.StringSetAsync(sequenceId, json); _ = await _db.SetAddAsync($"UserSessions:{data.UserId}", sequenceId); return StoreOutcome.Success; } public async Task InvalidateUserSessionsAsync(string userId) { var sessionKeys = await _db.SetMembersAsync($"UserSessions:{userId}"); foreach(var key in sessionKeys) { var seqOpt = await GetSequenceAsync(key.ToString()); _ = seqOpt.State switch { OptionState.Some => await UpdateToCompromisedAsync(key.ToString(), seqOpt.Value!), _ => StoreOutcome.Failure }; } return StoreOutcome.Success; } private async Task UpdateToCompromisedAsync(string sequenceId, SequenceData data) { var compData = data with { State = SequenceState.Compromised }; var json = JsonSerializer.Serialize(compData); _ = await _db.StringSetAsync(sequenceId, json); return StoreOutcome.Success; } }