mix gen.auth template - signed_in_path vs. redirect-once-after-sign-in

Hi,

first oft all, I apologize.

I’ve created a scaffold via mix gen.auth.

Then i tried to generate a live view (which will use app.html.heex):

mix phx.gen.live TimeTracker Index index
mix ecto.create

Secondly i copied the suggested lines in router.ex

  scope "/time_tracker", TimeTrackerLiveWeb.TimeTracker, as: :time_tracker do
    pipe_through [:browser, :redirect_if_user_is_authenticated]

    live_session :redirect_if_user_is_authenticated,
      on_mount: [{TimeTrackerLiveWeb.TimeTracker.UserAuth, :redirect_if_user_is_authenticated}] do
      live "/users/register", UserRegistrationLive, :new
      live "/users/log_in", UserLoginLive, :new
      live "/users/reset_password", UserForgotPasswordLive, :new
      live "/users/reset_password/:token", UserResetPasswordLive, :edit
    end

    live_session :require_authenticated_user,
      on_mount: [{IndexLive.Index, :ensure_authenticated}] do
      live "/index", IndexLive.Index, :index
      live "/index/new", IndexLive.Index, :new
      live "/index/:id/edit", IndexLive.Index, :edit

      live "/index/:id", IndexLive.Show, :show
      live "/index/:id/show/edit", IndexLive.Show, :edit
    end

    post "/users/log_in", UserSessionController, :create
  end

Thirdly: i want to redirect to /time_tracker/index after sign-in-button-pressed:

so i adjusted: lib/time_tracker_live_web/time_tracker/user_auth.ex

  defp signed_in_path(_conn), do: ~p"/time_tracker/index"

this leads to an endless redirect loop.

i am absolute sorry,
is there no kind of redirect-once-after-sign-in?

Many thanks in advance.
Regards
jensu

You are piping the entire thing through :redirect_if_user_is_authenticated, even for the routes where the user is meant to be authenticated. What you probably want is something like:

scope "/", AppWeb do
  pipe_through [:browser, :redirect_if_user_is_authenticated]

  live "/users/register", ...
  # ...
end

scope "/", AppWeb do
  pipe_through [:browser, :require_authenticated_user]

  live_session :default, on_mount: {AppWeb.UserAuth, :require_authenticated} do
    live "/index", ...
    # ...
  end
end

I got the plug/on_mount names from master so they might have changed (e.g. yours is ensure_authenticated not require_authenticated). I don’t think there was ever a redirect_if_user_is_authenticated on_mount, though, so I’m not sure where you got that from.

Thanks for your reply.

I am using:

cat .tool-versions 
erlang 27.3.4
elixir 1.18.4-otp-27
rebar 3.25.0

and:

 mix archive.install hex phx_new
Resolving Hex dependencies...
Resolution completed in 0.054s
New:
  phx_new 1.7.21
* Getting phx_new (Hex package)
All dependencies are up to date
Compiling 11 files (.ex)
Generated phx_new app
Generated archive "phx_new-1.7.21.ez" with MIX_ENV=prod
Found existing entry: ~/.asdf/installs/elixir/1.18.4-otp-27/.mix/archives/phx_new-1.7.21
Are you sure you want to replace it with "phx_new-1.7.21.ez"? [Yn] Y
* creating ~/.asdf/installs/elixir/1.18.4-otp-27/.mix/archives/phx_new-1.7.21

Perhaps, i was wrong at creating the scaffold.
I will retry to create the scaffold, and than re-apply your information.

Regards
jensu

It appears I was wrong, there is in fact an on_mount(:redirect_if_user_is_authenticated, ...) in Phoenix 1.7. Looks like it’s going to be removed in 1.8, probably because it’s not really necessary. Also, in your version it was indeed ensure_authenticated instead of require_authenticated.

But anyway, the actual problem with your code as I mentioned is that you are using pipe_through [:redirect_if_user_is_authenticated] for both sections, which is why you have a loop. That’s what you need to fix.

Thanks a lot for checking this changed feature.
If i got it right, 1.8 is a release candidate, yet.
For that reason, i will use 1.7… .

I’ve re-created my scaffold.
Thaks a lot for your hint, i realized it now like this:

   scope "/", TimeTrackerLiveWeb do
    pipe_through [:browser, :require_authenticated_user]

    live_session :ensure_authenticated_user,
      on_mount: [{TimeTrackerLiveWeb.UserAuth, :require_authenticated}] do
      live "/index", IndexLive, :index
    end
  end

I could not use :require_authenticated_user,
which you had suggested… : this block is otherwise
defined twice… .

I’ve just adjusted:

defp signed_in_path(_conn), do: ~p"/index"

Postive: no loop of redirects, but unfortunatelly:

function TimeTrackerLiveWeb.TimeTrackerLiveWeb.IndexLive.__live__/0 is undefined (module TimeTrackerLiveWeb.TimeTrackerLiveWeb.IndexLive is not available)

Any hint is very much appreciated.
Many thanks in advance.

Regards
jensu

Yes, it’s okay to use 1.7. I was just reading the code for 1.8. It was my mistake :slight_smile:

The error says that TimeTrackerWeb is duplicated twice. Have you nested the scope twice somehow, like this?

scope "/", TimeTrackerWeb do
  scope "/", TimeTrackerWeb do
    live "/index, IndexLive, :index
  end
end

That is equivalent to:

live "/index", TimeTrackerWeb.TimeTrackerWeb.IndexLive, :index

Either flatten the structure or at least remove the module from one of the scopes:

scope "/", TimeTrackerWeb do
  scope "/" do
    # ...
  end
end

Thanks a lot!

I think i got it:

  • I’ve inserted the (‘index’) route into the existing block
  scope "/", TimeTrackerLiveWeb do
    pipe_through [:browser, :require_authenticated_user]

    live_session :require_authenticated_user,
      on_mount: [{TimeTrackerLiveWeb.UserAuth, :ensure_authenticated}] do
      live "/users/settings", UserSettingsLive, :edit
      live "/users/settings/confirm_email/:token", UserSettingsLive, :confirm_email
      live "/index", IndexLive
    end
  end
  • I’ve not adjusted the scope-block above, but the module defintion in the index-ex-file:
defmodule TimeTrackerLiveWeb.TimeTrackerLiveWeb.TimeTrackerLiveWeb.IndexLive do

plus using an alias for this adjusted module name in router.ex

alias TimeTrackerLiveWeb.TimeTrackerLiveWeb.TimeTrackerLiveWeb.IndexLive

→ now I can start developing my live-view application.
I am really happy!
Again, thanks a lot!

Regards
jensu

P.S.:
If you wanted to adjust the styling of auto-container,
refer to Editing wrapper element around <main> in LiveView - #3 by MatijaL

e.g. adjust app.css

div[data-phx-main] {
  display: flex;
  flex: 1 1 100%;
  width: 100%;
  height: 100%;
}

Those repeating parts in your module names are not necessary. If you remove them everywhere your app will behave the same.

If you run mix phx.new time_tracker and then create a LiveView IndexLive, it should come out like:

defmodule TimeTrackerWeb.IndexLive do
  use TimeTrackerWeb, :live_view
  # ...
end

# Router:
scope "/", TimeTrackerWeb do
  pipe_through ...

  live_session ... do
    live "/index", IndexLive
    # ...
  end
end

See the docs for module names/aliases.