5

ruby 3.0.1 rails 6.1.2 'devise', '~> 4.7', '>= 4.7.3'

I'm in a very unusual situation. I am migrating a rails installation from one server to another. I believe I am about 95% of the way, having just restored the production database.

However, anything involving a form submission, including user registration and logging in, gives me the error page:

The change you wanted was rejected. Maybe you tried to change something you didn't have access to. 

The server log gives me something more helpful:

Completed 422 Unprocessable Entity in 2ms (Allocations: 433) FATAL -- ActionController::InvalidAuthenticityToken 

This confuses me. Because I did regenerate the master.key and credentials.yml.enc and made the contents of the master.key available via the RAILS_MASTER_KEY environment variable. This means that the forms have the proper <input type="hidden" name="authenticity_token" value="<removed for stack_overflow>"> included to shield against cross-site scripting attacks.

I don't think it has anything to do with sessions because even user registration is affected by this. I am using Devise for authentication.

But... now I've hit a brick wall. There's nowhere to go from here. Does anyone know what's wrong?

Update 1

Adding skip_before_action :verify_authenticity_token did allow me to skip past the problem. I am not comfortable with that as a solution.

Update 2

I have these meta tags.

<%= csrf_meta_tags %> <%= csp_meta_tag %> 
8
  • Have you cleared your browser cookies and retried yet? Commented Jul 18, 2021 at 1:34
  • I just now tried doing that. The problem persists, though. I would have been blown away if that was the solution. Commented Jul 18, 2021 at 1:40
  • are you using devise ? Commented Jul 18, 2021 at 3:19
  • Yes, I'll add that to my post in case that helps others. Commented Jul 18, 2021 at 3:22
  • this maybe help: gist.github.com/db0sch/19c321cbc727917bc0e12849a7565af9, i read and see an important note: "..had the problem with devise.rb. I just uncommented the line secret_key = ... just the time to run the command to regenerate the credentials file, and then commented the line out again". Commented Jul 18, 2021 at 3:33

2 Answers 2

8

I'm ashamed to admit it. But nginx was not configured correctly. Once I got to the breaking point with rails, I finally started asking how else this could be happening.

Here's the deal. Internally, rails was using http to hit various endpoints. I had an nginx redirect block that looked like this:

 server { listen 80 default_server; server_name _; return 301 https://$host$request_uri; } 

This meant that every time the internal endpoint was hit on http, it would turn into https with a 301... Which always is a GET. Once I used config.force_ssl = true in production.rb, I just had to redo my ngix config to support it.

This was the working config.

location / { proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_redirect off; proxy_pass http://app:3000; } 

The reason it looked like an auth token issue was because once the request was sent as a GET, it was then an unprocessable entity. Likely the token was nil or something by the time it got there, even though I could see it in the browser inspect. It was just the verb that was wrong.

Sign up to request clarification or add additional context in comments.

2 Comments

proxying to a local port isn't ideal by the way (this is also how I used to run this too xD) apache has mod_passenger which is great for rails and I think they support nginx too. phusionpassenger.com/library/config/nginx/intro.html It's better to run a real app sever integerated with nginx.
Thank you for writing you solution, I had same problem while upgrading rails to 7.0.1 and I missed this line proxy_set_header X-Forwarded-Proto https; within my nginx configuration,
1

Your forms aren't sending the X-CSRF-TOKEN header along with their request. This header is a security feature and you're right to feel uncomfortable turning it off. It's part of what prevents someone on evil.com sending a form to yourwebsite.co.

Out of the box rails <%= forms_* %> do this for you so you likely have something custom.

If you're sending these requests from javascript. I do something this like:

let token = document.querySelector("meta[name='csrf-token']").content fetch(url, { method, headers: { 'X-CSRF-Token': auth_token, // <---- this 'X-Requested-With': 'XMLHttpRequest', ... }, ... 

It's also possible you aren't including it in your application.html.erb so the forms cannot find it.

<!DOCTYPE html> <html> <head> <%= csrf_meta_tags %> <!-- you need this! --> ... 

3 Comments

Looks like I have two tags similar to that one. <%= csrf_meta_tags %> and <%= csp_meta_tag %> I've also noticed that every form fails, even ones not related to authentication. Updating my post. I have a very strong feeling this isn't even related to Rails at all. The token is being sent properly. My next thing to investigate is my Nginx config. Thank you for taking the time to respond.
Just a note you should leave both CSP and CSRF. CSP is content security policy which controls where content can come from (eg. should I load a script form evil.com or block it) and CSRF helps prevent js on other websites from making requests to your domain.
Thank you for your detailed answer. Even though it didn't turn out to be the solution I needed, I still appreciate the time. I feel like I finally understand the purpose of why that token exists in the first place.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.