Search code examples
ruby-on-railsauthenticationcurldevise

Hit web endpoints behind Devise authentication with non-browser request in a Rails app


How can I request pages in a Rails app which are behind Devise authentication?

This SO post from 5 years ago suggests hitting /sign_in and then using the returned access_token in future requests.

But when I make a request, no access_token is returned.

curl -XPOST -v -H 'Content-Type: application/json' http://localhost:3000/users/sign_in -d '{"email": "[email protected]", "password": "12345678" }'

Response:

The response is this, followed by the html for the page that is normally then returned.

* upload completely sent off: 56 out of 56 bytes
< HTTP/1.1 200 OK
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< X-Content-Type-Options: nosniff
< Content-Type: text/html; charset=utf-8
< ETag: W/"2477...80"
< Cache-Control: max-age=0, private, must-revalidate
< X-Request-Id: 2351...ad
< X-Runtime: 0.785395
< Transfer-Encoding: chunked

But I have no tokens with which to make further requests.

Is what I'm trying possible with Ruby on Rails in 2020?

Or is there a way to hack devise to make it work?


UPDATE:

It's the site of my employer. Can't share unfortunately.

They want a way to test that logic behind newly added routes works, without needing to login and click through the UI.


UPDATE

I'm ok hitting the endpoints with curl, Postman, (anything) etc.


Solution

  • One approach is scraping with capybara. You haven't said why you tried curl, just that 'your employer wants to test logic behind newly added routes'. In that case you want to study response. Capybara is designed for that purpose.

    Ofc you should test logic with unit test, but I'm not here to judge the reasoning.

    my_scraper.rb

    require 'capybara/dsl'
    require 'webdrivers'
    
    # Google chrome stable must be installed locally:
    # https://linuxconfig.org/google-chrome-web-browser-installation-on-debian-9-stretch-linux
    
    class MyScraper
      include Capybara::DSL
    
      attr_reader :domain, :email, :password
      def initialize(email:, password:, domain: "http://localhost:3000")
        @domain = domain
        @email = email
        @password = password
        register_chrome_driver
        Capybara.default_driver = Capybara.javascript_driver = :headless_chrome
        Capybara.default_max_wait_time = 60
        log_in
      end
    
      def log_in
        visit "#{domain}/users/sign_in"
        fill_in 'Email', with: email
        fill_in 'Password', with: password
        click_button 'Logg inn'
      end
    
      def test_urls
        visit "#{domain}/some/url"
        #check results. See the documentation
        visit "#{domain}/some/url2"
        visit "#{domain}/another/path"
        # ...
      end
    
      # Source: https://gist.github.com/bbonamin/4b01be9ed5dd1bdaf909462ff4fdca95
      def register_chrome_driver
        options = ::Selenium::WebDriver::Chrome::Options.new
        options.add_argument('--headless')
        options.add_argument('--disable-gpu')
        options.add_argument('--window-size=1920,1080')
        Capybara.register_driver(:headless_chrome) do |app|
          Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
        end
      end
    end
    

    run it

    $ irb
    load 'my_scraper.rb'
    MyScraper.new(email: "[email protected]", password: "12345678").test_urls