Authorization

Authorization controls what an authenticated user can access. Hot Chocolate provides the @authorize directive for field-level and type-level access control, integrating with ASP.NET Core roles and policies.

Authentication is a prerequisite. You must first validate a user's identity before evaluating their permissions.

Learn how to set up authentication

Setup#

After configuring authentication, complete these steps to enable authorization.

1. Install the Authorization Package#

Bash
dotnet add package HotChocolate.AspNetCore.Authorization
Warning
All HotChocolate.* packages need to have the same version.

2. Register the Required Services#

Call AddAuthorization() on both IServiceCollection (for ASP.NET Core services) and IRequestExecutorBuilder (for the @authorize directive and middleware):

C#
builder.Services.AddAuthorization();

builder
    .AddGraphQL()
    .AddAuthorization()
    .AddQueryType<Query>();

3. Add Authorization Middleware#

C#
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapGraphQL();
});

Applying Authorization#

The @authorize directive can be applied to types and fields. When applied to a type, it applies to every field on that type. A directive on a specific field overrides the one on the type.

Warning

Use HotChocolate.Authorization.AuthorizeAttribute, not Microsoft.AspNetCore.Authorization.AuthorizeAttribute. The Microsoft attribute does not integrate with the Hot Chocolate authorization pipeline. Using the wrong attribute is a common source of authorization not working.

If no arguments are specified on [Authorize], the directive requires the user to be authenticated. Unauthenticated users who access an authorized field receive a GraphQL error with the code AUTH_NOT_AUTHENTICATED, and the field value is set to null.

JSON
{
  "errors": [
    {
      "message": "The current user is not authorized to access this resource.",
      "path": ["me"],
      "extensions": {
        "code": "AUTH_NOT_AUTHENTICATED"
      }
    }
  ],
  "data": {
    "me": null
  }
}

Roles#

Roles provide a straightforward way to group users by access level. Add role claims to the ClaimsPrincipal:

C#
claims.Add(new Claim(ClaimTypes.Role, "Administrator"));

Then restrict access by role:

When multiple roles are specified, a user needs to match only one of them to gain access.

Learn more about role-based authorization in ASP.NET Core

Policies#

Policies decouple authorization logic from your GraphQL resolvers. A policy consists of an IAuthorizationRequirement and an AuthorizationHandler<T>.

Register policies on the service collection:

C#
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AtLeast21", policy =>
        policy.Requirements.Add(new MinimumAgeRequirement(21)));

    options.AddPolicy("HasCountry", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim(c => c.Type == ClaimTypes.Country)));
});

builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();

Apply policies to fields:

The @authorize directive is repeatable. When multiple policies are specified, the user must satisfy all of them:

Learn more about policy-based authorization in ASP.NET Core

Accessing IResolverContext in an AuthorizationHandler#

When you need access to GraphQL-specific data in your authorization handler, use IResolverContext as the resource type:

C#
public class MinimumAgeHandler
    : AuthorizationHandler<MinimumAgeRequirement, IResolverContext>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        MinimumAgeRequirement requirement,
        IResolverContext resolverContext)
    {
        // Access GraphQL context data, arguments, etc.
        // Omitted for brevity
    }
}

Allow Anonymous Access#

Use [AllowAnonymous] to bypass authorization on specific fields. This is useful for registration or public content endpoints.

Warning

Use HotChocolate.Authorization.AllowAnonymousAttribute, not Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute.

C#
[MutationType]
public static partial class AccountMutations
{
    [Authorize]
    public static async Task<User> AddAddressAsync(/* ... */)
    {
        // Requires authentication
    }

    [AllowAnonymous]
    public static async Task<User> RegisterAsync(/* ... */)
    {
        // Open to everyone
    }
}

[AllowAnonymous] removes all other authorization requirements on the field. Use it carefully to avoid exposing sensitive data.

Global Authorization#

Apply authorization to the entire GraphQL endpoint by calling RequireAuthorization():

C#
app.UseEndpoints(endpoints =>
{
    endpoints.MapGraphQL().RequireAuthorization();
});

This returns HTTP 401 for unauthorized requests and blocks access to all middleware including Nitro. To keep Nitro accessible while protecting the GraphQL endpoint, split the middleware:

C#
app.UseEndpoints(endpoints =>
{
    endpoints.MapGraphQLHttp().RequireAuthorization();
    endpoints.MapNitroApp();
});

Learn more about available middleware

Next Steps#

Edit this page on GitHub
Last updated on by Tobias Tengler