Scope auth
Authorization is declared on fields, objects and interfaces. It is checked before the resolver runs and composes with every applicable rule.
Field rules
auth takes a Fn(&Ctx) -> bool. For I/O, use auth_async:
auth_error changes the message returned when the rule denies. If omitted, the client sees
"Not authorized".
Denials are GraphQL field errors with extensions.code = "UNAUTHORIZED", so a server-side
mask_internal keeps the message visible while still hiding unexpected internals.
Object and interface rules
A type-level rule applies to every field on the object, including fields added by later
object_fields calls:
A field must pass:
- its own field rule,
- its object's rule(s), and
- every implemented interface's rule(s).
Reopening a type with another auth composes the rules: all must allow. A later auth_error
refines the message of the previous rule instead of replacing the check.
Interface field rules are inherited by implementing objects through the same inheritance path as types and arguments. A mock plugin that replaces an object field's resolver bypasses field auth, but object and interface rules run outside the plugin chain and are never bypassed.
Scopes
Model the request's grants in the context:
For a policy that applies to many fields, implement a plugin that checks the path or metadata and wraps the resolver. See Writing plugins.
Authentication versus authorization
Authentication happens in the server's context extractor. suku can reject a request before
GraphQL execution:
Authorization stays in the schema. This separation keeps the GraphQL rules testable without HTTP and lets a different transport reuse them.
Public fields
A public field simply has no rule, or a rule that allows anonymous requests:
That field is nullable when it returns Option<User>. A non-null field with an optionally
anonymous rule must either always return a value or use .nullable().
Errors and masking
A denial is deliberate client-facing output. Do not attach internal details to auth_error.
suku::mask_internal preserves UNAUTHORIZED; format_error can rewrite messages but should
keep the code and path.
Testing
Build the schema once and execute with different contexts:
Assert both the message and extensions.code. See Troubleshooting.
