-
Notifications
You must be signed in to change notification settings - Fork 13.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RoR forms project - Update CSRF token related sections #29165
base: main
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -32,10 +32,34 @@ The first form you build will be mostly HTML (remember that stuff at all?). Bui | |||||||||
|
||||||||||
1. Build a form for creating a new user. See the [W3Schools page for forms](https://www.w3schools.com/tags/tag_form.asp) if you’ve totally forgotten how they work. Specify the `method` and the `action` attributes in your `<form>` tag (use `$ rails routes` to see which HTTP method and path are being expected based on the resource you created). Include the attribute `accept-charset="UTF-8"` as well, which Rails naturally adds to its forms to specify Unicode character encoding. | ||||||||||
|
||||||||||
You don't want to forget about safety, so make sure you provide the form with an authenticity token. If you don't remember how to do so, go back to the [Form Basics lesson](https://www.theodinproject.com/lessons/ruby-on-rails-form-basics#railsifying-your-form) and refresh your memory. | ||||||||||
|
||||||||||
1. Create the proper input tags for your user's fields (email, username and password). Use the proper password input for "password". Be sure to specify the `name` attribute for these inputs. Make label tags which correspond to each field. | ||||||||||
1. Submit your form and view the server output. You will see nothing happening, no error message, nothing. If you look at the network tab in your inspector or at your server log, you can see that a request was issued, but a response of `204 No Content` is returned. | ||||||||||
|
||||||||||
1. CSRF Safety: | ||||||||||
From Rails 7, Turbo is enabled by default in new apps. Turbo intercepts form submission and makes a partial XHR request instead of a standard HTTP request with full page reload. To get a better grasp of Rails protection against [cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery), let's take a small detour and disable Turbo for this form by setting the data attribute `data-turbo=false`. | ||||||||||
In the dev tools network tab, compare the request type with and without the `data-turbo=false` attribute to confirm it works as expected. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When using our markdown preview tool, I don't really like having the "CSRF Safety:" and the last sentence on different lines look. I propose rewording slightly to putting them all on 1 line, something like this:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated as proposed |
||||||||||
|
||||||||||
1. Submit your form and view the server output. The request should be intercepted before reaching your controller and the server will throw a CSRF error `ActionController::InvalidAuthenticityToken (Can't verify CSRF token authenticity.)`. | ||||||||||
|
||||||||||
That's because Rails by default automatically protects you from [cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery) and it requires you to verify that the form was actually submitted from a page you generated. In order to do so, it generates an ["authenticity token"](http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf) which looks like gibberish but helps Rails match the form with your session and the application. | ||||||||||
|
||||||||||
So, if you want to create your own form that gets handled by Rails, you need to provide the token somehow as well. Luckily, Rails gives you a method called `form_authenticity_token` to do so | ||||||||||
|
||||||||||
```erb | ||||||||||
<input | ||||||||||
type="hidden" | ||||||||||
name="authenticity_token" | ||||||||||
value="<%= form_authenticity_token %>" | ||||||||||
> | ||||||||||
``` | ||||||||||
|
||||||||||
You'll now notice the token in the server output: | ||||||||||
|
||||||||||
```bash | ||||||||||
... | ||||||||||
Parameters: {"utf8"=>"✓", "authenticity_token"=>"jJa87aK1OpXfjojryBk2Db6thv0K3bSZeYTuW8hF4Ns=", "email"=>"foo@bar.com", "commit"=>"Submit Form"} | ||||||||||
``` | ||||||||||
|
||||||||||
1. However, if you look at the server output, you will see nothing much happening after the log of parameters received. The log should indicate a completed response with status 204 (no content). And indeed, if you look at the network tab in your inspector, you can see that a request was issued, but a response of `204 No Content` is returned. | ||||||||||
1. That's A-OK because it means that we've successfully gotten through our blank `#create` action in the controller (and didn't specify what should happen next). Look at the server output. It should include the parameters that were submitted, looking something like: | ||||||||||
|
||||||||||
```bash | ||||||||||
|
@@ -46,6 +70,8 @@ The first form you build will be mostly HTML (remember that stuff at all?). Bui | |||||||||
|
||||||||||
That looks a whole lot like what you normally see when Rails does it, right? | ||||||||||
|
||||||||||
#### Controller setup | ||||||||||
|
||||||||||
1. Go into your UsersController and build out the `#create` action to take those parameters and create a new User from them. If you successfully save the user, you should redirect back to the New User form (which will be blank) and if you don't, it should render the `:new` form again (but it will still have the existing information entered in it). You should be able to use something like: | ||||||||||
|
||||||||||
```ruby | ||||||||||
|
@@ -85,6 +111,20 @@ Now we'll start morphing our form into a full Rails form using the `#form_tag` a | |||||||||
1. Test out your form. You'll need to change your `#create` method in the controller to once again accept normal top level User attributes, so uncomment the old `User.new` line and comment out the newer one. | ||||||||||
1. You've just finished the first step. | ||||||||||
|
||||||||||
#### Turn Turbo back ON | ||||||||||
|
||||||||||
Above, we asked to disable Turbo for the sake of the exercise. | ||||||||||
|
||||||||||
1. Re-enable form submission with Turbo by removing the `data-turbo=false` attribute on the form tag, then also remove the hidden input with CSRF token tag and submit. | ||||||||||
|
||||||||||
No more CSRF error!?! | ||||||||||
The from is now submitted with Turbo, yet Rails still protects you by verifying a CSRF token. Where does this token comes from? | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not a fan of how these lines lay out in the markdown preview either, though I don't really have a great solution in mind. Maybe add an empty line between 120 and 121? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here I have added an empty line to keep the "No more CSRF error" as a standalone observation that the reader should pause on. I did not put it on a separate numbered list item as it s not a new task. |
||||||||||
|
||||||||||
1. Check your inspector and your `application.html.erb` template. See a CSRF token that s always available? | ||||||||||
Remove this one too from `application.html.erb`, and verify that the server hits back with a CSRF error. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a typo here: "that s always available?". Also, I'm not a big fan of how this looks in the markdown preview. I would either put these two lines on the same line, or add an empty line in between (line lines 118-120). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated and put in a single paragraph. |
||||||||||
|
||||||||||
1. Reinstate the CSRF token tag in both places and carry on. | ||||||||||
|
||||||||||
#### Railsy-er forms with #form_with | ||||||||||
|
||||||||||
`#form_tag` probably didn't feel that useful -- it's about the same amount of work as using `<form>`, though it does take care of the authenticity token stuff for you. Now we'll convert that into `#form_with`, which will make use of our model objects to build the form. | ||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of combining these two titles with a hyphen, I'd prefer to combine them together. Maybe something like this:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated as proposed