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#
dotnet add package HotChocolate.AspNetCore.AuthorizationHotChocolate.* 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):
builder.Services.AddAuthorization();
builder
.AddGraphQL()
.AddAuthorization()
.AddQueryType<Query>();3. Add Authorization Middleware#
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.
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.
{
"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:
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:
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:
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.
Use HotChocolate.Authorization.AllowAnonymousAttribute, not Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute.
[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():
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:
app.UseEndpoints(endpoints =>
{
endpoints.MapGraphQLHttp().RequireAuthorization();
endpoints.MapNitroApp();
});Learn more about available middleware
Next Steps#
- Need to set up authentication first? See Authentication.
- Need to protect against expensive queries? See Cost Analysis.
- Need an overview of security options? See Security Overview.