109 lines
3.1 KiB
C#
109 lines
3.1 KiB
C#
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using DysonNetwork.Common.Models;
|
|
using DysonNetwork.Pass.Data;
|
|
using DysonNetwork.Pass.Features.Auth.Interfaces;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using NodaTime;
|
|
|
|
namespace DysonNetwork.Pass.Features.Auth.Services;
|
|
|
|
public class SessionService : ISessionService
|
|
{
|
|
private readonly PassDatabase _db;
|
|
private readonly IClock _clock;
|
|
|
|
public SessionService(PassDatabase db, IClock clock)
|
|
{
|
|
_db = db;
|
|
_clock = clock;
|
|
}
|
|
|
|
public async Task<AuthSession> CreateSessionAsync(Guid accountId, string ipAddress, string userAgent)
|
|
{
|
|
var now = _clock.GetCurrentInstant();
|
|
var session = new AuthSession
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
AccountId = accountId,
|
|
Label = $"Session from {ipAddress} via {userAgent}",
|
|
LastGrantedAt = now,
|
|
ExpiredAt = now.Plus(Duration.FromDays(30))
|
|
};
|
|
|
|
await _db.AuthSessions.AddAsync(session);
|
|
await _db.SaveChangesAsync();
|
|
|
|
return session;
|
|
}
|
|
|
|
public async Task<AuthSession?> GetSessionAsync(Guid sessionId)
|
|
{
|
|
return await _db.AuthSessions
|
|
.Include(s => s.Account)
|
|
.FirstOrDefaultAsync(s => s.Id == sessionId && s.ExpiredAt > _clock.GetCurrentInstant());
|
|
}
|
|
|
|
public async Task<bool> ValidateSessionAsync(Guid sessionId)
|
|
{
|
|
var session = await GetSessionAsync(sessionId);
|
|
if (session == null)
|
|
return false;
|
|
|
|
var now = _clock.GetCurrentInstant();
|
|
if (session.ExpiredAt <= now)
|
|
return false;
|
|
|
|
session.LastGrantedAt = now;
|
|
await _db.SaveChangesAsync();
|
|
|
|
return true;
|
|
}
|
|
|
|
public async Task InvalidateSessionAsync(Guid sessionId)
|
|
{
|
|
var session = await GetSessionAsync(sessionId);
|
|
if (session != null)
|
|
{
|
|
session.ExpiredAt = _clock.GetCurrentInstant();
|
|
await _db.SaveChangesAsync();
|
|
}
|
|
}
|
|
|
|
public async Task InvalidateAllSessionsAsync(Guid accountId, Guid? excludeSessionId = null)
|
|
{
|
|
var now = _clock.GetCurrentInstant();
|
|
var sessions = await _db.AuthSessions
|
|
.Where(s => s.AccountId == accountId && s.ExpiredAt > now)
|
|
.ToListAsync();
|
|
|
|
foreach (var session in sessions)
|
|
{
|
|
if (excludeSessionId == null || session.Id != excludeSessionId.Value)
|
|
{
|
|
session.ExpiredAt = now;
|
|
}
|
|
}
|
|
|
|
await _db.SaveChangesAsync();
|
|
}
|
|
|
|
public async Task UpdateSessionActivityAsync(Guid sessionId)
|
|
{
|
|
var session = await GetSessionAsync(sessionId);
|
|
if (session != null)
|
|
{
|
|
session.LastGrantedAt = _clock.GetCurrentInstant();
|
|
await _db.SaveChangesAsync();
|
|
}
|
|
}
|
|
|
|
private static string GenerateRefreshToken()
|
|
{
|
|
var randomNumber = new byte[32];
|
|
using var rng = RandomNumberGenerator.Create();
|
|
rng.GetBytes(randomNumber);
|
|
return Convert.ToBase64String(randomNumber);
|
|
}
|
|
}
|