Problem with csrf and omniauth with Hanami 2

I’m working on a small Hanami 2 app where I want to the users to be able to create there account with various social logins. I’m using omniauth for the integration. For this I have configured Hanami to use sessions. I’ve configured omniauth the way it should work but whenever I am trying to login with the developer strategy I’m getting a OmniAuth::AuthenticityError.
It appears to me that this happens because Omniauth is expecting a different csrf token than the one I am putting inside my form. I retrieve the csrf value like this:
request.session["csrf"] (I also tried to access the “_csrf_token” key but this doesn’t help either.
What am I missing? How can I make omniauth pass the csrf test?

Hi, take a look at this thread: Peter Solnica: "@BobbyMcWho hey there, may I have an omniauth que…" - Ruby.social

Folks, I ran into this problem myself and found a workaround.

Two next steps:

  1. Share the short term solution (once back home)
  2. Design a solution in the framework (if possible)

Right, so we all stumbled upon the same issue. Like @kukicola wrote, there’s a workaround but my understanding is that what it actually does, is disabling CSRF altogether because setting an empty action as the validation handler just calls it, and it always returns a truthy value soooo, that’s not what we want. I don’t know enough about omniauth itself to propose any solution yet though.

@solnic It’s not disabling it. Hanami Actions have CSRF validations enabled by default: https://github.com/hanami/controller/blob/main/lib/hanami/action/csrf_protection.rb#L95
So it’ll still validate the token but using Hanami implementation (instead of the one from RackProtection). If I modify the token before sending request I can see Hanami::Action::InvalidCSRFTokenError.

1 Like

Here’s my working solution:

# config/app.rb
require "omniauth"
require "rack/csrf"

OmniAuth::AuthenticityTokenProtection.default_options(key: "csrf.token", authenticity_param: "_csrf")

module MyApp
  class App < Hanami::App
    config.actions.sessions = :cookie, {
      key: "_my_app.session",
      secret: settings.session_secret,
      expire_after: 60 * 60 * 24 * 365 # 1 year
    }
    # ...
  end
end
# app/actions/sessions/new.rb
# frozen_string_literal: true

module Hashtag
  module Actions
    module Sessions
      class New < Hashtag::Action
        def handle(req, res)
          # TODO: shouldn't `request` be exposed by default?
          res[:request] = req
        end
      end
    end
  end
end
# app/views/sessions/new.rb
# frozen_string_literal: true

require "rack/csrf"

module Hashtag
  module Views
    module Sessions
      class New < Hashtag::View
        # TODO: shouldn't `request` be exposed by default?
        expose :request
        expose :csrf_tag do |request|
          Rack::Csrf.tag(request.env)
        end
      end
    end
  end
end
# app/templates/sessions/new.html.slim
form action="/auth/developer" method="post"
  == csrf_tag
  button type="submit" Login with Developer

Thanks, that worked for me too!

As a sidenode: I tried the solution I found in the mastodon thread before the one @jodosha mentioned. This didn’t work + it was more stuff to do anyways…

1 Like