Files
Swarm/DysonNetwork.Pass/Features/Auth/Services/AccountConnectionService.cs

159 lines
5.6 KiB
C#

using System;
using System.Threading.Tasks;
using DysonNetwork.Common.Models;
using DysonNetwork.Pass.Data;
using DysonNetwork.Pass.Features.Auth.Models;
using Microsoft.EntityFrameworkCore;
using NodaTime;
// Use fully qualified names to avoid ambiguity
using CommonAccount = DysonNetwork.Common.Models.Account;
using CommonAccountConnection = DysonNetwork.Common.Models.AccountConnection;
using CommonOidcUserInfo = DysonNetwork.Common.Models.OidcUserInfo;
namespace DysonNetwork.Pass.Features.Auth.Services;
public class AccountConnectionService : IAccountConnectionService
{
private readonly PassDatabase _db;
private readonly IClock _clock;
private readonly ISessionService _sessionService;
public AccountConnectionService(PassDatabase db, IClock clock, ISessionService sessionService)
{
_db = db;
_clock = clock;
_sessionService = sessionService;
}
public async Task<CommonAccountConnection> FindOrCreateConnection(CommonOidcUserInfo userInfo, string provider)
{
if (string.IsNullOrEmpty(userInfo.UserId))
throw new ArgumentException("User ID is required", nameof(userInfo));
// Try to find existing connection
var connection = await _db.AccountConnections
.FirstOrDefaultAsync(c => c.Provider == provider &&
c.ProvidedIdentifier == userInfo.UserId);
if (connection == null)
{
// Create new connection
connection = new CommonAccountConnection
{
Id = Guid.NewGuid().ToString("N"),
Provider = provider,
ProvidedIdentifier = userInfo.UserId,
DisplayName = userInfo.Name,
CreatedAt = _clock.GetCurrentInstant(),
LastUsedAt = _clock.GetCurrentInstant(),
Meta = userInfo.ToMetadata()
};
await _db.AccountConnections.AddAsync(connection);
}
// Update connection with latest info
await UpdateConnection(connection, userInfo);
await _db.SaveChangesAsync();
return connection;
}
public async Task UpdateConnection(CommonAccountConnection connection, CommonOidcUserInfo userInfo)
{
connection.LastUsedAt = _clock.GetCurrentInstant();
connection.AccessToken = userInfo.AccessToken;
connection.RefreshToken = userInfo.RefreshToken;
connection.ExpiresAt = userInfo.ExpiresAt != null ? Instant.FromDateTimeOffset(userInfo.ExpiresAt.Value) : null;
// Update metadata
var metadata = userInfo.ToMetadata();
if (metadata != null)
{
connection.Meta = metadata;
}
_db.AccountConnections.Update(connection);
await _db.SaveChangesAsync();
}
public async Task<CommonAccountConnection?> FindConnection(string provider, string userId)
{
if (string.IsNullOrEmpty(provider) || string.IsNullOrEmpty(userId))
return null;
return await _db.AccountConnections
.AsNoTracking()
.FirstOrDefaultAsync(c => c.Provider == provider &&
c.ProvidedIdentifier == userId);
}
public async Task<Models.AuthSession> CreateSessionAsync(CommonAccount account, string? deviceId = null)
{
if (account == null)
throw new ArgumentNullException(nameof(account));
var now = _clock.GetCurrentInstant();
var session = new Models.AuthSession
{
Id = Guid.NewGuid(),
AccountId = Guid.Parse(account.Id),
Label = $"OIDC Session {DateTime.UtcNow:yyyy-MM-dd}",
LastGrantedAt = now,
ExpiredAt = now.Plus(Duration.FromDays(30)), // 30-day session
// Challenge will be set later if needed
};
await _db.AuthSessions.AddAsync(session);
await _db.SaveChangesAsync();
return session;
}
public async Task<CommonAccountConnection> AddConnectionAsync(CommonAccount account, CommonOidcUserInfo userInfo, string provider)
{
if (account == null)
throw new ArgumentNullException(nameof(account));
if (string.IsNullOrEmpty(userInfo.UserId))
throw new ArgumentException("User ID is required", nameof(userInfo));
// Check if connection already exists
var existingConnection = await FindConnection(provider, userInfo.UserId);
if (existingConnection != null)
{
// Update existing connection
await UpdateConnection(existingConnection, userInfo);
return existingConnection;
}
// Create new connection
var connection = new CommonAccountConnection
{
Id = Guid.NewGuid().ToString("N"),
AccountId = account.Id,
Provider = provider,
ProvidedIdentifier = userInfo.UserId,
DisplayName = userInfo.Name,
CreatedAt = _clock.GetCurrentInstant(),
LastUsedAt = _clock.GetCurrentInstant(),
Meta = userInfo.ToMetadata()
};
// Set token info if available
if (userInfo.AccessToken != null)
{
connection.AccessToken = userInfo.AccessToken;
connection.RefreshToken = userInfo.RefreshToken;
connection.ExpiresAt = userInfo.ExpiresAt != null
? Instant.FromDateTimeOffset(userInfo.ExpiresAt.Value)
: null;
}
await _db.AccountConnections.AddAsync(connection);
await _db.SaveChangesAsync();
return connection;
}
}