55

After loading the Rails console, how should I sign in a user?

Devise provides a test helper which can be used in tests and I've tried to use in console:

>> include Devise::TestHelpers
>> helper.sign_in(User.first)

But I get:

NoMethodError: undefined method `env' for nil:NilClass

Anyway, I would like to use the real devise helper and not this test helper. Is there any way to achieve this?

Christian
  • 1,872
  • 1
  • 14
  • 14

5 Answers5

87

Here's one way I was able to do it:

>> ApplicationController.allow_forgery_protection = false
>> app.post('/sign_in', {"user"=>{"login"=>"login", "password"=>"password"}})

Then you can do:

 >> app.get '/some_other_path_that_only_works_if_logged_in'
 >> pp app.response.body
Brian Deterling
  • 13,556
  • 4
  • 55
  • 59
  • 2
    With modern Devise you will have to use `email` instead of `login`: `app.post('/sign_in', {"user"=>{"email"=>"login", "password"=>"password"}})` – Xavier Jul 23 '16 at 23:44
  • 2
    How is "app" initialized in this context? – Nona Aug 18 '16 at 22:10
  • app is initialized when you start the console. You don't have to do anything extra. – Brian Deterling Aug 20 '16 at 02:55
  • I had to use `app.post('/users/sign_in', {params: {"email"=>"[FILTERED]", "password"=>"[FILTERED]"}})` but otherwise works great ty! That bit about the `forgery_protection` would have held me up all day! – Mirv - Matt Mar 05 '18 at 16:48
16

Here is another example which uses the csrf token, authenticates the user, and makes a POST/GET request.

# get csrf token
app.get  '/users/sign_in'
csrf_token = app.session[:_csrf_token]

# log in
app.post('/users/sign_in', {"authenticity_token"=>csrf_token, "user"=>{"email"=>"foo", "password"=>"bar"}})

# get new csrf token, as auth user
app.get ''
csrf_token = app.session[:_csrf_token]

# make a POST request
app.post '/some_request.json', {"some_value"=>"wee", "authenticity_token"=>csrf_token}

# make a GET request
app.get '/some_other_request.json'
Eric London
  • 351
  • 2
  • 5
3

You can add an action inside one of your controller, and use the technique explained here.

class MyController < ApplicationController
  # POST /my_controller/become {'email': 'test@example.com'}
  def become
    raise 'not in development environment' unless Rails.env == 'development'
    sign_in User.find_by_email(params[:email])
  end
end
  • This doesn't really answer the question, since the question is about *signing iin from the console*, and not signing in as another user from the web app. – Martin Tournoij Jun 17 '15 at 14:16
  • 1
    But its still helpful, you can call this method from console also. I came searching through google here and yes above answers are nice and working but this one is helpful for me too. So don't just keep giving negative votes to anyone like this. – user1735921 Sep 09 '17 at 06:50
  • is there any way to use sign_in on the console? since i need to generate a token access but this will happen after the login – vinicius gati May 16 '18 at 15:50
3

Important to note that since Rails 5, the params parsing has been changed to take only one argument and params (instead of params being a second argument.

ActionDispatch::ParamsParser is deprecated and was removed from the middleware stack. To configure the parameter parsers use ActionDispatch::Request.parameter_parsers=

So these earlier examples will no longer work in modern Rails without patching your middleware. To make them work, simply include the arguments in the POST params like:

app.post('/users/sign_in', {"user"=>{"email"=>"me@mail.com", "password"=>"mycoolpassword"}}) <-- No longer valid

app.post('/users/sign_in', params: {"user"=>{"email"=>"me@mail.com", "password"=>"mycoolpassword"}}) <-- valid

0

There is a better way to do that. Add this code to the config/application.rb

# config/pplication.rb

  class Application < Rails::Application
    # ...
    console do
      class ActionDispatch::Integration::Session
        def host; 'localhost'; end
      end

      class ::BackdoorController < Devise::SessionsController
        def comeonin
          sign_in $current_user
          render json: {}
        end
      end
      Rails.application.routes.disable_clear_and_finalize = true
      Rails.application.routes.draw do
        devise_scope(:user) { get '/backdoor', to: "backdoor#comeonin" }
      end
    end
    # ...
  end

Now you can log in on behalf of any user:

$current_user = User.last
app.get '/backdoor' # sign in
app.get '/profile' # any other request

In the case of HTTPS, one may need to use the full hostname:

app.get 'https://localhost/backdoor'
Anton Orel
  • 576
  • 7
  • 8