I’m curious to understand a bit more what you had in mind. In the scopes guide, there’s a blog example that goes through the following three steps:
- run the auth generator
- run the live generator to create blog posts and add the routes to the router
- run the migration (which includes a reference to user_id thanks to step 1)
Now, the app has blog posts, but they’re locked down for every action. Let’s suppose the would be blog creator wants all site visitors to be able to read the blog posts instead of only the authors being able to read their own posts. Also they want visitors to be able to read blog posts without creating an account.
So, they’ll want to move the router line for live "/posts/:id", PostLive.Show, :show
and probably live "/posts", PostLive.Index, :index
out of the live_session
that requires authenticated users.
However, when doing this, there will be no function clause matching
errors for logged out users visiting /posts
or /post/1
, etc. After editing the context to resolve that, the Blog.get_post!/2
will fail.
In this case, the next steps might be something like…
- In the Blog context, add a function head to handle nil scopes
def subscribe_posts(nil), do: :ok
- Do the same for
broadcast/2
- Make alternate versions of
get_post
andlist_posts
that don’t include theuser_id
in the query sent to Ecto.
Is this roughly the workflow you envision people using for something that needs to be more open than the new default?
Edit: I’m guessing the answer is probably not, since this means navigating across live_sessions to go from one Blog action to another (and doing full page reloads as a result).
The pattern I’ve used for times in the past when some actions for a schema require authenticated users and some don’t has been to put them all in the same live_session block in the router and then do the authorization from the mount function of individual live views using the LiveHelpers.assign_user/2
(from previous phx generators) to ensure authentication.
Is there a pattern in particular you’re hoping to steer people towards in this scenario now?
Additional Edit: How about this pattern for when only some actions in a LiveView require authentication?
In UserAuth, create a custom on_mount like this:
def on_mount({:require_authenticated_unless, allowed_actions}, _params, session, socket) do
socket = mount_current_scope(socket, session)
current_user = socket.assigns.current_scope && socket.assigns.current_scope.user
action_always_allowed = socket.assigns.live_action in allowed_actions
if current_user || action_always_allowed do
{:cont, socket}
else
socket =
socket
|> Phoenix.LiveView.put_flash(:error, "You must log in to access this page.")
|> Phoenix.LiveView.redirect(to: ~p"/users/log-in")
{:halt, socket}
end
end
Then in any LiveView where it’s needed, use the custom on_mount hook to open specific actions (while still requiring authentication for all others)?
on_mount {AppWeb.UserAuth, {:require_authenticated_unless, [:show]}}