111 lines
3.1 KiB
C#
111 lines
3.1 KiB
C#
using Femto.Common.Domain;
|
|
using Femto.Common.Infrastructure.DbConnection;
|
|
using Femto.Modules.Auth.Application.Dto;
|
|
using Femto.Modules.Auth.Data;
|
|
using Femto.Modules.Auth.Errors;
|
|
using Femto.Modules.Auth.Models;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace Femto.Modules.Auth.Application.Interface.ValidateSession;
|
|
|
|
internal class ValidateSessionCommandHandler(AuthContext context)
|
|
: ICommandHandler<ValidateSessionCommand, ValidateSessionResult>
|
|
{
|
|
public async Task<ValidateSessionResult> Handle(
|
|
ValidateSessionCommand request,
|
|
CancellationToken cancellationToken
|
|
)
|
|
{
|
|
try
|
|
{
|
|
return new ValidateSessionResult(await DoSessionValidation(request, cancellationToken));
|
|
}
|
|
finally
|
|
{
|
|
await context.SaveChangesAsync(cancellationToken);
|
|
}
|
|
}
|
|
|
|
private async Task<SessionDto> DoSessionValidation(
|
|
ValidateSessionCommand request,
|
|
CancellationToken cancellationToken
|
|
)
|
|
{
|
|
var now = DateTimeOffset.UtcNow;
|
|
|
|
var session = await context.Sessions.SingleOrDefaultAsync(
|
|
s => s.Id == request.SessionId,
|
|
cancellationToken
|
|
);
|
|
|
|
var rememberMe = request.RememberMe;
|
|
|
|
if (session is null)
|
|
{
|
|
(session, rememberMe) = await this.TryAuthenticateWithRememberMeToken(
|
|
request.User,
|
|
request.RememberMe,
|
|
cancellationToken
|
|
);
|
|
}
|
|
|
|
if (session.UserId != request.User.Id)
|
|
{
|
|
context.Remove(session);
|
|
throw new InvalidSessionError();
|
|
}
|
|
|
|
if (session.Expires < now)
|
|
{
|
|
context.Remove(session);
|
|
throw new InvalidSessionError();
|
|
}
|
|
|
|
if (session.ShouldRefresh)
|
|
{
|
|
context.Remove(session);
|
|
session = Session.Weak(session.UserId);
|
|
await context.AddAsync(session, cancellationToken);
|
|
}
|
|
|
|
return new SessionDto(session, rememberMe);
|
|
}
|
|
|
|
private async Task<(Session, string)> TryAuthenticateWithRememberMeToken(
|
|
UserInfo user,
|
|
string? rememberMeToken,
|
|
CancellationToken cancellationToken
|
|
)
|
|
{
|
|
if (rememberMeToken is null)
|
|
throw new InvalidSessionError();
|
|
|
|
var parts = rememberMeToken.Split('.');
|
|
if (parts.Length != 2)
|
|
throw new InvalidSessionError();
|
|
|
|
var selector = parts[0];
|
|
var verifier = parts[1];
|
|
|
|
var longTermSession = await context.LongTermSessions.SingleOrDefaultAsync(
|
|
s => s.Selector == selector,
|
|
cancellationToken
|
|
);
|
|
|
|
if (longTermSession is null)
|
|
throw new InvalidSessionError();
|
|
|
|
context.Remove(longTermSession);
|
|
|
|
if (!longTermSession.Validate(verifier))
|
|
throw new InvalidSessionError();
|
|
|
|
var session = Session.Weak(user.Id);
|
|
await context.AddAsync(session, cancellationToken);
|
|
|
|
(longTermSession, rememberMeToken) = LongTermSession.Create(user.Id);
|
|
await context.AddAsync(longTermSession, cancellationToken);
|
|
|
|
return (session, rememberMeToken);
|
|
}
|
|
}
|