refresh user

This commit is contained in:
john 2025-05-19 09:23:20 +02:00
parent 4e24796a5d
commit 0d34774059
12 changed files with 141 additions and 32 deletions

View file

@ -5,31 +5,18 @@ using Microsoft.Extensions.Hosting;
namespace Femto.Modules.Auth.Application;
internal class AuthModule(IHost host) : IAuthModule
internal class AuthModule(IMediator mediator) : IAuthModule
{
public async Task Command(ICommand command, CancellationToken cancellationToken = default)
{
using var scope = host.Services.CreateScope();
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
public async Task Command(ICommand command, CancellationToken cancellationToken = default) =>
await mediator.Send(command, cancellationToken);
}
public async Task<TResponse> Command<TResponse>(
ICommand<TResponse> command,
CancellationToken cancellationToken = default
)
{
using var scope = host.Services.CreateScope();
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
var response = await mediator.Send(command, cancellationToken);
return response;
}
) => await mediator.Send(command, cancellationToken);
public async Task<TResponse> Query<TResponse>(IQuery<TResponse> query, CancellationToken cancellationToken = default)
{
using var scope = host.Services.CreateScope();
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
var response = await mediator.Send(query, cancellationToken);
return response;
}
public async Task<TResponse> Query<TResponse>(
IQuery<TResponse> query,
CancellationToken cancellationToken = default
) => await mediator.Send(query, cancellationToken);
}

View file

@ -21,7 +21,12 @@ public static class AuthStartup
var hostBuilder = Host.CreateDefaultBuilder();
hostBuilder.ConfigureServices(services => ConfigureServices(services, connectionString, eventBus));
var host = hostBuilder.Build();
rootContainer.AddScoped<IAuthModule>(_ => new AuthModule(host));
rootContainer.AddScoped(_ => new ScopeBinding(host.Services.CreateScope()));
rootContainer.AddScoped<IAuthModule>(services =>
services.GetRequiredService<ScopeBinding>().GetService<IAuthModule>());
rootContainer.AddHostedService(services => new AuthApplication(host));
eventBus.Subscribe((evt, cancellationToken) => EventSubscriber(evt, host.Services, cancellationToken));
}
@ -50,11 +55,11 @@ public static class AuthStartup
services.AddMediatR(c => c.RegisterServicesFromAssembly(typeof(AuthStartup).Assembly));
services.ConfigureDomainServices<AuthContext>();
services.AddSingleton(publisher);
services.AddScoped<IAuthModule, AuthModule>();
}
private static async Task EventSubscriber(

View file

@ -0,0 +1,3 @@
namespace Femto.Modules.Auth.Application.Dto;
public record RefreshUserSessionResult(Session Session, UserInfo User);

View file

@ -2,4 +2,9 @@ using Femto.Modules.Auth.Models;
namespace Femto.Modules.Auth.Application.Dto;
public record Session(string SessionId, DateTimeOffset Expires);
public record Session(string SessionId, DateTimeOffset Expires)
{
internal Session(UserSession session) : this(session.Id, session.Expires)
{
}
}

View file

@ -0,0 +1,7 @@
using Femto.Common;
using Femto.Common.Domain;
using Femto.Modules.Auth.Application.Dto;
namespace Femto.Modules.Auth.Application.Interface.RefreshUserSession;
public record RefreshUserSessionCommand(Guid ForUser, CurrentUser CurrentUser) : ICommand<RefreshUserSessionResult>;

View file

@ -0,0 +1,32 @@
using Femto.Common.Domain;
using Femto.Common.Infrastructure.DbConnection;
using Femto.Modules.Auth.Application.Dto;
using Femto.Modules.Auth.Data;
using Microsoft.EntityFrameworkCore;
namespace Femto.Modules.Auth.Application.Interface.RefreshUserSession;
internal class RefreshUserSessionCommandHandler(AuthContext context)
: ICommandHandler<RefreshUserSessionCommand, RefreshUserSessionResult>
{
public async Task<RefreshUserSessionResult> Handle(
RefreshUserSessionCommand request,
CancellationToken cancellationToken
)
{
if (request.CurrentUser.Id != request.ForUser)
throw new DomainError("invalid request");
var user = await context.Users.SingleOrDefaultAsync(
u => u.Id == request.ForUser,
cancellationToken
);
if (user is null)
throw new DomainError("invalid request");
var session = user.PossiblyRefreshSession(request.CurrentUser.SessionId);
return new(new Session(session), new UserInfo(user));
}
}

View file

@ -0,0 +1,16 @@
using Microsoft.Extensions.DependencyInjection;
namespace Femto.Modules.Auth.Application;
/// <summary>
/// We use this to bind a scope to the request scope in the composition root
/// Any scoped services provided by this subcontainer should be accessed via a ScopeBinding injected in the host
/// </summary>
/// <param name="scope"></param>
public class ScopeBinding(IServiceScope scope) : IDisposable
{
public T GetService<T>() where T : notnull => scope.ServiceProvider.GetRequiredService<T>();
public void Dispose() => scope.Dispose();
}