Declarative, schema-first authorization for Absinthe GraphQL.
Auth rules live next to the field they protect. They are compiled to introspectable data at schema-compile time, evaluated by middleware at request time, and emit telemetry on every decision.
At a glance
defmodule MyApp.Schema do
use Absinthe.Schema
use AbsinthePermission
loaders do
loader :todo, fn id, _ctx -> MyApp.Todos.get(id) end
end
query do
field :todos, list_of(:todo) do
authorize "view_todos"
resolve &MyApp.Resolvers.list_todos/2
end
end
mutation do
field :update_todo, :todo do
arg :id, :integer
arg :state, :string
authorize "edit_todos"
authorize "close_todos", when: arg(:state) == "CLOSED"
authorize_owner :todo,
by: arg(:id),
if_owner: "edit_own_todo",
if_other: "edit_others_todo"
resolve &MyApp.Resolvers.update_todo/2
end
end
endRequired Absinthe context
At request time the middleware expects two keys in the Absinthe context:
:current_user— any term; available ascurrent_user/current_user(:field)in conditions:permissions— a list of permission strings the caller holds
Set them in your Plug pipeline before Absinthe runs:
conn
|> Absinthe.Plug.put_options(
context: %{
current_user: user,
permissions: MyApp.Auth.permissions_for(user)
}
)If the context is missing these keys, the middleware raises
AbsinthePermission.MissingContextError by default. Override with
the :on_missing_context option to use AbsinthePermission:
use AbsinthePermission, on_missing_context: :deny # or :allowOptions for use AbsinthePermission
:on_missing_context—:raise(default),:deny, or:allow. Behaviour whencurrent_user/permissionsare absent from context.
See also
AbsinthePermission.DSL— the macros (authorize,load,authorize_owner,loader, …)AbsinthePermission.Rule— the rule data structureAbsinthePermission.Decision— the result emitted on each checkmix help absinthe_permission.audit— list every rule in a schema
Summary
Functions
Sets up a schema module to use AbsinthePermission.
Return all rules in a schema, grouped by {type_id, field_id}.
Look up a registered loader function.
Return all loads declared for a field on a schema.
Return the list of rules attached to a field on a schema.
Functions
Sets up a schema module to use AbsinthePermission.
Imports the DSL, registers the necessary module attributes, hooks
into Absinthe's middleware pipeline, and installs a
@before_compile callback that generates the rule lookup
functions.
Options
:on_missing_context—:raise|:deny|:allow(default:raise)
@spec all_rules(module()) :: %{ required({atom(), atom()}) => [AbsinthePermission.Rule.t()] }
Return all rules in a schema, grouped by {type_id, field_id}.
This is what mix absinthe_permission.audit consumes.
Look up a registered loader function.
AbsinthePermission.loader(MyApp.Schema, :todo)
#=> #Function<...>
@spec loads_for(module(), atom(), atom()) :: [AbsinthePermission.Load.t()]
Return all loads declared for a field on a schema.
AbsinthePermission.loads_for(MyApp.Schema, :mutation, :update_todo)
@spec rules_for(module(), atom(), atom()) :: [AbsinthePermission.Rule.t()]
Return the list of rules attached to a field on a schema.
AbsinthePermission.rules_for(MyApp.Schema, :mutation, :update_todo)Returns [] when the field has no rules. Useful in tests, audits,
and for AI agents inspecting the schema.