9

I am trying to login to a website and redirect to a secured page from a rails action. My code looks something like this.

 def redirect_to_external 
   agent = Mechanize.new
   page = agent.get('http://example.com/home.asp')
   login_form = page.form_with(:name => "loginForm")
   login_form.login = 'username'
   login_form.password = 'password'
   agent.submit(login_form)

   #cookies = agent.cookie_jar.store.map {|i|  i} #need to store the cookie with a specific in browser
   redirect_to('http://example.com/admin.asp') #page behind password protection
 end

Login is successful in the background, but actual redirection to the admin page is asking again for authentication in the browser, as the session cookie is not stored in the browser. Tried storing the cookies from cookie_jar, but couldn't find the exact way to do that. Can someone help me in this?

sawa
  • 165,429
  • 45
  • 277
  • 381
Vijendra
  • 338
  • 3
  • 11
  • Make agent an instance var, or pass it to redirect_to. It has the cookie, there's no need to store it. – pguardiario Sep 04 '13 at 00:31
  • Thanks. But making @agent = Mechanize.new didn't work. Also, redirect_to is not taking agent as parameter. Is it possible to show an example for that please? – Vijendra Sep 04 '13 at 07:45
  • I don't know how to help you without being condescending. Just think about why you shouldn't be instantiating the Mechanize object as a local variable of your method (hint - it won't stick around for long.) – pguardiario Sep 04 '13 at 08:55
  • I have already tried making agent as instance variable, but without success. The problem is, it is not adding the session cookie to the browser (client side). So, once I redirect to the third party website, my control will be lost. – Vijendra Sep 05 '13 at 11:56

2 Answers2

10

I struggled with this one for a long time! The other answers on StackOverflow didn't exactly address how to cache the cookies and retrieve them, so I'm combining my experience with the other answers I've read, into one solution here.

(See:

Rails 3 - Log into another site and keep cookie in session

Store login session cookie in browser using ruby mechanize

Get Mechanize to handle cookies from an arbitrary POST (to log into a website programmatically)

keep mechanize page over request boundaries

).

I used this solution to create a custom OAuth2.0 for a website that doesn't support the real OAuth2.0.

  1. The user gave me credentials for the other site. I passed them onto the site via Mechanize in the create method of my Sessions controller and immediately destroyed them (I don't want to touch someone else's secure stuff as much as possible. That's why this all takes place under an SSL as well).

  2. That means my Mechanize instance with the cookies I need will get destroyed once I redirect anywhere else in my app. For example, in my app, I next redirected to the index method of my Sessions Controller. If you're new to rails you might think the same instance variables (like the mechanize agent with those cookies) would be available from create to index, but that's wrong, each redirect will destroy everything! (basically)

  3. So before the redirect, you need to store the cookies. The only way to store something like this between requests is via the cache, and the cache doesn't like to store the whole mechanize instance. Instead you need to store serialized cookies. The only trouble is, Mechanize doesn't have a method for outputting its cookies as a string, it can only save them as a file. What to do?

  4. StringIO to the rescue! You can essentially fake a file with StringIO. After all that talking, here's the code:

    @agent = Mechanize.new
    #handle the sign in stuff
    stringio = StringIO.new
    @agent.cookie_jar.save(stringio, session: true)
    cookies = stringio.string
    session[:login_cookies] = cookies
    

Note that I passed session: true to the save method of cookie_jar. Without that parameter, the cookie will only save non-session cookies.

  1. So now our session cache has a string that has all of the cookies from mechanize. To get them back after a redirect you'll need StringIO again:

    @agent = Mechanize.new
    cookies = session[:login_cookies]
    @agent.cookie_jar.load StringIO.new(cookies)
    
  2. And voila! Your new agent is ready to act just like the old one.

Community
  • 1
  • 1
KCE
  • 1,159
  • 8
  • 25
  • Was looking how to do the exact same thing with Mechanize (save cookies and load them without having to save and load actual files). Thank you so much! – uechan Jun 17 '16 at 14:35
  • A glowing post! Thanks you! – flunder Jul 05 '16 at 14:01
  • Great response, I do have 1 question though: I am only intermediately familiar with Ruby, so I am not sure what the syntax means on the line `session[:login_cookies] = cookies` is doing. Currently getting an error because it doesn't know what the `session` variable is. Thanks! – Tyler Russell Mar 02 '17 at 18:28
  • 1
    @TylerRussell `session` is a method that behaves like a hash. It should be available inside your controller. See this thread: http://stackoverflow.com/questions/24499378/where-does-session-come-from – KCE Mar 03 '17 at 19:58
  • @KCE Thanks for your response, I worked through it the other day. Regards. – Tyler Russell Mar 05 '17 at 19:14
2

Please refer following code.This create a code within a block this will maintain authentication,

 def redirect_to_external 
    @agent = Mechanize.new
    @agent.get('http://example.com/home.asp') do | home_page | # Need to pass the others requests into the block
      login_form = home_page.form_with(:name => "loginForm")
      login_form.login = 'username'
      login_form.password = 'password'
      @agent.submit(login_form)
      #cookies = agent.cookie_jar.store.map {|i|  i} #need to store the cookie with a specific in browser
      @agent.get('http://example.com/admin.asp') #page behind password protection
   end  
 end
Ronan Louarn
  • 472
  • 1
  • 6
  • 27
Babasaheb Gosavi
  • 7,735
  • 1
  • 14
  • 14