lynndylanhurley / devise_token_auth

Token based authentication for Rails JSON APIs. Designed to work with jToker and ng-token-auth.
Do What The F*ck You Want To Public License
3.55k stars 1.14k forks source link

Unsafe redirect on signup [Rails 7.0] #1536

Open urbanrotnik opened 2 years ago

urbanrotnik commented 2 years ago

I am getting an UnsafeRedirectError at:

ActionController::Redirecting::UnsafeRedirectError: Unsafe redirect to "https://example.com/signup?auth_token=myAuthToken&blank=true&client_id=ClientID...", pass allow_other_host: true to redirect anyway.
File "/app/vendor/bundle/ruby/3.1.0/gems/actionpack-7.0.3/lib/action_controller/metal/redirecting.rb" line 193 in _enforce_open_redirect_protection
File "/app/vendor/bundle/ruby/3.1.0/gems/actionpack-7.0.3/lib/action_controller/metal/redirecting.rb" line 89 in redirect_to
File "/app/vendor/bundle/ruby/3.1.0/gems/actionpack-7.0.3/lib/action_controller/metal/instrumentation.rb" line 42 in block in redirect_to
File "/app/vendor/bundle/ruby/3.1.0/gems/activesupport-7.0.3/lib/active_support/notifications.rb" line 206 in block in instrument
File "/app/vendor/bundle/ruby/3.1.0/gems/activesupport-7.0.3/lib/active_support/notifications/instrumenter.rb" line 24 in instrument
File "/app/vendor/bundle/ruby/3.1.0/gems/activesupport-7.0.3/lib/active_support/notifications.rb" line 206 in instrument
File "/app/vendor/bundle/ruby/3.1.0/gems/actionpack-7.0.3/lib/action_controller/metal/instrumentation.rb" line 41 in redirect_to
File "/app/vendor/bundle/ruby/3.1.0/bundler/gems/devise_token_auth-798255ee7e3f/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb" line 232 in render_data_or_redirect
File "/app/vendor/bundle/ruby/3.1.0/bundler/gems/devise_token_auth-798255ee7e3f/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb" line 73 in omniauth_success

Adding config.action_controller.raise_on_open_redirects = false to applcation.rb would help, but redirect should be fixed I think.

muyiwaoyeniyi commented 1 year ago

@urbanrotnik I ran into the same issue. A quick workaround is to do a double redirect. Provide a same-host url to devise_token_auth that will route to one of your controller actions and you can route to whatever url you want. But now it's in your control.

malopez16 commented 1 year ago

@muyiwaoyeniyi could you share the changes you've done step by step 🙏 I know parameters should be passed but I'm confused enough trying to get the whole flow to work...

muyiwaoyeniyi commented 1 year ago

@malopez16 I can't put together a code snippet at the moment. This issue is happening because you're redirecting to a domain different from your server's domain. So here's what I did... set the redirect_url to a route on your server, then in the controller action for the route, you can redirect to the original/final url but add the allow_other_host flag... so

redirect_to FINAL_URL, allow_other_host: true

This goal here is to get devise token auth to redirect to a safe url (controller action) that is within your control and then from there you can redirect wherever you want.

micred commented 1 year ago

Exactly, we monkey-patch device_token_auth as follow to add allow_other_host: true Disclaimer: it's a quick and dirty solution.

config/initializers/devise_token_auth_monkey_patching.rb

Rails.configuration.to_prepare do
  DeviseTokenAuth::ConfirmationsController.class_eval do
    def show
      @resource = resource_class.confirm_by_token resource_params[:confirmation_token]

      if @resource.errors.empty?
        yield @resource if block_given?

        redirect_header_options = {account_confirmation_success: true}

        if signed_in? resource_name
          token = signed_in_resource.create_token
          signed_in_resource.save!

          redirect_headers = build_redirect_headers(token.token,
                                                    token.client,
                                                    redirect_header_options)

          redirect_to_link = signed_in_resource.build_auth_url redirect_url, redirect_headers
        else
          redirect_to_link = DeviseTokenAuth::Url.generate redirect_url, redirect_header_options
        end

        redirect_to redirect_to_link, allow_other_host: true
      else
        redirect_to DeviseTokenAuth::Url.generate(redirect_url, account_confirmation_success: false), allow_other_host: true
      end
    end
  end
end
GMolini commented 1 year ago

I was having this same problem for the password reset flow. In my case, I just had to add allow_other_host: true in my passwords controller, as the default redirect in the DeviseTokenAuth::PasswordsController has a redirect_options hash. Not sure why the confirmations controller doesnt

class Auth::PasswordsController < DeviseTokenAuth::PasswordsController

  private
    def resource_params
      params.permit(:email, :reset_password_token, :config, :redirect_url)
    end

    def redirect_options
      {
        allow_other_host: true
      }
    end

end
lisimba commented 2 months ago

Since an ActionController::Redirecting::UnsafeRedirectError is raised, you can rescue from it and handle it accordingly. In my specific scenario, I did the following in my ApplicationController. This way, I was able to perform the necessary redirect:

class ApplicationController < ActionController::Base
  rescue_from ActionController::Redirecting::UnsafeRedirectError do
    redirect_to root_url, allow_other_host: true
  end
end