using System.Security.Claims; using System.Text.Encodings.Web; using Femto.Api.Sessions; using Femto.Common; using Femto.Modules.Auth.Application; using Femto.Modules.Auth.Application.Services; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Options; namespace Femto.Api.Auth; internal class SessionAuthenticationHandler( IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, IAuthService authService, CurrentUserContext currentUserContext ) : AuthenticationHandler(options, logger, encoder) { protected override async Task HandleAuthenticateAsync() { Logger.LogDebug("{TraceId} Authenticating session", this.Context.TraceIdentifier); var sessionId = this.Context.GetSessionId(); if (sessionId is null) { Logger.LogDebug("{TraceId} SessionId was null ", this.Context.TraceIdentifier); return AuthenticateResult.NoResult(); } var session = await authService.GetSession(sessionId); if (session is null) { Logger.LogDebug("{TraceId} Loaded session was null ", this.Context.TraceIdentifier); return await FailAndDeleteSession(sessionId); } if (session.IsExpired) { Logger.LogDebug("{TraceId} Loaded session was expired ", this.Context.TraceIdentifier); return await FailAndDeleteSession(sessionId); } var user = await authService.GetUserWithId(session.UserId); if (user is null) { await authService.DeleteSession(sessionId); this.Context.DeleteSession(); return AuthenticateResult.Fail("invalid session"); } if (session.ExpiresSoon) { session = await authService.CreateWeakSession(session.UserId); this.Context.SetSession(session, user); } var claims = new List { new(ClaimTypes.Name, user.Username), new("sub", user.Id.ToString()), new("user_id", user.Id.ToString()), }; claims.AddRange(user.Roles.Select(role => new Claim(ClaimTypes.Role, role.ToString()))); var identity = new ClaimsIdentity(claims, this.Scheme.Name); var principal = new ClaimsPrincipal(identity); currentUserContext.CurrentUser = new CurrentUser(user.Id, user.Username); return AuthenticateResult.Success(new AuthenticationTicket(principal, this.Scheme.Name)); } private async Task FailAndDeleteSession(string sessionId) { await authService.DeleteSession(sessionId); this.Context.DeleteSession(); return AuthenticateResult.Fail("invalid session"); } }