Custom Authorization Policies
The @policy directive lets you enforce authorization rules that the router cannot evaluate on its
own, such as resource ownership, tenant isolation, or any business rule that lives in your own
systems. Instead of hard-coding that logic in every subgraph, you declare a policy name in your
schema and let a coprocessor decide
whether the current request satisfies it.
@policy complements @authenticated and @requiresScopes,
which are described in the general Authorization guide. Use
@policy when the decision cannot be derived from a JWT scopes alone.
For the complete configuration reference, see
authorization configuration.
How It Works
- Collect - before calling the
graphql.analysiscoprocessor stage, the router walks the requested operation and publishes every@policypolicy it depends on to thehive::authorization::required_policiesrequest context key, mapped tonull. - Decide - your coprocessor (or a plugin) looks at
the request and overwrites each entry with
trueorfalse. - Enforce - after the stage returns, the router applies the decisions. Any policy left
null, or missing from the answer entirely, is treated as denied. Unauthorized fields are then handled exactly like@authenticated/@requiresScopesviolations, following your configuredauthorization.directives.unauthorized.mode.
Defining Policies in Your Schema
Just like @requiresScopes, policies is a list of lists: an OR of AND groups.
- Single list (AND logic): every policy in the list must be granted.
- Multiple lists (OR logic): at least one full list must be granted.
In the example above, users is allowed if the admin policy is granted, or if both
read_users and internal are granted.
When multiple directives protect the same field, all of them must be satisfied - @policy is
combined with @authenticated/@requiresScopes using AND, the same way multiple auth directives
on one field already compose in the Authorization guide.
Wiring Up a Coprocessor
Enable the graphql.analysis stage and include the request context, so your coprocessor can read
hive::authorization::required_policies and reply with its decisions:
Coprocessor Input
For an operation that selects Query.users, the graphql.analysis stage payload includes the
policies the router needs a decision on, each initialized to null:
Coprocessor Output
Your coprocessor looks up the current user (from headers, a session store, an internal service - whatever your business logic requires) and answers by overwriting the relevant entries:
Given this answer, the read_users AND internal group is fully satisfied, so users is
authorized even though admin was denied.
Deciding Policies from a Plugin
A plugin can decide @policy grants too, using the
exact same mechanism as a coprocessor: its on_graphql_analysis hook runs before enforcement, reads
required_policies off the request context, and writes back a true/false decision for each:
Reach for a plugin instead of a coprocessor when the router binary itself is already the right place to own the decision - no network hop, no separate service to run. Reach for a coprocessor when the logic is shared across services or owned by a different team.
See the custom_policy
plugin example, and the on_graphql_analysis hook reference
for the full API.
Handling Denied Policies
A denied policy is handled exactly like any other unauthorized field access - the field is filtered
out (or the whole request is rejected in reject mode) and an error is returned:
See Handling Authorization Errors
for the full behavior of filter and reject modes.