1

I am trying to implement omniauth-twitter with Devise in Ruby-on-Rails with no success. According to the definitive article of Devise, the link

<%= link_to "Sign up with twitter", user_twitter_omniauth_authorize_path,
    method: :post %>

should take the visitor to Twitter (Note I have disabled turbo site-wide with Turbo.session.drive = false and so turbo is irrelevant).

However, it just displays

Not found. Authentication passthru.

I notice the route looks wrong in the first place:

% bin/rails routes -g omni
                          Prefix Verb     URI Pattern                             Controller#Action
user_twitter_omniauth_authorize GET|POST /users/auth/twitter(.:format)          users/omniauth_callbacks#passthru
 user_twitter_omniauth_callback GET|POST /users/auth/twitter/callback(.:format) users/omniauth_callbacks#twitter

It apparently points to Users::OmniauthCallbacksController#passthru, which is a non-existent method, hence the error?

This problem has been reported multiple times in Stackoverflow and Github (e.g., Github, SO1, SO2). A general advice seems to be using POST as opposed to GET. However, besides it is definitely POST in my case (as confirmed with a log file), I doubt if it is relevant to the routes!

What is the probable cause of this probblem and how can I solve it?

I confirm I am using OA1 API keys as opposed to Twitter OA2 (the latter is not supported by Omniauth, yet).. Also, as the doc of omniauth-twitter suggests, I confirm that "User authentication set up" is active in the Twitter Dev center to allow users to log in.

My relevant files are as follows:

 # Gemfile
gem 'devise'
gem 'devise-i18n'
gem 'omniauth', '~> 2.1' #, '1.9.1'
gem 'omniauth-twitter'
gem 'omniauth-rails_csrf_protection'
 # /config/initializers/devise.rb
Devise.setup do |config|
  config.omniauth :twitter, 'MY_APP', 'MY_SECRET'
  OmniAuth.config.logger = Rails.logger if Rails.env.development? # for debug
end

Note there are no Omniauth or Twitter-related configs in config/initializers/. Just devise.rb.

 # /models/user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable, #...snipped...
         :omniauthable, omniauth_providers: %i(twitter)

  def self.from_omniauth(auth)
    find_or_create_by(provider: auth.provider, uid: auth.uid) do |user|
      user.display_name = auth.info.nickname.strip
      user.skip_confirmation!
    end
  end

end
 # /app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  skip_before_action :verify_authenticity_token, only: [:twitter]

  def twitter
    callback_from __method__
  end

  def callback_from(provider)
    # See User model
    @user = User.from_omniauth(request.env['omniauth.auth'])

    if @user.persisted?
      sign_in_and_redirect @user, event: :authentication
      set_flash_message(:notice, :success, kind: provider.to_s.capitalize) if is_navigational_format?
    else
      session["devise.#{provider.to_s}_data"] = request.env['omniauth.auth'].except(:extra)
      redirect_to new_user_registration_url(from_omniauth_callback: true)
    end
  end

  def failure
    redirect_to root_path
  end
end
 # /config/routes.rb
Rails.application.routes.draw do
  devise_for :users, except: [:destroy], 
    controllers: { omniauth_callbacks: 'users/omniauth_callbacks' } 
end

View (the turbo-part is meaningless, for it is globally turned off).

 <%# /app/views/devise/registrations/new.html.erb >
<%= link_to t(".sign_up_with_twitter"), user_twitter_omniauth_authorize_path,
  method: :post, data: { turbo: false } %>

The standard output of the server (bin/dev in Rail 7):

