159 lines
5.6 KiB
C#
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;
|
|
}
|
|
}
|