janko / rodauth-rails

Rails integration for Rodauth authentication framework
https://github.com/jeremyevans/rodauth
MIT License
584 stars 40 forks source link

[ignore: user error] ActionController::InvalidAuthenticityToken after loading session from memory (`remember` feature) #11

Closed bjeanes closed 4 years ago

bjeanes commented 4 years ago

It appears that session[:_csrf_token] is not set in the session in this case. This causes it to be lazily set on each check of the submitted token but because it is generated after the token used in the form, it does not match.

On a page load after session restore (i.e. delete the session cookie, but leave the remember cookie):

>> session.to_h
=> {"account_id"=>"9dd41226-d110-4b13-b6de-c8d6b09d441f", "active_session_id"=>"XSrfiUyX_MYIatmN1HyJMglgwFHOQOUR0EmWfi14fY4", "authenticated_by"=>["remember"], "two_factor_auth_setup"=>false

I have confirmed that session[:_csrf_token] is set after the failed token (on the server side only, though) and is different on every occurrence.

rails_controller_instance.send(:real_csrf_token, session) is also different between requests, because the real token is not being stored in the session.

bjeanes commented 4 years ago

(Whoops I didn't intend to submit that issue yet as I am still spelunking around...)

What confuses me is that simply fetching the token (i.e. to render into the form) should be setting it in the session:

>> session.to_h
=> {"account_id"=>"9dd41226-d110-4b13-b6de-c8d6b09d441f", "active_session_id"=>"VeC4l-xoqRfB90HhUjMgitrnn9zIt3vMU475OsSoiVI", "authenticated_by"=>["remember"], "two_factor_auth_setup"=>false}
>> rails_csrf_token
=> "1G7iFBLwiBTqAUV7h8oBuCFH/iBzbEy+3oA51yS/s0eUgW8GB0GjBi7SyPBMVsJArnsrL+RpOtKZMTbcwW/e1Q=="
>> session.to_h
=> {"account_id"=>"9dd41226-d110-4b13-b6de-c8d6b09d441f", "active_session_id"=>"VeC4l-xoqRfB90HhUjMgitrnn9zIt3vMU475OsSoiVI", "authenticated_by"=>["remember"], "two_factor_auth_setup"=>false, "_csrf_token"=>"Lq6wUavZO0xdKgX5tZrs/Fk12taoeKxvozS6U0podE4="}

I have an inkling that perhaps this is happening in the view layer with a copy of the session instead of the one which the session middleware can see and send back to the user.

Inspecting the session before and after the form is renders shows that _csrf_token is in the session:

image

and yet, if I add a before_rodauth block to inspect the request.session BEFORE authenticity token verification can mutate the session, the key is absent and after authenticity token verification, the key is present but different than the one that should be in the session:

=> {"account_id"=>"9dd41226-d110-4b13-b6de-c8d6b09d441f", "active_session_id"=>"ATTz8uBbrX3tY5PfdBu2eSVzScqqz0w0X4wD80pRk9Q", "authenticated_by"=>["remember"], "_csrf_token"=>"N1JemnFSB0q+5DGNl8bwHrR/b4sRBkOZdkbGShL2aqI="}
bjeanes commented 4 years ago

OK another thing I noticed is that session["active_session_id"] is also changing each request (which it does NOT when logged in via password.

I am guessing the load_memory thing is constantly replacing the session on each request with a fresh one constructed based on the remember token. :\

I'm not as sure now if this is a rodauth defect or intentional behaviour which is reasonable in that context but doesn't work here so needs to be mitigated by rodauth-rails.

What do you think @jeremyevans @janko ?

bjeanes commented 4 years ago

OK OK this is all my bad.

I have multiple configurations but I had NOT overridden remember_cookie_key to be different in each case.

I haven't exactly determined how this results in the behaviour I am experiencing to be honest (I'd expect it would just log me in with both configurations for that underlying account, as they use the same tables), but setting remember_cookie_key on either one to be different than the other DOES appear to fix my issue.

Sorry for any time wasted!

bjeanes commented 4 years ago

It would be a backwards incompatible change, but it might make sense to incorporate the name of the configuration into any session or cookie keys for the default values @jeremyevans. WDYT?

jeremyevans commented 4 years ago

It's not possible for the cookie key to be different. Theoretically, we could make the session keys prefixed with the rodauth configuration name, but you are correct that it would break backwards compatibility, and I'm not sure it's worth it. Maybe we can add something like session_key_prefix which would allow you to change the default values for all session keys to prefix them with a string?

bjeanes commented 4 years ago

It's not possible for the cookie key to be different.

I'm not sure I understand what you are saying here. I was able to change the remember_cookie_key. Or do you mean to change the default (because of backwards compat)?

setting remember_cookie_key on either one to be different than the other DOES appear to fix my issue.

It may have also been the change to session keys that did this, although keeping the remember cookie separate is also necessary for the ultimate desired behaviour (i.e. being able to remember one login but not the other).

Thanks to you both for jeremyevans/rodauth#121 while I slept! :pray: :pray: :pray:

jeremyevans commented 4 years ago

It's not possible for the cookie key to be different.

I'm not sure I understand what you are saying here. I was able to change the remember_cookie_key. Or do you mean to change the default (because of backwards compat)?

I was referring to the session cookie key. Rodauth just uses the Roda session, which will only look at a single cookie key, not a separate key based on the Rodauth configuration. Rereading your earlier comment, I can see you were only talking about the remember_cookie_key, so I apologize for the confusion.

And you are correct that changing the default remember_cookie_key for alternate configurations would break backwards compatibility and is not something that could not be done outside of a major version bump.

bjeanes commented 4 years ago

Ack. Thanks for the clarification :)