janko / rodauth-rails

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

turbo throws error when authenticating "view recovery codes" #67

Closed gruschis closed 2 years ago

gruschis commented 2 years ago

I finally got around to fiddle with this gem. Super convenient setup, great documentation.

I have implemented OTPs and Recovery Codes thanks to your blog article.

It's a fresh install: Rails 6.1.4.1 and Ruby 3.0.2. I have also added the latest turbo-rails.

When accessing /recovery-codes I get presented with a form to enter my password (two_factor_modifications_require_password? is true)

When submitting the form, turbo throws the following JavaScript error: Error: Form responses must redirect to another location.

turbo can't render the page since the location hasn't changed. I can work around it by disabling turbo for this particular form by adding data: { turbo: false } to the form_tag in _recovery_codes_form.html.erb

<!--_recovery_codes_form.html.erb-->
<%= form_tag rodauth.recovery_codes_path, method: :post, data: { turbo: false } do %>
...

The form submission then works as expected.

I wonder if there's another—more elegant—way to solve this. Maybe by passing some kind of setting in the class RodauthApp < Rodauth::Rails::App so it would work with turbo enabled. recovery_codes_route won't work since it's overriding the path for both, the form and the redirect.

Thank you!

janko commented 2 years ago

Thanks for reporting the issue, I would definitely like Rodauth to fully work with Turbo out-of-the-box. In this case, however, I'm not sure what could be done, other than disabling Turbo like you did.

From the security standpoint, recovery codes should probably remain viewable only via a POST route, as otherwise it cannot be password-protected. An alternative could be to use the confirm_password & password_grace_period features, which would allow you to enter the password in a separate dialog. But actually you would still be able to access the recovery codes only via POST additional changes in Rodauth config.

Given that the above strategy would rely on enabling two more features, I'm not sure that could be done automatically in rodauth-rails. I will think about other options.

gruschis commented 2 years ago

Thank you for your prompt response. To have these features added is no problem. I just haven't gotten around to try them. But as you said, this doesn't solve the problem with turbo, since the location is still the same.

Something similar happens when overriding the after_otp_setup following your blog article. The response.write add_recovery_codes_view causes the issue when rendering the auto-added recovery codes. data: { turbo: false } in the otp_setup.html.erb template helps here again.

Nevertheless, it is a good-enough trade-off for me right now. I'm curious to see if there will be a solution to this.

janko commented 2 years ago

For now I'm considering just adding data-turbo="false" to the HTML templates that ship with rodauth-rails. That still doesn't solve the built-in templates that are used by default, though.

janko commented 2 years ago

I've updated the recovery codes form with data-turbo="false" (only the one generated by rodauth:views, not the built-in one), and also updated the MFA article to recommend data-turbo="false" on the OTP setup form.

In the future it would be great if Turbo would allow opting in to 200 form submissions, ideally via a response header. I will probably propose this at some point to the maintainers, but for now disabling Turbo is the only possibility I saw. Returning a 4xx or 5xx status would also technically work, but it would be wrong, and could be problematic with monitoring tools.