PermitEx includes optional Absinthe middleware compiled only when :absinthe
is present in the project.
Setup
Add :absinthe to your dependencies and configure PermitEx normally:
def deps do
[
{:permit_ex, "~> 0.1"},
{:absinthe, "~> 1.7"},
{:absinthe_plug, "~> 1.5"}
]
endconfig :permit_ex, repo: MyApp.RepoPassing the Scope to Absinthe
Load the authorization scope in your context-building plug and pass it through Absinthe's context map:
defmodule MyAppWeb.Context do
@behaviour Plug
def init(opts), do: opts
def call(conn, _opts) do
scope = conn.assigns[:current_scope]
Absinthe.Plug.put_options(conn, context: %{current_scope: scope})
end
endAdd it to your router pipeline:
pipeline :graphql do
plug MyAppWeb.AuthPipeline # sets conn.assigns.current_scope
plug MyAppWeb.Context
plug Absinthe.Plug, schema: MyApp.Schema
endField-Level Middleware
Require a permission on a single field:
middleware PermitEx.Absinthe.RequirePermission, "orders:manage"Require a role:
middleware PermitEx.Absinthe.RequireRole, "admin"Use RequireAuthorization for richer checks:
middleware PermitEx.Absinthe.RequireAuthorization,
any_permissions: ["orders:manage", "settings:manage"]Full Example
defmodule MyApp.Schema do
use Absinthe.Schema
mutation do
field :create_order, :order do
middleware PermitEx.Absinthe.RequirePermission, "orders:manage"
arg :input, non_null(:order_input)
resolve &MyApp.Resolvers.Orders.create/2
end
field :delete_order, :order do
middleware PermitEx.Absinthe.RequireAuthorization,
any_roles: ["admin", "support"]
arg :id, non_null(:id)
resolve &MyApp.Resolvers.Orders.delete/2
end
end
query do
field :orders, list_of(:order) do
middleware PermitEx.Absinthe.RequirePermission, "orders:view"
resolve &MyApp.Resolvers.Orders.list/2
end
end
endCustom Scope Key
If your context uses a different key:
middleware PermitEx.Absinthe.RequireAuthorization,
permission: "orders:manage",
assign_key: :auth_scopeCustom Error Message
middleware PermitEx.Absinthe.RequireAuthorization,
permission: "orders:manage",
message: "You do not have permission to manage orders"Multi-Tenant SaaS
Load the workspace-scoped scope before passing it to Absinthe:
def call(conn, _opts) do
workspace = conn.assigns[:current_workspace]
user = conn.assigns[:current_user]
scope = PermitEx.Scope.for_user(user, workspace)
Absinthe.Plug.put_options(conn, context: %{current_scope: scope})
endThe middleware checks apply the same context-aware role resolution as Plug and LiveView adapters.