The host owns auth, authorization, durable audit identity, and display/redaction posture. That is not optional glue around Powertools; it is part of the public contract.
Auth seam shape
The configured auth_module should implement ObanPowertools.Auth:
defmodule MyAppWeb.ObanPowertoolsAuth do
@behaviour ObanPowertools.Auth
@impl true
def current_actor(%Plug.Conn{assigns: %{current_user: user}}), do: user
def current_actor(%{"current_user" => user}), do: user
def current_actor(_), do: nil
@impl true
def authorize(nil, _action, _resource), do: {:error, :unauthorized}
def authorize(%{role: :ops}, _action, _resource), do: :ok
def authorize(_actor, _action, _resource), do: {:error, :unauthorized}
@impl true
def audit_principal(%{id: id, email: email}) do
%{id: to_string(id), type: :user, label: email}
end
endThe key responsibilities are distinct:
current_actor/1extracts the current operator from the host session or socket contextauthorize/3decides whether a page or mutation is allowedaudit_principal/1returns the stable identity stored on immutable operator events
Do not collapse those into a single “is admin?” helper if your production app needs clearer operator attribution.
Display policy seam shape
The configured display_policy module should expose display/3.
The configured display_policy owns redaction and human-readable rendering:
defmodule MyAppWeb.ObanPowertoolsDisplayPolicy do
def display(:actor_label, actor, _context), do: actor.email
def display(:reason, reason, _context), do: reason
def display(:workflow_result, result, _context) do
%{summary: "Result available", payload: "[redacted]", redacted?: true, status: result.status}
end
def display(_kind, _value, _context), do: nil
endUse it to control:
- actor labels shown in native pages
- reason text rendering
- workflow result payload visibility
- any host-specific redaction of sensitive job metadata
Read-only vs mutation posture
Powertools-native pages are the supported mutation surface. Your policy should make the read-only case explicit instead of pretending unauthorized operators can “almost mutate.”
Practical pattern:
- allow broad page visibility for trusted support or SRE roles
- gate preview and execute actions separately in
authorize/3 - keep the optional
/ops/jobs/obanbridge read-only even for operators with broader native mutation permissions
Multi-tenant and support patterns
For real Phoenix SaaS apps, decide these seams deliberately:
- whether operators can cross tenant boundaries
- what tenant context must be visible in audit labels
- which payloads are redacted by default
- whether support impersonation is allowed and how it is labeled durably
If those answers are fuzzy, the library is not your real risk. The host policy is.