Skip to content
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

token_verifier.rb:34 ActionController::InvalidAuthenticityToken #8

Open
ybakos opened this issue Nov 4, 2019 · 23 comments
Open

token_verifier.rb:34 ActionController::InvalidAuthenticityToken #8

ybakos opened this issue Nov 4, 2019 · 23 comments

Comments

@ybakos
Copy link

ybakos commented Nov 4, 2019

Please complete all sections.

Configuration

  • Provider Gem: omniauth-rails_csrf_protection 0.1.2
  • Ruby Version: 2.6.5
  • Framework: Rails
  • Platform: Heroku

Expected Behavior

Recent changes to csrf protection seem to cause a non-rescuable exception. Using

  • devise 4.7.1
  • omniauth 1.9.0
  • omniauth-google-oauth2 0.7.0
  • omniauth-oauth2 1.6.0
  • omniauth-rails_csrf_protection 0.1.2

application_controller.rb:

rescue_from ActionController::InvalidAuthenticityToken, with: :redirect_and_prompt_for_sign_in

protected

def redirect_and_prompt_for_sign_in
  redirect_to(new_user_session_path, alert: 'Please sign in.')
end

I expect that ApplicationController can catch the exception, but it does not.

Actual Behavior

An exception is raised with the following stacktrace.

/GEM_ROOT/gems/omniauth-rails_csrf_protection-0.1.2/lib/omniauth/rails_csrf_protection/token_verifier.rb:34

URL: https://myapp/users/auth/google_oauth2

/GEM_ROOT/gems/omniauth-rails_csrf_protection-0.1.2/lib/omniauth/rails_csrf_protection/token_verifier.rb:34 in call
/GEM_ROOT/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:209 in request_call
/GEM_ROOT/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:188 in call!
/GEM_ROOT/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169 in call
/GEM_ROOT/gems/warden-1.2.8/lib/warden/manager.rb:36 in block in call
/GEM_ROOT/gems/warden-1.2.8/lib/warden/manager.rb:34 in catch
/GEM_ROOT/gems/warden-1.2.8/lib/warden/manager.rb:34 in call
/GEM_ROOT/gems/rack-2.0.7/lib/rack/tempfile_reaper.rb:15 in call
/GEM_ROOT/gems/rack-2.0.7/lib/rack/etag.rb:25 in call
/GEM_ROOT/gems/rack-2.0.7/lib/rack/conditional_get.rb:38 in call
/GEM_ROOT/gems/rack-2.0.7/lib/rack/head.rb:12 in call
/GEM_ROOT/gems/actionpack-5.2.3/lib/action_dispatch/http/content_security_policy.rb:18 in call
/GEM_ROOT/gems/rack-2.0.7/lib/rack/session/abstract/id.rb:232 in context
/GEM_ROOT/gems/rack-2.0.7/lib/rack/session/abstract/id.rb:226 in call
/GEM_ROOT/gems/actionpack-5.2.3/lib/action_dispatch/middleware/cookies.rb:670 in call
/GEM_ROOT/gems/actionpack-5.2.3/lib/action_dispatch/middleware/callbacks.rb:28 in block in call
/GEM_ROOT/gems/activesupport-5.2.3/lib/active_support/callbacks.rb:98 in run_callbacks
/GEM_ROOT/gems/actionpack-5.2.3/lib/action_dispatch/middleware/callbacks.rb:26 in call
/GEM_ROOT/gems/airbrake-9.5.0/lib/airbrake/rack/middleware.rb:32 in call!
/GEM_ROOT/gems/airbrake-9.5.0/lib/airbrake/rack/middleware.rb:21 in call
/GEM_ROOT/gems/actionpack-5.2.3/lib/action_dispatch/middleware/debug_exceptions.rb:61 in call
/GEM_ROOT/gems/actionpack-5.2.3/lib/action_dispatch/middleware/show_exceptions.rb:33 in call
/GEM_ROOT/gems/railties-5.2.3/lib/rails/rack/logger.rb:38 in call_app
/GEM_ROOT/gems/railties-5.2.3/lib/rails/rack/logger.rb:26 in block in call
/GEM_ROOT/gems/activesupport-5.2.3/lib/active_support/tagged_logging.rb:71 in block in tagged
/GEM_ROOT/gems/activesupport-5.2.3/lib/active_support/tagged_logging.rb:28 in tagged
/GEM_ROOT/gems/activesupport-5.2.3/lib/active_support/tagged_logging.rb:71 in tagged
/GEM_ROOT/gems/railties-5.2.3/lib/rails/rack/logger.rb:26 in call
/GEM_ROOT/gems/actionpack-5.2.3/lib/action_dispatch/middleware/remote_ip.rb:81 in call
/GEM_ROOT/gems/actionpack-5.2.3/lib/action_dispatch/middleware/request_id.rb:27 in call
/GEM_ROOT/gems/rack-2.0.7/lib/rack/method_override.rb:22 in call
/GEM_ROOT/gems/rack-2.0.7/lib/rack/runtime.rb:22 in call
/GEM_ROOT/gems/activesupport-5.2.3/lib/active_support/cache/strategy/local_cache_middleware.rb:29 in call
/GEM_ROOT/gems/actionpack-5.2.3/lib/action_dispatch/middleware/executor.rb:14 in call
/GEM_ROOT/gems/actionpack-5.2.3/lib/action_dispatch/middleware/static.rb:127 in call
/GEM_ROOT/gems/rack-2.0.7/lib/rack/sendfile.rb:111 in call
/GEM_ROOT/gems/actionpack-5.2.3/lib/action_dispatch/middleware/ssl.rb:74 in call
/GEM_ROOT/gems/railties-5.2.3/lib/rails/engine.rb:524 in call
/GEM_ROOT/gems/puma-3.12.1/lib/puma/configuration.rb:227 in call
/GEM_ROOT/gems/puma-3.12.1/lib/puma/server.rb:660 in handle_request
/GEM_ROOT/gems/puma-3.12.1/lib/puma/server.rb:474 in process_client
/GEM_ROOT/gems/puma-3.12.1/lib/puma/server.rb:334 in block in run
/GEM_ROOT/gems/puma-3.12.1/lib/puma/thread_pool.rb:135 in block in spawn_thread

