This guide documents Threadline's current phx-gen-auth-reference lane: a maintained composition path for Phoenix hosts using mix phx.gen.auth (or equivalent generated session auth). It is a reference claim, not a blanket support promise for arbitrary generator versions, role fields, or non-Phoenix auth. This lane is narrower than generic phoenix-surface integration and is not Sigra-compatible.
Prerequisites
- Your host already ran
mix phx.gen.auth(or equivalent generated session auth). - Threadline does not add an auth Hex dependency; capture uses
Threadline.Plugcallbacks only. - The host owns user schema, session tables, and pipeline plugs.
Plug callback wire-up
Wire Threadline.Plug after session and scope assigns exist; see guides/integration-contracts.md.
Plug order (hard requirement):
plug :fetch_sessionplug :fetch_current_scope(or host equivalent from generatedUserAuth)plug Threadline.Plug, ...on pipelines with audited writes
Host module MyApp.AuditActor:
defmodule MyApp.AuditActor do
alias Threadline.Semantics.ActorRef
def actor_ref_from_conn(conn) do
case conn.assigns[:current_scope] do
%{user: %{id: id}} ->
{:ok, ref} = ActorRef.new(:user, to_string(id))
ref
_ ->
nil
end
end
def audit_context_overrides_from_conn(_conn), do: %{}
endRouter pipeline:
pipeline :browser do
plug :fetch_session
plug :fetch_current_scope
plug Threadline.Plug,
actor_fn: &MyApp.AuditActor.actor_ref_from_conn/1,
context_overrides_fn: &MyApp.AuditActor.audit_context_overrides_from_conn/1
end- Phoenix 1.7 legacy: if only
current_userexists, fall back insideactor_fnwith a shortcase conn.assigns[:current_user]branch — scope-first, not dual-primary.
Threadline.Plug derives request_id from x-request-id first and correlation_id from x-correlation-id first. context_overrides_fn is additive fill-only; unknown override keys raise ArgumentError.
Surface and export auth stay host-owned
Capture uses actor_fn; operator UI uses host-owned authorize_fn. Resolve the user scope-first via assigns[:current_scope].user, with current_user fallback for Phoenix 1.7 hosts:
defmodule MyApp.Audit do
@moduledoc false
def authorize_operator(%{assigns: assigns}) do
user =
case assigns[:current_scope] do
%{user: user} when not is_nil(user) -> user
_ -> assigns[:current_user]
end
case user do
%{is_admin: true} -> :ok
_ -> {:error, :unauthorized}
end
end
endthreadline_operator_surface "/audit",
authorize_fn: &MyApp.Audit.authorize_operator/1For advanced {:ok, scope} returns (support read-only, org scoping), see guides/integration-contracts.md and getting-started §9. See guides/operator-surface.md for export_authorize_fn, evidence, coverage, and policy callbacks. LiveView on_mount does not secure export HTTP routes; export uses export_authorize_fn or falls back to authorize_fn with %{assigns: conn.assigns}.
Reference semantics
- Scope-shaped
user.id→:useractor viaactor_fn. - Logged-out scope →
nilactor. - 1-arity
authorize_fnresolves scope-first (assigns[:current_scope].user, thencurrent_userfallback) and gates onis_admin: true. x-request-idheader wins over conn-derived request id.x-correlation-idheader wins; overrides are additive only.- Unknown override keys raise
ArgumentError.
Optional correlation strategy
Default context_overrides_fn returns %{}; headers win. Optionally propagate W3C traceparent, BFF-set x-correlation-id, or business ids via Threadline.Audit.transaction/3. Strict timeline :correlation_id filters need :action / Audit.transaction/3 — plug context alone is insufficient.
Non-goals
- Threadline does not run
mix phx.gen.auth. - Threadline does not own user tables or sessions.
- Threadline does not secure routes without host
require_authenticated_user/ pipeline plugs.
Lane and proof
Maintained composition path: this guide. Root CI proof: test/threadline/integrations/phx_gen_auth_integration_test.exs (mix verify.test). This is not a second example application. Reference semantics items 4–6 are covered by test/threadline/plug_test.exs.