Key :current_scope not found in assigns (Phoenix 1.8.0-rc.3)

I am new to Phoenix and having a problem with Phoenix 1.8.0-rc.3 and the new user scope when using generated resources.

When I use the generator to create a new Phoenix app called scope,

$ mix phx.new scope

then create a LiveView based authentication system,

$ mix phx.gen.auth Accounts User users

then create a scoped LiveView resource

$ mix phx.gen.live Curriculum Level levels name:string difficulty:integer

and add the generated live rules to router.ex so that the request goes through the :browser pipeline that contains the plug :fetch_current_scope_for_user

scope "/", ScopeWeb do
    pipe_through(:browser)

    get("/", PageController, :home)

    live("/levels", LevelLive.Index, :index)
    live("/levels/new", LevelLive.Form, :new)
    live("/levels/:id", LevelLive.Show, :show)
    live("/levels/:id/edit", LevelLive.Form, :edit)
  end

and then start the app and navigate to /levels,

I get the following error:

[error] ** (KeyError) key :current_scope not found in: %{__changed__: %{}, flash: %{}, live_action: :index}

I left out the obvious steps of fetching dependencies and creating and migrating the database.

I must be doing something wrong, but cannot find what causes the problem.

Both branches of the fetch_current_scope_for_user/2 plug defined in user_auth.ex assign :current_scope, so it should be in the assigns?

I am essentially just following along the video on this site, only with a different version of Phoenix: Elixir learning: A first look at Phoenix scopes

Any help appreciated, thanks!

I’m on the subway so keeping it short, but you need to wrap your live routes with an on_mount that fetches the scope, the browser one is for the initial request but then the liveview also needs to populate the assigns since it’s on a websocket.
Look for :mount_current_scope here Scopes — Phoenix v1.8.0-rc.0

1 Like

Thanks a lot, the part about the plug being only used for the initial request makes total sense.

I first added the mount hook to the LiveView, but then started wondering why the generator wouldn’t do this by default.

defmodule ScopeWeb.LevelLive.Show do
  use ScopeWeb, :live_view
  on_mount {ScopeWeb.UserAuth, :mount_current_scope}

I then re-examined router.ex and found that the hook was also set in a live_session (a concept I wasn’t aware of and will have to read up on) and simply added my routes there.

  scope "/", ScopeWeb do
    pipe_through([:browser])

    live_session :current_user,
      on_mount: [{ScopeWeb.UserAuth, :mount_current_scope}] do
      live("/users/register", UserLive.Registration, :new)
      live("/users/log-in", UserLive.Login, :new)
      live("/users/log-in/:token", UserLive.Confirmation, :new)

      live("/levels", LevelLive.Index, :index)
      live("/levels/new", LevelLive.Form, :new)
      live("/levels/:id", LevelLive.Show, :show)
      live("/levels/:id/edit", LevelLive.Form, :edit)
    end

    post("/users/log-in", UserSessionController, :create)
    delete("/users/log-out", UserSessionController, :delete)
  end

So that was what the generator was trying to tell me with

Ensure the routes are defined in a block that sets the `:current_scope` assign.

Now I get an error when I navigate to /levels without being logged in, so probably this is a better place for my routes? I guess I have to do some more reading…

 scope "/", ScopeWeb do
    pipe_through([:browser, :require_authenticated_user])

    live_session :require_authenticated_user,
      on_mount: [{ScopeWeb.UserAuth, :require_authenticated}] do
      live("/users/settings", UserLive.Settings, :edit)
      live("/users/settings/confirm-email/:token", UserLive.Settings, :confirm_email)

      live("/levels", LevelLive.Index, :index)
      live("/levels/new", LevelLive.Form, :new)
      live("/levels/:id", LevelLive.Show, :show)
      live("/levels/:id/edit", LevelLive.Form, :edit)
    end

    post("/users/update-password", UserSessionController, :update_password)
  end
1 Like

require_authenticated is the right place!

3 Likes