00:... | I, [2022-...6 #793]  INFO -- : Started POST "/en/users/auth/twitter" for 127.0.0.1 at 2022-11-27 00:33:14 +0000
00:... | I, [2022-...4 #793]  INFO -- : Processing by Users::OmniauthCallbacksController#passthru as HTML
00:... | I, [2022-...0 #793]  INFO -- :   Parameters: {"authenticity_token"=>"[FILTERED]", "locale"=>"en"}
00:... | D, [2022-...0 #793] DEBUG -- :   Rendering text template
00:... | I, [2022-...7 #793]  INFO -- :   Rendered text template (Duration: 0.0ms | Allocations: 10)
00:... | I, [2022-...9 #793]  INFO -- : Completed 404 Not Found in 4ms (Views: 2.5ms | ActiveRecord: 0.0ms | Allocations: 1170)

Version information

  • omniauth-rails_csrf_protection (0.1.2) → (1.0.1)
    • Gemfile did not specify the version and yet a lower-version was installed. I now bundle install with '~> 1.0'. The same error still remains (after server-restart).
  • omniauth (2.1.0)
  • omniauth-oauth (1.2.0)
  • omniauth-twitter (1.4.0)
  • devise (4.8.1)
  • rails (7.0.4)
  • ruby (3.1.2)

That's it. Thank you.

Masa Sakano
  • 1,921
  • 20
  • 32
  • 1
    Please post your `routes.rb` file. – Chiperific Nov 27 '22 at 19:04
  • 1
    `users/omniauth_callbacks#passthru` should be a method provided by the `omniauth-twitter` gem to "pass through" to Twitter URL. [See here](https://github.com/arunagw/omniauth-twitter#authentication-options) – Chiperific Nov 27 '22 at 19:13
  • 1
    Can you show the POST method happening? Since you turned off Turbo, `method: :post` shouldn't actually do anything. Turbo is what uses `data-method="POST"` to turn a GET request into a POST request. – Chiperific Nov 27 '22 at 19:15
  • @Chiperific I have edited the question to include `routes.rb`, server log, and View. I see, `#passthru` is supposed to "pass through" to Twitter… It seems not working, even though in Twitter Dev center, "*User authentication set up*" is active to allow users to log in. – Masa Sakano Nov 27 '22 at 22:19
  • Please run `rails routes` and post output, just the omniauth portion is fine. – Chiperific Nov 28 '22 at 00:05
  • 1
    You have no Omniauth or Twitter configs in your `config/initializers` folder, right? – Chiperific Nov 28 '22 at 00:08
  • 1
    And can you confirm that `omniauth-rails_csrf_protection` is upgraded '~> 1.0' – Chiperific Nov 28 '22 at 00:11
  • 1
    And make sure your base `oauth` gem is pristine: `bundle update oauth` (from [here](https://stackoverflow.com/a/74154377/1880203)) – Chiperific Nov 28 '22 at 00:17
  • 1
    @Chiperific Omniauth-portion of routes are already included near the top of the post (which points to `passthru`). Sure, no Omniauth initializer is present. Version of `omniauth-rails_csrf_protection` was 0.1.2. It is now explicitly upgraded to 1.0.1, plus `bundle update oauth` was run, which changed nothing. Yet, the error still remains… The question is edited to reflect these and also to include other versions. Perplexing… – Masa Sakano Nov 28 '22 at 01:34

1 Answers1

1

This isn't a full answer, but more of a suggestion of where to keep looking.

You are supposed to see (twitter) Request phase initiated. in the logs immediately following Started POST "/users/auth/twitter" (from here).

But, the Omniauth controller is instead looking for an HTML template to render (and failing to find one).

INFO -- : Started POST "/en/users/auth/twitter" for 127.0.0.1 at 2022-11-27 00:33:14 +0000
INFO -- : Processing by Users::OmniauthCallbacksController#passthru as HTML
INFO -- :   Parameters: {"authenticity_token"=>"[FILTERED]", "locale"=>"en"}
DEBUG -- :   Rendering text template #<--- HERE!!!!
INFO -- :   Rendered text template (Duration: 0.0ms | Allocations: 10)
INFO -- : Completed 404 Not Found in 4ms (Views: 2.5ms | ActiveRecord: 0.0ms | Allocations: 1170)

This does make sense; because Users::OmniauthCallbacksController#passthru is getting the POST request as HTML, it's looking for an HTML template to render (and failing to find one).

It seems like Omniauth expects an AJAX request as JSON, not an HTML request.

A few thoughts:

  • Turn Turbo back on and see what the content-type of the request is when Turbo gets to decide

  • Ditch the user_twitter_omniauth_authorize_path in favor of intercepting the link click and forming an AJAX POST request using Stimulus (Rails 7) or Javascript directly to the path /users/auth/twitter.

  • The language "en" in the URL of the POST is probably coming from some other gem (devise-i18n?) That could be interfering with the POST request somehow and turning the content-type to HTML instead of JSON.

Chiperific
  • 4,428
  • 3
  • 21
  • 41
  • Thank you for your informative answer. I now understand the fact a HTML template is looked for is wrong… though "why" is yet to figure out… Turning Turbo back on resulted in the same, as far as the log is concerned (providing I was doing right – my knowledge of Turbo is limited). The locale in the URL is from the URL-param/path-based polity in [Rails standard I18n](https://guides.rubyonrails.org/i18n.html#setting-the-locale-from-url-params). Devise is fully I18n-compatible. I am not sure about OmniAuth now… – Masa Sakano Nov 28 '22 at 10:08