Skip to content

Commit

Permalink
Update CSRF token related sections
Browse files Browse the repository at this point in the history
  • Loading branch information
jg-k committed Dec 8, 2024
1 parent 4cc0a62 commit 7cf4484
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 25 deletions.
23 changes: 1 addition & 22 deletions ruby_on_rails/forms_and_authentication/form_basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,7 @@ The first line tells us which HTTP method was used and which route the form went

You'll find yourself looking at this server output a lot when you start building forms. It'll keep you sane because it tells you exactly what the browser sent back to your application so you can see if there's been a... misunderstanding.

### Railsifying your form

The first thing you'll realize if you try to create a plain vanilla form in a Rails view is that it won't work. You'll either get an error or your user session will get zeroed out (depending on your Rails version). 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.

You'll notice the token in the server output from above:

```bash
...
Parameters: {"utf8"=>"", "authenticity_token"=>"jJa87aK1OpXfjojryBk2Db6thv0K3bSZeYTuW8hF4Ns=", "email"=>"[email protected]", "commit"=>"Submit Form"}
```

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, and we'll cover it in the project.

```erb
<input
type="hidden"
name="authenticity_token"
value="<%= form_authenticity_token %>"
>
```

### Making forms into params
### Railsifying your form - Making forms input into params

What about the other form inputs, the ones we actually care about?

Expand Down
46 changes: 43 additions & 3 deletions ruby_on_rails/forms_and_authentication/project_forms.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

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"=>"[email protected]", "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
Expand All @@ -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
Expand Down Expand Up @@ -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?

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.

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.
Expand Down

0 comments on commit 7cf4484

Please sign in to comment.