63 lines
2.0 KiB
C#
63 lines
2.0 KiB
C#
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<Option<SequenceData>> GetSequenceAsync(string sequenceId)
|
|
{
|
|
var value = await _db.StringGetAsync(sequenceId);
|
|
|
|
return ((string?)value) switch
|
|
{
|
|
null => Option<SequenceData>.None(),
|
|
string str => ParseData(str)
|
|
};
|
|
}
|
|
|
|
private static Option<SequenceData> ParseData(string str)
|
|
{
|
|
var data = JsonSerializer.Deserialize<SequenceData>(str);
|
|
return data switch
|
|
{
|
|
null => Option<SequenceData>.None(),
|
|
_ => Option<SequenceData>.Some(data)
|
|
};
|
|
}
|
|
|
|
public async Task<StoreOutcome> 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<StoreOutcome> 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<StoreOutcome> 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;
|
|
}
|
|
}
|