using System.Security.Claims; using System.Text.Encodings.Web; using HopFrame.Database.Repositories; using HopFrame.Security.Claims; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously #pragma warning disable CS0618 // Type or member is obsolete namespace HopFrame.Security.Authentication; public class HopFrameAuthentication( IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, ITokenRepository tokens, IPermissionRepository perms, IOptions tokenOptions) : AuthenticationHandler(options, logger, encoder, clock) { public const string SchemeName = "HopFrame.Authentication"; protected override async Task HandleAuthenticateAsync() { var accessToken = Request.Cookies[ITokenContext.AccessTokenType]; if (string.IsNullOrEmpty(accessToken)) accessToken = Request.Headers[SchemeName]; if (string.IsNullOrEmpty(accessToken)) accessToken = Request.Headers["Token"]; if (string.IsNullOrEmpty(accessToken)) accessToken = Request.Query["token"]; if (string.IsNullOrEmpty(accessToken)) return AuthenticateResult.Fail("No Access Token provided"); var tokenEntry = await tokens.GetToken(accessToken); if (tokenEntry is null) return AuthenticateResult.Fail("The provided Access Token does not exist"); if (tokenEntry.CreatedAt + tokenOptions.Value.AccessTokenTime < DateTime.Now) return AuthenticateResult.Fail("The provided Access Token is expired"); if (tokenEntry.Owner is null) return AuthenticateResult.Fail("The provided Access Token does not match any user"); var claims = new List { new(HopFrameClaimTypes.AccessTokenId, accessToken), new(HopFrameClaimTypes.UserId, tokenEntry.Owner.Id.ToString()) }; var permissions = await perms.GetFullPermissions(tokenEntry.Owner); claims.AddRange(permissions.Select(perm => new Claim(HopFrameClaimTypes.Permission, perm))); var principal = new ClaimsPrincipal(); principal.AddIdentity(new ClaimsIdentity(claims, SchemeName)); return AuthenticateResult.Success(new AuthenticationTicket(principal, Scheme.Name)); } }