Steps to Reproduce

I apologize (!) but whatever I do in development or production, I can't seem to recreate this issue, but my end users in production can. I need to isolate exactly what they are doing and will post back.

@ybakos
Copy link
Author

ybakos commented Nov 4, 2019

@sikachu I will try to find the steps to reproduce.

@sikachu
Copy link
Collaborator

sikachu commented Nov 7, 2019

@ybakos sorry for the late reply, and thank you for your report.

This gem actually hooks into Rails request stack by adding itself in before_request_phase of OmniAuth's middleware. If you run rake:middleware, you can see that the request will hit OmniAuth middleware, raise exception, then skipping your application code (where ApplicationController live) entirely.

One way to workaround this issue is to by creating a middleware that rescue ActionController::InvalidAuthenticityToken and redirect user to sign in. Something like:

# app/middlewares/rescue_from_invalid_authenticity_token.rb
class RescueFromInvalidAuthenticityToken
  def call(env)
    yield
  rescue ActionController::InvalidAuthenticityToken
    [302, {'Location' => "/sessions/new, 'Content-Type' => 'text/html'}, ['Invalid Authenticity Token']]
  end
end

Then insert it before OmniAuth::Builder:

config.middlewares.insert_before "RescueFromInvalidAuthenticityToken", OmniAuth::Builder

I have not tested the code above, but I hope I can give you some idea.

@mbenitezm
Copy link

mbenitezm commented Nov 7, 2019

I am having this issue as well. I realized this problem existed when I deployed the first time to my sandbox environment. Other developers weren't able to access the login links because of this.

We found out that deleting cookies fixed the issue, but I don't want my users to delete their cookies when I release this to production.

@sikachu Is your proposed workaround a safe solution? meaning this would still protect against csrf?

Provider Gem: omniauth-rails_csrf_protection 0.1.2
Ruby Version: 2.4.5
Framework: Rails
Platform: Heroku

How to reproduce:
Click any post link that should redirect you to auth0 universal login in any none localhost environment before deleting cookies.

@sikachu
Copy link
Collaborator

sikachu commented Nov 11, 2019

@mbenitezm yes, it should not compromise the security as we pretty much send user back to the start to initiate the flow again.

I wonder what is the right way to work around this issue. Maybe having an option for you to specify how we should handle the bad token might be a good start ...

tagliala added a commit to diowa/icare that referenced this issue Jan 16, 2020
This approach has issues with cookies.

Ref: cookpad/omniauth-rails_csrf_protection#8
@arjun-urs
Copy link

+1 Having the same issue

@jarthod
Copy link

jarthod commented Mar 24, 2020

For the record, I recently tried to upgrade my application from rails 5.2.4.2 to rails 6.0.2.2 and I got this error at every form I try to submit, even without oauth if I just submit the login/password devise form ☹️

I'm using:

devise (4.7.1)
omniauth (1.9.1)
omniauth-github (1.4.0)
omniauth-google-oauth2 (0.8.0)
omniauth-oauth2 (1.6.0)
omniauth-rails_csrf_protection (0.1.2)

🕵️‍♂️ But I tried without this gem and saw that I was still getting CSRF errors (less visible though as maybe rescued differently). I followed rails 6 upgrade process which changes config.cache_store = :memory_store to config.cache_store = :null_store by default in dev env and that's what was causing the issue for me, simply because the CSRF token is stored in the session store (which is using cache_store by default) and the default store in dev is now disabled unless you explicitly enable caching.

So that's one thing to look for if you end up having this error, I'm not sure why they made this change at it can break every form by default 🤷‍♂️, hopefully it'll help some people 👍

@fschwahn
Copy link

fschwahn commented Apr 2, 2020

I wonder what is the right way to work around this issue. Maybe having an option for you to specify how we should handle the bad token might be a good start ...

This sounds like a good idea. Adding another middleware seems a bit clunky.

@HoneyryderChuck
Copy link

HoneyryderChuck commented Apr 17, 2020

AFAICT this happens in between deploys. Most rails applications handle token errors in application controller, however this is a middleware, to it never reaches the controller (as @sikachu already mentioned).

From the solutions I see described here, some mitigation strategy needs to be done at the rails level, so maybe this gem could rethink the approach and maybe implement the verification as a before_action instead of as a middleware? Because we already handle such errors there, and could treat it accordingly, while the extra-middleware approach is definitely clunky.

Also conscious that this is built on top of omniauth and omniauth is itself a middleware, so not sure how to best approach this.

@cyri113
Copy link

cyri113 commented May 29, 2020

Do you happen to be running the secure_headers gem?

@pasih
Copy link

pasih commented Jul 16, 2020

I just spent quite some time debugging this. In my case, I was following an auth0 tutorial that instructed to generate a link with <%= button_to "Login", "auth/auth0", method: :post %>. I was banging my head to a wall for a long time because of the InvalidAuthenticityToken exception.

Turns out that the path had to be "/auth/auth0" (slash in the beginning) for rails to correctly compare the path. Shrug. Maybe this helps someone else. Not sure if this is actually a Rails bug.. it seems at least little unfriendly.

@mindsublimes
Copy link

I was facing the same issue and i tried all the solutions given above but none of them worked for me. The issue was replicating whenever my cache was set to nil. Then I see the example app given by Auth0 and in that they were using cache_store in a different way inside development.rb

I tried with
config.cachestore = :memorystore
instead of
config.cache_store = :memory_store

and it worked for me. I dont know what is the difference between the two but it is working for me.
I hope it will help anyone else as well.

@ybakos
Copy link
Author

ybakos commented Aug 9, 2020

@mindsublimes Can you please post a link to the example app that shows this config difference?

@mindsublimes
Copy link

@sikachu
Copy link
Collaborator

sikachu commented Aug 11, 2020

I followed rails 6 upgrade process which changes config.cache_store = :memory_store to config.cache_store = :null_store by default in dev env and that's what was causing the issue for me, simply because the CSRF token is stored in the session store (which is using cache_store by default) and the default store in dev is now disabled unless you explicitly enable caching.

@jarthod that is very interesting. I feel like that's a bug in Rails if upgrading to Rails 6 switch your cache store to :null_store and break session. Would you mind letting me know which guide you followed and which script actually causing that, as we might want to mention that in the README?

maybe this gem could rethink the approach and maybe implement the verification as a before_action instead of as a middleware?

Yep, that won't work as omniauth is hooking into rails at middleware level so we have to be at this level as well.

I will have some time to working on the failure handling this week, and I'll cut a new version afterward. I'm going to keep this open for now as I want to hear more about the problem with the cache_store though.

@jarthod
Copy link

jarthod commented Aug 11, 2020

@jarthod that is very interesting. I feel like that's a bug in Rails if upgrading to Rails 6 switch your cache store to :null_store and break session. Would you mind letting me know which guide you followed and which script actually causing that, as we might want to mention that in the README?

I used the classic rake app:update command to upgrade to Rails 6 and that's the command which changed the cache store (which I saw and approved because I didn't think it would break anything). This is not necessarily a bug though because by default Rails 6 uses cookie store for sessions AFAIR so it does NOT depend on the cache store, but in my case I changed the session store to CacheStore (because I use this in production) and that's why removing the cache store broke the sessions for me.

So yeah maybe a little note about that in the Readme could help because the error message is clearly not obvious in this case but it'll boil down to: "make sure you have a session store that stores stuff" ^^. But people won't check the gem readme when they get this error I think, they'll just look it up online and find this issue that's why I added this comment here ;) Ideally if we can have a different error message for when the session store is simply returning nil maybe it could make it easier to lead people to the solution.

