69 lines
2.3 KiB
C#
69 lines
2.3 KiB
C#
using MealPlan.Api.Data;
|
|
using MealPlan.Api.Models;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
namespace MealPlan.Api.Services;
|
|
|
|
/// <summary>Persists refresh tokens in PostgreSQL (survives API restarts).</summary>
|
|
public class DbRefreshTokenStore : IRefreshTokenStore
|
|
{
|
|
private readonly IServiceScopeFactory _scopeFactory;
|
|
|
|
public DbRefreshTokenStore(IServiceScopeFactory scopeFactory) => _scopeFactory = scopeFactory;
|
|
|
|
public void Add(RefreshToken token)
|
|
{
|
|
using var scope = _scopeFactory.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
|
db.RefreshTokens.Add(new RefreshTokenEntity
|
|
{
|
|
Token = token.Token,
|
|
UserId = Guid.Parse(token.UserId),
|
|
ExpiresAtUtc = token.ExpiresAtUtc,
|
|
CreatedAtUtc = token.CreatedAtUtc,
|
|
IsRevoked = false,
|
|
});
|
|
db.SaveChanges();
|
|
}
|
|
|
|
public RefreshToken? Get(string token)
|
|
{
|
|
using var scope = _scopeFactory.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
|
var entity = db.RefreshTokens.AsNoTracking().FirstOrDefault(t => t.Token == token);
|
|
return entity is null ? null : Map(entity);
|
|
}
|
|
|
|
public RefreshToken? Revoke(string token)
|
|
{
|
|
using var scope = _scopeFactory.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
|
var entity = db.RefreshTokens.FirstOrDefault(t => t.Token == token);
|
|
if (entity is null) return null;
|
|
var wasActive = entity.IsActive;
|
|
entity.IsRevoked = true;
|
|
db.SaveChanges();
|
|
return wasActive ? Map(entity) : null;
|
|
}
|
|
|
|
public void RemoveExpired()
|
|
{
|
|
using var scope = _scopeFactory.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
|
var cutoff = DateTime.UtcNow;
|
|
db.RefreshTokens
|
|
.Where(t => t.IsRevoked || t.ExpiresAtUtc <= cutoff)
|
|
.ExecuteDelete();
|
|
}
|
|
|
|
private static RefreshToken Map(RefreshTokenEntity entity) => new()
|
|
{
|
|
Token = entity.Token,
|
|
UserId = entity.UserId.ToString(),
|
|
ExpiresAtUtc = entity.ExpiresAtUtc,
|
|
CreatedAtUtc = entity.CreatedAtUtc,
|
|
IsRevoked = entity.IsRevoked,
|
|
};
|
|
}
|