Ash / Phoenix deps update results in /sign-in error: Cannot set assign `current_members` before default `AshAuthentication.Phoenix.LiveSession.on_mount/4` has run

I just ran a relatively minor deps update on my Ash / Ash Auth / Phoenix project:

Upgraded:
  ash 3.5.32 => 3.5.36
  ash_authentication 4.9.8 => 4.9.9
  ash_authentication_phoenix 2.10.4 => 2.10.5
  ash_phoenix 2.3.11 => 2.3.14
  ash_postgres 2.6.13 => 2.6.16
  ash_sql 0.2.89 => 0.2.90
  bandit 1.7.0 => 1.8.0
  castore 1.0.14 => 1.0.15
  cc_precompiler 0.1.10 => 0.1.11
  fine 0.1.1 => 0.1.4
  igniter 0.6.25 => 0.6.28
  lazy_html 0.1.3 => 0.1.6
  phoenix 1.8.0-rc.4 => 1.8.0
  phoenix_live_view 1.1.0-rc.4 => 1.1.8
  postgrex 0.20.0 => 0.21.1 (minor)
  spark 2.2.67 => 2.2.68
  thousand_island 1.3.14 => 1.4.0

Probably the most notable was Phoenix Live View 1.1.0-rc4 → 1.1.8 (eek, not sure why that was stuck back there). Pretty minor Ash deps upgrade…

And now when I hit /sign-in (or, any other page that requires an authenticated user) I’m getting this:

[error] ** (RuntimeError) Cannot set assign `current_members` before default `AshAuthentication.Phoenix.LiveSession.on_mount/4` has run.
    (ash_authentication_phoenix 2.10.5) lib/ash_authentication_phoenix/live_session.ex:174: anonymous fn/5 in AshAuthentication.Phoenix.LiveSession.on_mount/4
    (elixir 1.18.4) lib/enum.ex:4515: anonymous fn/3 in Enum.reduce/3
    (elixir 1.18.4) lib/stream.ex:1761: anonymous fn/3 in Enumerable.Stream.reduce/3
    (elixir 1.18.4) lib/enum.ex:4968: Enumerable.List.reduce/3
    (elixir 1.18.4) lib/stream.ex:1773: Enumerable.Stream.do_each/4
    (elixir 1.18.4) lib/enum.ex:4515: Enum.reduce/3
    (ash_authentication_phoenix 2.10.5) lib/ash_authentication_phoenix/live_session.ex:170: AshAuthentication.Phoenix.LiveSession.on_mount/4
    (phoenix_live_view 1.1.8) lib/phoenix_live_view/lifecycle.ex:158: anonymous fn/4 in Phoenix.LiveView.Lifecycle.mount/3
    (phoenix_live_view 1.1.8) lib/phoenix_live_view/lifecycle.ex:237: Phoenix.LiveView.Lifecycle.reduce_socket/3
    (phoenix_live_view 1.1.8) lib/phoenix_live_view/utils.ex:346: anonymous fn/6 in Phoenix.LiveView.Utils.maybe_call_live_view_mount!/5
    (telemetry 1.3.0) /Users/zac/Projects/BossLogic/waste_walk/deps/telemetry/src/telemetry.erl:324: :telemetry.span/3
    (phoenix_live_view 1.1.8) lib/phoenix_live_view/static.ex:324: Phoenix.LiveView.Static.call_mount_and_handle_params!/5
    (phoenix_live_view 1.1.8) lib/phoenix_live_view/static.ex:155: Phoenix.LiveView.Static.do_render/4
    (phoenix_live_view 1.1.8) lib/phoenix_live_view/controller.ex:39: Phoenix.LiveView.Controller.live_render/3
    (phoenix 1.8.0) lib/phoenix/router.ex:416: Phoenix.Router.__call__/5
    (waste_walk 0.1.0) lib/waste_walk_web/endpoint.ex:1: WasteWalkWeb.Endpoint.plug_builder_call/2
    (waste_walk 0.1.0) deps/plug/lib/plug/debugger.ex:155: WasteWalkWeb.Endpoint."call (overridable 3)"/2
    (waste_walk 0.1.0) lib/waste_walk_web/endpoint.ex:1: WasteWalkWeb.Endpoint.call/2
    (phoenix 1.8.0) lib/phoenix/endpoint/sync_code_reload_plug.ex:22: Phoenix.Endpoint.SyncCodeReloadPlug.do_call/4
    (bandit 1.8.0) lib/bandit/pipeline.ex:131: Bandit.Pipeline.call_plug!/2

Which is kind of a black-box thing to break. Any ideas how I can diagnose and fix it?

I’m suspecting that perhaps my Ash Authentication original config is somehow out of date (maybe run a new igniter project, compare the results)… or… kinda at a loss.

//Edit

Well, generated a parallel project with latest libs. I don’t see any obvious differences between the new and the old, looked at all the config files, things like live_user_auth.ex etc., I don’t see any significant delta. Really weird.

Step one would likely be to isolate a specific upgrade that caused the issue, its minor version bumps but that is a lot to change at once and hard to say what the interacts :slight_smile:

Do you have an on_mount setting that assign in a LiveView when it’s being set by some other on mount hook? I’ve not seen that error before so it’s hard to say.

Nope. I have one Live page which, for now, I’ve completed commented out. My router.ex is identical to a fresh Ash Auth install.

