I cant place my new middleware where I want to place it in the rack stack

884 Views Asked by At

I wrote a new middleware called RescueAuthTokenFromOmniauth, which rescues an exception created by either OmniAuth::Strategies::GoogleOauth2 or OmniAuth::Strategies::Facebook. Ideally, I want to place RescueAuthTokenFromOmniauth just before the Omniauth middleware in the stack.

I can add RescueAuthTokenFromOmniauth to the stack by using

# application.rb
config.middleware.use RescueAuthTokenFromOmniauth

But when I try to do, for example,

# application.rb
config.middleware.insert_before(OmniAuth::Strategies::GoogleOauth2, RescueAuthTokenFromOmniauth)

I get the following error when running rails middleware:

No such middleware to insert before: OmniAuth::Strategies::GoogleOauth2

The Omniauth::Strategies middleware was added by the gems omniauth-google-oauth2 and omniauth-facebook.

Is there a way to reference OmniAuth::Strategies::GoogleOauth2 in application.rb so that I can place my new middleware just before it?

I'm using rails (6.0.3.6).

Here's the result of running rails middleware. It already includes RescueAuthTokenFromOmniauth by using config.middleware.use RescueAuthTokenFromOmniauth

use Webpacker::DevServerProxy
use ActionDispatch::HostAuthorization
use Rack::Sendfile
use ActionDispatch::Static
use ActionDispatch::Executor
use Rack::Rewrite
use ActiveSupport::Cache::Strategy::LocalCache::Middleware
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use RequestStore::Middleware
use ActionDispatch::RemoteIp
use Sprockets::Rails::QuietAssets
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use WebConsole::Middleware
use ActionDispatch::DebugExceptions
use BetterErrors::Middleware
use Rollbar::Middleware::Rails::RollbarMiddleware
use ActionDispatch::ActionableExceptions
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ContentSecurityPolicy::Middleware
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use Rack::TempfileReaper
use Warden::Manager
use RescueAuthTokenFromOmniauth
use Rack::Attack
use Bullet::Rack
use OmniAuth::Strategies::GoogleOauth2
use OmniAuth::Strategies::Facebook
run SaverLife::Application.routes

These are all the settings regarding middleware in application.rb

require_relative '../lib/rescue_auth_token_from_omniauth'
...
config.middleware.use RescueAuthTokenFromOmniauth

My new middleware defined in lib/rescue_auth_token_from_omniauth.rb

class RescueAuthTokenFromOmniauth
  def initialize(app)
    @app = app
  end

  def call(env)
    begin
      @app.call env
    rescue ActionController::InvalidAuthenticityToken
      [302, {'Location' => "/users/sign_up", 'Content-Type' => 'text/html'}, ['Invalid Authenticity Token']]
    end
  end
end

I add OmniAuth middleware inside devise.rb

  # ==> OmniAuth
  # Add a new OmniAuth provider. Check the wiki for more information on setting
  # up on your models and hooks.
  # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
  config.omniauth :google_oauth2,
    Rails.application.credentials.dig(Rails.env.to_sym, :google_client_id),
    Rails.application.credentials.dig(Rails.env.to_sym, :google_client_secret)

  config.omniauth :facebook,
    Rails.application.credentials.dig(Rails.env.to_sym, :facebook_key),
    Rails.application.credentials.dig(Rails.env.to_sym, :facebook_secret),
    scope: "email", info_fields: 'name,email,first_name,last_name'

This is how my OmniAuth initializer looks like

# omniauth.rb
OmniAuth.config.logger = Rails.logger

OmniAuth.config.allowed_request_methods = [:post]

Note: I know right now RescueAuthTokenFromOmniauth is before the Omniauth::Strategies middleware, so my feature works. But I want to explicitly set it with insert_before so that it's clear that it should go before the Omniauth::Strategies middleware.

2

There are 2 best solutions below

2
On

I agree with Lam Phan's approach. If you did want to keep the initializer in a separate initializer file, make sure to reference the dependency initializers with something like require_relative './omniauth.rb', otherwise it will be sensitive to the alphabetical ordering of the filenames.

4
On

you can do that when omniauth initialize

# config/initializers/omniauth.rb
require Rails.root.join("lib/rescue_auth_token_from_omniauth")

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET']
end

Rails.application.config.middleware.insert_before(OmniAuth::Builder, RescueAuthTokenFromOmniauth)

note that if use above code, you should not add your middleware on application.rb otherwise it'll duplicate.