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 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 GetSessionAsync(Guid sessionId) { return await _db.AuthSessions .Include(s => s.Account) .FirstOrDefaultAsync(s => s.Id == sessionId && s.ExpiredAt > _clock.GetCurrentInstant()); } public async Task 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); } }