Code Monkey home page Code Monkey logo

Comments (3)

GQAdonis avatar GQAdonis commented on July 18, 2024

So I solved the issue with a custom refactored GraphQLMiddleware.cs:

public class GraphQLMiddleware
    {
        private readonly RequestDelegate _next;

        public GraphQLMiddleware(
            RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context, UserManager<User> userManager)
        {
            var result = await context.AuthenticateAsync("Bearer");
            ClaimsPrincipal principal = null;
            if (result.Succeeded)
            {
                principal = result.Principal;
            }
            
            if (!IsGraphQLRequest(context))
            {
                await _next(context);
                return;
            }

            if (principal != null)
            {
                await ExecuteAsync(context, principal, userManager);
            }
            else
            {
                await _next(context);
            }
            
        }

        private bool IsGraphQLRequest(HttpContext context)
        {
            return context.Request.Path.StartsWithSegments("/graphql")
                && string.Equals(context.Request.Method, "POST", StringComparison.OrdinalIgnoreCase);
        }

        private async Task ExecuteAsync(HttpContext context, ClaimsPrincipal principal, UserManager<User> userManager)
        {
            var claims = (from c in principal.Claims select c).ToList();
            string name = claims.SingleOrDefault(c => c.Type == "sub")?.Value;
            
            User user = await userManager.FindByEmailAsync(name);

            if (user != null)
            {
                bool inRole = await userManager.IsInRoleAsync(user, "Admin");
                if (inRole)
                {
                    // add the claim....
                    Claim c = new Claim("role", "Admin");
                    var claimsIndentity = principal.Identity as ClaimsIdentity;
                    if (claimsIndentity != null)
                    {
                        claimsIndentity.AddClaim(c);
                    }
                }
            }

            await _next(context);
        }
    }

This allowed me to add the proper claims to the principal, so they are picked up by GraphQL.Authorization. The key is to add the middleware before adding the GraphQL middleware.

from authorization.

joemcbride avatar joemcbride commented on July 18, 2024

var result = await context.AuthenticateAsync("Bearer");

Instead of that code you can also just issue a Challenge, so it would kick it back to ASP.NET to run their own authentication middleware.

from authorization.

PapaMufflon avatar PapaMufflon commented on July 18, 2024

I had the same problem and I think I solved it a bit better.

I map my custom roles to a "Role" claim per Middleware (this step is optional when you already have a "Role" claim in your token - I had not).

And I provide a custom IUserContextBuilder which sets my GraphQLUserContext so that AuthorizationValidationRule has something to get the "Role" claim from.

public class Startup
{
    public virtual void ConfigureServices(IServiceCollection services)
    {
        ...
        services.AddGraphQLAuth(_ =>
        {
            _.AddPolicy("AdminPolicy", p => p.RequireClaim("Role", "Admin"));
        });
        services.AddScoped<IDependencyResolver>(x => new FuncDependencyResolver(x.GetRequiredService));
        services.AddScoped<MySchema>();
        services
            .AddGraphQL(options => { options.ExposeExceptions = true; })
            .AddGraphTypes(ServiceLifetime.Scoped);
        ...
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
    {
        ...
        app.UseMiddleware<MapRolesForGraphQLMiddleware>();
        app.UseGraphQL<MySchema>();
        ...
    }
}

public static class GraphQLAuthExtensions
{
    public static void AddGraphQLAuth(this IServiceCollection services, Action<AuthorizationSettings> configure)
    {
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        services.AddSingleton<IAuthorizationEvaluator, AuthorizationEvaluator>();
        services.AddTransient<IValidationRule, AuthorizationValidationRule>();

        services.AddTransient<IUserContextBuilder>(s => new UserContextBuilder<GraphQLUserContext>(context =>
        {
            var userContext = new GraphQLUserContext
            {
                User = context.User
            };
                
            return Task.FromResult(userContext);
        }));

        services.AddSingleton(s =>
        {
            var authSettings = new AuthorizationSettings();
            configure(authSettings);
            return authSettings;
        });
    }
}

public class GraphQLUserContext : IProvideClaimsPrincipal
{
    public ClaimsPrincipal User { get; set; }
}

public class MapRolesForGraphQLMiddleware
{
    private readonly RequestDelegate _next;

    public MapRolesForGraphQLMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        var metadata = context.User.Claims.SingleOrDefault(x => x.Type.Equals("metadata"));

        if (metadata != null)
        {
            var roleContainer = JsonConvert.DeserializeObject<RoleContainer>(metadata.Value);
            (context.User.Identity as ClaimsIdentity).AddClaim(new Claim("Role", string.Join(", ", roleContainer.Roles)));
        }
        
        await _next(context);
    }
}

public class RoleContainer
{
    public String[] Roles { get; set; }
}

from authorization.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.