@sikachu
Copy link
Collaborator

sikachu commented Aug 11, 2020

but in my case I changed the session store to CacheStore (because I use this in production) and that's why removing the cache store broke the sessions for me.

Got it, thanks!

And yes, I think document that the CSRF token will be stored in session would be useful ... so I'll add that.

@ybakos
Copy link
Author

ybakos commented Aug 11, 2020

@sikachu @jarthod Is this related to the original issue ?

@jarthod
Copy link

jarthod commented Aug 11, 2020

@sikachu @jarthod Is this related to the original issue ?

It's related only because it generates the exact same error message "token_verifier.rb:34 ActionController::InvalidAuthenticityToken" which is in the title of this issue so people (like me) investigating it will find this issue first.

@vinaymehta
Copy link

@pasih Thanku So much man!

@rodoneill
Copy link

I had to set
Rails.application.config.action_controller.per_form_csrf_tokens = false
to get around this issue in apps I've upgrade to Rails 6.

adamkasztenny added a commit to adamkasztenny/hybridly that referenced this issue Apr 21, 2021
adamkasztenny added a commit to adamkasztenny/hybridly that referenced this issue Apr 21, 2021
@simonrentzke
Copy link

simonrentzke commented Oct 21, 2022

Thanks @sikachu, just to add to your code from here #8 (comment)