What’s really bizarre (to me) is that the default Ash Auth login (it’s completely default, I haven’t changed a thing there…) is broken too. E.g. going to /sign-in.

:thinking:

So that error actually comes from AshAuthenticationPhoenix directly:

if Map.has_key?(socket.assigns, current_subject_name) do
  raise "Cannot set assign `#{current_subject_name}` before default `AshAuthentication.Phoenix.LiveSession.on_mount/4` has run."
end

Hm…does your user resource appear in multiple domains? Or do you have multiple resources set up with AshAuthentication? You’re not doing anything anywhere that could be assigning current_members?

I don’t think I understand those questions in all their nuance, so let me try to answer them as best I can. :slight_smile:

Hm…does your user resource appear in multiple domains? 

I don’t believe so, but I’m not entirely clear on what you mean by “appear in multiple domains.” There is a single user.ex in a single domain (WasteWalk.Accounts). The WasteWalk.Accounts.User resource is of course used in many places.

Or do you have multiple resources set up with AshAuthentication? 

Also, not entirely clear about what you mean, I assume you mean have I set up several different resources (other than Accounts.User) for authentication. No. And the only changes I’ve made to the User resource is to add support for a :role property, and add a few relationships to other resources, like many_to_many :teams, WasteWalk.Teams.Team.

You’re not doing anything anywhere that could be assigning current_members?

Not that I know of, no UX at this point (I was just starting to write the UX). Until now it’s all been back-end and worked fine. Lots of resource tests, all passing. I was just starting to add some UX, very first LiveView.

//EDIT

Ok, rolled back to pre-deps update. Did a clean and recompile. And…. still same behavior. So it was not the deps update after all – must be something I changed leading up to it.

……

Pinpointed it to a about half a dozen commits back. At first glance I don’t understand what’s breaking, but I did pin it down to a change in my Members resource. Has nothing to do with current_members, it’s the join between a Sprint and a User. But… weird, because it’s not used in any way yet, except to pass a bunch of unit tests for e.g., adding users to sprints.

Something is amiss there. Not making sense to my tired brain right now.

Too tired to fully pin it down – but, at least the root cause is localized!! :smiley: Will continue tomorrow.

Here’s the Members resource for reference.

defmodule WasteWalk.Sprints.Members do
  @moduledoc """
  The `WasteWalk.Sprints.Members` resource represents the many-to-many relationship between users and sprints, allowing for the management
  of sprint memberships.
  """

  use Ash.Resource,
    otp_app: :waste_walk,
    domain: WasteWalk.Sprints,
    data_layer: AshPostgres.DataLayer,
    authorizers: [Ash.Policy.Authorizer],  # take this out... works fine.
    extensions: [AshAuthentication]        #

  postgres do
    table "sprint_members"
    repo WasteWalk.Repo

    references do
      reference :user, on_delete: :delete, index?: true
      reference :sprint, on_delete: :delete
    end
  end

  actions do
    create :create, accept: [:user_id, :sprint_id], primary?: true
  end

  policies do
    bypass actor_attribute_equals(:role, :admin) do
      authorize_if always()
    end

    policy action_type([:create, :update, :destroy]) do
      # MAYBE-TODO make more robust custom check, eg., authorize if is_member_of_sprint(^actor(:id)) etc.
      authorize_if actor_attribute_equals(:role, :team_lead)
    end

    policy action_type(:read) do
      authorize_if always()
    end
  end

  relationships do
    belongs_to :user, WasteWalk.Accounts.User, primary_key?: true, allow_nil?: false
    belongs_to :sprint, WasteWalk.Sprints.Sprint, primary_key?: true, allow_nil?: false

    actions do
      defaults [:read, :destroy, update: :*]
    end
  end
end

And on the User resource:

  relationships do
    has_many :sprint_members, WasteWalk.Sprints.Members

    many_to_many :sprints, WasteWalk.Sprints.Sprint do
      join_relationship :sprint_members
      source_attribute_on_join_resource :user_id
      destination_attribute_on_join_resource :sprint_id
    end

    has_many :team_members, WasteWalk.Teams.Members

    many_to_many :teams, WasteWalk.Teams.Team do
      join_relationship :team_members
      source_attribute_on_join_resource :user_id
      destination_attribute_on_join_resource :team_id
    end
  end

Why add the ash authentication extension to this resource?

Aaaahhhh! Aha. Of course.

As for “why,” well… Probably because I copy/pasted.

BUT, I’m realizing I picked it up as a pattern, probably from the book or online docs, and it never occurred to me that having it there would break stuff. It’s kinda rare that a use does that. It might be worth adding a note about appropriate use and unintended consequences to the book / docs.

Oddly, it’s on the Teams.Members resource too… and that doesn’t cause any problems whatsoever. These two Members resources are almost exactly the same (but one for teams, one for sprints).

ANYHOW, thank you for pointing that out! After removing it (not just from this Members resource but a few other resources) all is well again. :+1:

It being on two resources is actually one of the first issues I asked about :joy:. The plug for setting the actor uses the “subject name” to assign to the socket or the conn, i.e “current_members”. It ensures that the assign is always present but nil if no token was given. We should provide a better error there naturally, but in this case the second pass failed thinking someone had touched its assign and was trying to warn you not to mess with it.