44 lines
1.3 KiB
C#
44 lines
1.3 KiB
C#
using System.Collections.Concurrent;
|
|
using MealPlan.Api.Models;
|
|
|
|
namespace MealPlan.Api.Services;
|
|
|
|
/// <summary>
|
|
/// Thread-safe in-memory refresh token store registered as a singleton.
|
|
/// Note: tokens live only for the lifetime of the process. For multi-instance
|
|
/// deployments this should be swapped for a shared store (e.g. Redis), but it
|
|
/// satisfies the "do not modify the database" constraint of this project.
|
|
/// </summary>
|
|
public class InMemoryRefreshTokenStore : IRefreshTokenStore
|
|
{
|
|
private readonly ConcurrentDictionary<string, RefreshToken> _tokens = new();
|
|
|
|
public void Add(RefreshToken token) => _tokens[token.Token] = token;
|
|
|
|
public RefreshToken? Get(string token) =>
|
|
_tokens.TryGetValue(token, out var found) ? found : null;
|
|
|
|
public RefreshToken? Revoke(string token)
|
|
{
|
|
if (!_tokens.TryGetValue(token, out var found))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var wasActive = found.IsActive;
|
|
found.IsRevoked = true;
|
|
return wasActive ? found : null;
|
|
}
|
|
|
|
public void RemoveExpired()
|
|
{
|
|
foreach (var kvp in _tokens)
|
|
{
|
|
if (kvp.Value.IsRevoked || DateTime.UtcNow >= kvp.Value.ExpiresAtUtc)
|
|
{
|
|
_tokens.TryRemove(kvp.Key, out _);
|
|
}
|
|
}
|
|
}
|
|
}
|