I've tested this code in the wild (Rails 7) and it works:

config.middleware.use RescueFromInvalidAuthenticityToken

class RescueFromInvalidAuthenticityToken
  def initialize app
    @app = app
  end

  def call(env)
     @app.call(env)
  rescue ActionController::InvalidAuthenticityToken
    env["rack.session"]["flash"] = ActionDispatch::Flash::FlashHash.new(alert: "Invalid Authenticity Token.")
    [302, {'Location' => "/sessions/new", 'Content-Type' => 'text/html'}, ['Invalid Authenticity Token']]
  end
end

@sergioisidoro
Copy link

sergioisidoro commented May 25, 2023

+1 for changing config.cache_store to memory store solved my issue (Rails 7.0.4)
I think this gem might not work on new Rails projects because of this default.

@NavneelDaniel
Copy link

NavneelDaniel commented Jun 7, 2023

When creating a form a csrf token is added to the form (suppose: token1) Remember: 'This token was generated in the begining by ActionController when a session is create'). When the request goes to the file token_verifier.rb of omniauth-rails_csrf_protection, there seems to be no session[:_csrf_token] present. Therefore it hits the ActionControlller of Rails and generate a new token (suppose token2). But the session stored has token1. The omniauth-rails_csrf_protection now checks rails' ActionController's verified_request? method and token2 doesn't match with token1.
Anyone has any idea why omniauth-rails_csrf_protection is not able to fetch the already generated token1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests