📄 src/Api/YouTubeAuth/YouTubeScopeAuthorizationHandler.cs
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;

namespace Slopper.Api.YouTubeAuth;

internal sealed class YouTubeScopeAuthorizationHandler(IHttpContextAccessor httpContextAccessor)
    : AuthorizationHandler<YouTubeScopeRequirement>
{
    protected override async Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        YouTubeScopeRequirement requirement
    )
    {
        var httpContext = httpContextAccessor.HttpContext!;

        var auth = await httpContext.AuthenticateAsync();
        if (!auth.Succeeded || auth.None)
        {
            httpContext.Items["ScopeIncremental"] = string.Join(" ", requirement.Scopes);
            return;
        }

        var existingScopes =
            auth.Properties?.Items.TryGetValue("scope", out var existingScope) is true && existingScope is not null
                ? existingScope.Split(" ", StringSplitOptions.RemoveEmptyEntries)
                : [];
        var additionalScopes = requirement.Scopes.Except(existingScopes).ToArray();
        if (additionalScopes is not [])
        {
            httpContext.Items["ScopeIncremental"] = string.Join(" ", additionalScopes);
            return;
        }

        context.Succeed(requirement);
    }
}