Hotwire Discussion

Prevent user from accessing edit view directly (turbo-frame only view)

In a regular Rails 7 application, if you build an #index action that lists Tweets, and you want the user to be able to edit the tweets inline, you could do something like this:

# _tweet.html.erb
<%= turbo_frame_tag(tweet) do %>
  <p><%= tweet.text %></p>
  <%= link_to "Edit this tweet", edit_tweet_path(tweet) %>
<% end %>

And then in tweets/edit.html.erb:

<%= turbo_frame_tag(tweet) do %>
  <%= render "form", tweet: @tweet %>
<% end %>

Even tough it works, I’d like to prevent users from hitting the URL /tweets/1/edit directly, so that the edit.html.erb is only accessible via the Turbo frame request.

However, these edit request (which is not a turbo_stream form submissions) is actually just a regular HTML request (it prints Processing by TweetsController#edit as HTML in the console), so by doing this:

def edit
  respond_to {|format| format.turbo_stream }
end

I can’t prevent users from accessing /tweets/1/edit directly, since it’s not a turbo stream.

In the good ol days of Rails UJS, this could be easily achieved by restricting the controller to respond to format.js, like so:

def edit
  respond_to {|format| format.js }
end

Or only providing a edit.js.erb file in the views folder, because then HTML requests wouldn’t be served if the user requested /tweets/1/edit` directly (as it would be an HTML request).

Any way to achieve the same using Hotwire & Turbo?

Have you tried implementing an authorization mechanism. It seems to me that your issue cannot be solved by turbo per se. But, you need to implement some role based authorization in your controller.

So, using something like pundit you can authorise the resource.

def edit
   authorise @tweet
end