![]() |
VOOZH | about |
dotnet add package Cloudey.Reflex.Authorization.HotChocolate --version 4.0.5
NuGet\Install-Package Cloudey.Reflex.Authorization.HotChocolate -Version 4.0.5
<PackageReference Include="Cloudey.Reflex.Authorization.HotChocolate" Version="4.0.5" />
<PackageVersion Include="Cloudey.Reflex.Authorization.HotChocolate" Version="4.0.5" />Directory.Packages.props
<PackageReference Include="Cloudey.Reflex.Authorization.HotChocolate" />Project file
paket add Cloudey.Reflex.Authorization.HotChocolate --version 4.0.5
#r "nuget: Cloudey.Reflex.Authorization.HotChocolate, 4.0.5"
#:package Cloudey.Reflex.Authorization.HotChocolate@4.0.5
#addin nuget:?package=Cloudey.Reflex.Authorization.HotChocolate&version=4.0.5Install as a Cake Addin
#tool nuget:?package=Cloudey.Reflex.Authorization.HotChocolate&version=4.0.5Install as a Cake Tool
Utilities for easy-to-use authorization with HotChocolate GraphQL server.
Install with NuGet
Make sure authorization is enabled for your HotChocolate server:
services.AddGraphQLServer()
// ...
.AddAuthorization() // <-- Enable authorization
// ...
RequireParentAssertion enables you to assert that the parent of a field fulfils a condition.
NOTE
When using projection, non-projected fields will not be resolved in the delegate. Therefore, it is important that you mark any other fields that you need in your delegate with the [IsProjected] attribute to make sure they are always loaded, otherwise they will be null in the assertion delegate.
The delegate receives the following arguments:
TAuthorizationHandlerContextIMiddlewareContextExample:
// UserPolicy.cs
// IMPORTANT: Defining policies in this way requires setting up Cloudey.Reflex.Authorization
public class SecretKeyPolicy : IPolicy
{
public static AuthorizationPolicy Policy { get; } = new AuthorizationPolicyBuilder()
.RequireParentAssertion<User>(
// The secret key can only be accessed by the user itself or an admin
(user, context, directiveContext) => user.Id == context.User.GetId() || context.User.IsInRole(Role.Admin)
)
.Build();
}
// User.cs
public class User : Entity {
// ...
[Guard<SecretKeyPolicy>]
public string SecretKey { get; set; }
}
RequireResultAssertion enables you to assert that the resolved entity or field fulfills a condition.
Important
If the policy is applied to a field, the target is the value of the field.
If the policy is applied to a class, the target is the class instance.
If the policy is applied to a resolver, the target is the result of the resolver.
If the result is an IEnumerable, then the assertion is applied to all elements.
The delegate receives the following arguments:
TAuthorizationHandlerContextIMiddlewareContextExample:
// UserPolicy.cs
// IMPORTANT: Defining policies in this way requires setting up Cloudey.Reflex.Authorization
public class AvatarPolicy : IPolicy
{
public static AuthorizationPolicy Policy => new AuthorizationPolicyBuilder()
.RequireTargetAssertion<Avatar>(
// The avatar can only be accessed if it is set as public
(avatar, context, directiveContext) => avatar.IsPublic
)
.Build();
}
// User.cs
public class User : Entity {
// ...
[Guard<AvatarPolicy>] // Can be applied here to only have an effect when accessed through User
public Avatar? Avatar { get; set; }
}
// Avatar.cs
[Guard<AvatarPolicy>] // Can also be applied here to always have an effect whenever Avatar is resolved, incl. through other types
public class Avatar : Entity {
// ...
public bool IsPublic { get; set; }
}
RequireRelatedAssertion enables you to assert that the field or the entity containing the field fulfills a condition. This is useful for defining policies used on both fields, classes, and resolvers interchangeably.
Important
If the policy is applied to a field with type T, the target is the value of the field.
If the policy is applied to a member of T which is not of type T, the target is an instance of the parent T.
If the policy is applied to a class of type T, the target is the instance of the class.
If the policy is applied to a resolver of return type T, the result is the result of the resolver.
If the result is an IEnumerable, then the assertion is applied to all elements.
NOTE
When using projection, non-projected fields will not be resolved in the delegate. Therefore, it is important that you mark any other fields that you need in your delegate with the [IsProjected] attribute to make sure they are always loaded, otherwise they will be null in the resolver if the field is not requested.
The delegate receives the following arguments:
TAuthorizationHandlerContextIMiddlewareContextExample:
// UserPolicy.cs
// IMPORTANT: Defining policies in this way requires setting up Cloudey.Reflex.Authorization
public class AvatarPolicy : IPolicy
{
public static AuthorizationPolicy Policy => new AuthorizationPolicyBuilder()
.RequireRelatedAssertion<Avatar>(
// The avatar can only be accessed if it is set as public
(avatar, context, directiveContext) => avatar.IsPublic
)
.Build();
}
// User.cs
public class User : Entity {
// ...
[Guard<AvatarPolicy>] // Here, the assertion is applied to Avatar
public Avatar? Avatar { get; set; }
}
// Avatar.cs
[Guard<AvatarPolicy>] // Here, the assertion is applied to Avatar
public class Avatar : Entity {
// ...
[Guard<AvatarPolicy>] // Here, the assertion is applied to Avatar (the parent)
public bool IsPublic { get; set; }
[Guard<AvatarPolicy>] // Here, the assertion is applied to the FIELD of type Avatar (the field not the parent!)
public Avatar AlternativeAvatar { get; set; } // Just an example
}
RequireArgumentAssertion enables you to assert that an argument to the given resolver (query or mutation method) fulfills a condition. This is useful for authorising based on input objects.
Important
Policies with argument assertions should be applied with BEFORE_RESOLVER ApplyPolicy to avoid unintended side effects!
By default, the Authorize<T> attribute from this library applies an AFTER_RESOLVER policy, which means that the authorisation only runs after the resolver has returned a result. In the case of mutations, this could lead to the action being taken without authorisation.
Note that this also means the policy cannot contain assertions that work with the resolver result (i.e. RequireParentAssertion, RequireResultAssertion, RequireRelatedAssertion).
Example:
[QueryType]
public class HelloWorldQuery
{
// If not using Reflex authorisation: [Guard("NameOfYourPolicy", ApplyPolicy.BeforeResolver]
[Guard<HelloWorldQueryPolicy>(ApplyPolicy.BeforeResolver)]
public HelloWorldPayload HelloWorld (HelloWorldInput input)
{
return new HelloWorldPayload
{ Message = $"Hello {input.Name}!" };
}
public record HelloWorldInput(string Name);
public record HelloWorldPayload
{
public string Message { get; init; } = string.Empty;
}
// Defining policies in this way requires using the Cloudey.Reflex.Authorization package!
public class HelloWorldQueryPolicy : IPolicy
{
public static AuthorizationPolicy Policy => new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
// Only John is authorised to receive greetings in this application
.RequireArgumentAssertion<HelloWorldInput>(
(input, context, middlewareContext) => input?.Name == "John"
)
.Build();
}
To authenticate a given request, entity, or property, use the [Guard<T>] attribute. The Guard attribute can be used to authenticate based on roles or a policy, and replaces the Authorize attribute from HotChocolate.
Apply the [Guard] attribute to the query or mutation method, eg:
[QueryType]
public class MyQuery {
...
[Guard("Admin")] // Only users with the "Admin" role can access this query
public async string GetHello () {
return "Hello";
}
}
Apply the [Guard] attribute to the entity class, eg:
[Guard("Admin")]
public class SecretInformation : Entity {
...
}
This prevents anyone without the Admin role from accessing the entity in any query.
You can also restrict access on a field-level. Apply the [Guard] attribute to the field, eg:
public class User : Entity {
public Guid Id {get; set;}
[Guard("Admin")]
public string PasswordHash {get; set;}
}
This disallows access to the PasswordHash fields for everyone except Admins.
You can apply authorization attributes on multiple levels, and they will all be executed in order. E.g. you can allow access to the Role entity to all authenticated users with a [Guard] attribute on the Role class, but only allow access for Admins to a specific field in that entity by adding [Guard(new[] { "Admin" })] to that field.
When simple role-based authentication is not enough, you can also use policies to create more complex authorization logic. For an easy way to implement policy-based authorization, see Cloudey.Reflex.Authorization.
Licensed under Apache 2.0.
Copyright © 2024 Cloudey IT Ltd
Cloudey® is a registered trademark of Cloudey IT Ltd. Use of the trademark is NOT GRANTED under the license of this repository or software package.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 net9.0 is compatible. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.0.5 | 265 | 1/5/2025 |
| 4.0.4 | 233 | 1/5/2025 |
| 4.0.3 | 238 | 1/5/2025 |
| 4.0.2 | 242 | 1/5/2025 |
| 4.0.1 | 239 | 1/5/2025 |
| 4.0.0 | 260 | 1/1/2025 |
| 3.1.0 | 264 | 6/15/2024 |
| 3.0.2 | 242 | 6/15/2024 |
| 3.0.1 | 243 | 6/15/2024 |
| 3.0.0 | 235 | 6/15/2024 |
| 2.0.0 | 257 | 4/19/2024 |
| 1.1.2 | 372 | 7/29/2023 |
| 1.1.1 | 279 | 7/29/2023 |
| 1.1.0 | 426 | 4/24/2023 |
| 1.0.3 | 371 | 4/21/2023 |
| 1.0.2 | 345 | 4/21/2023 |
| 1.0.1 | 383 | 4/21/2023 |
| 1.0.0 | 366 | 4/21/2023 |