Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Rails 3.1rc1 streaming breaks AuthLogic session #262

Open
subelsky opened this Issue · 24 comments

14 participants

Mike Subelsky Anthony Eden Reinier de Lange David Y. Zhang Sam Woodard Scotty Nelson Peter Nixey Tristan Koch Paul Volpato Mason Andy Ferra Tim McAllister Joost Baaij Bill Kirtley
Mike Subelsky

I ran into this problem trying to use Authlogic with Rails 3.1rc1; looks like a problem on the Authlogic side with how cookies are handled;

rails/rails#1452

I'm going to dig around but wanted to post this to get on the radar..

Anthony Eden

I've put together a simple example of this error in practice with Cucumber. https://github.com/aeden/rails31_authlogic

Just run cucumber to see the error.

Reinier de Lange

ok, so this seems to be an authlogic bug. Is someone able to fix this or should I move a different authentication solution?

Mike Subelsky

it's not an authlogic bug - it affects other frameworks because they all do stuff with cookies. If you look at the Rails ticket you'll see people complaining about Devise

Reinier de Lange

As I don't have the time to wait for someone to eventually find a solution, I stripped authlogic out and went with using has_secure_password instead.. seems like it contains some 'bad' code anyway according to the other ticket.

David Y. Zhang

I'm hoping Authlogic will work perfectly with Rails 3.1rc soon... for now, though, it's apparent that the problem is on Rails 3.1's side?

Mike Subelsky

That Rails ticket states this is an authlogic bug: "It is storing an instance of the controller in a thread variables and modifying the cookies after the response is sent back to the client.

You can clearly see in the backtrace that the error comes from a model callback. This is nasty. Storing the controller in threads variables so it can be accessible from the model is one of the worst ways to violate MVC."

that's sort of core to how Authlogic works, no? Is there a different where to share this state?

David Y. Zhang

Are you saying that Rails 3.1 users shouldn't use Authlogic? :/ I like Authlogic...

Anthony Eden

No, I think he's saying that it may take a little bit of effort to figure out a way that doesn't break in Rails 3.1.

David Y. Zhang

I'm wondering about the timeframe, then. For new applications in Rails 3.1, what is recommended for user authentication if this problem won't be resolved anytime soon?
When do we think this issue could be resolved?

David Y. Zhang

In a new Rails 3.1.0rc4 app, I finally encountered this error in a cucumber test.
Interestingly, I did not encounter it in my first feature, in which I call this step definition:

Given /^I am the registered (.+) "(.+)"$/ do |role, login|
  @user = User.create!(
    :role => role,
    :login => login,
    :password => "password",
    :password_confirmation => "password",
    :email => "johndoe@somewhere.com"
  )
end

I encounter the issue when I run the broad command "cucumber" or "rake cucumber" - i.e. when all my features are run. The feature that first calls this step is fine, but the second feature that calls this step yields the "Cannot modify cookies because it was closed. This means it was already streamed back to the client or converted to HTTP headers. (ActionDispatch::ClosedError)." If I run one feature at a time (with cucumber features/featureX.feature), this step works fine... so individually, all good; together, not all.

Interested in knowing when the fix could be made.

Sam Woodard

Can this be fixed soon?

Scotty Nelson

I think I found the cause ...

It has to do with AuthLogic's automatic session maintenance. When you create a model that acts_as_authentic (User.create), by default it will create a new UserSession instance for you, or, in other words, log the user in.

If you create a User outside of a controller request, like when you factory a User to login with, that's when you get the ActionDispatch::ClosedError, not when you post to the controller.

If you disable automatic session maintenance, you don't get the error. Here's how you do that:

class User < ActiveRecord::Base
  acts_as_authentic do |c|
    c.maintain_sessions = false
  end
end

But, disabling automatic session maintenance means you'll need to add some code anywhere you expected a User.save or User.create to log in automatically. For me, it just meant adding this one line after a successful User.save wherever login was expected:

if @user.save
  UserSession.create @user
end

If you don't want to change your code, just do this whenever you need to factory a User account in your tests:

# ActiveRecord
user = User.new(attributes)
user.save_without_session_maintenance

# FactoryGirl
user = Factory.build(:user)
user.save_without_session_maintenance

There is one other way I did try that worked as a temporary hack, but I really wouldn't recommended it because I'm sure it would cause other problems. I stubbed out ActionDispatch::Cookies::CookieJar to never think it was closed, in an initializer:

# DON'T USE THIS

if Rails.env.test?
  module ActionDispatch
    class Cookies
      class CookieJar
        def closed?
          return false
        end
      end
    end
  end
end

I'm not sure what the approach will be for a long term fix, but I suspect it would involve checking if the User create or save is happening within a controller request, and if its not, don't try to do any session maintenance. I personally wouldn't mind if it became an opt-in instead of being default, perhaps by passing an attribute :login => true or adding a save_and_login or create_and_login, etc.

David Y. Zhang

@snelson: Great info. It looks like I'll be using Factory.build(:user) and save_without_session_maintenance. The other options look great too.
Truly, truly appreciated.

Scotty Nelson

@dmonopoly: No problem man, it was driving me nuts not being able to run my acceptance specs.

Peter Nixey

@snelson - thank you, that fix worked straight off the bat for me too. Much appreciated

Tristan Koch

@snelson, saving without session maintenance did the trick. Many thanks! Maybe one should a notice to the README?

Tristan Koch

Unfortunately I ran into another problem. Turns out I need an existing session for a couple of tests. Reluctantly applied the monkey patch.

Paul Volpato

Added @snelson 's User patch to your support folder (for cucumber) and you'll solve this issue. (Works a charm for me).

Mason

@snelson's the c.maintain_sessions = false modification was the most straightforward way to solve this issue for me (thanks!). I agree with @trkoch that this should probably be mentioned in the README.

Andy Ferra

If you like, you can also alter the configuration in your spec setup with:

User.acts_as_authentic_config[:maintain_sessions] = false

I find this preferable as opposed to re-opening the class.

Tim McAllister

Here's what we did in the user model to address.

class User < ActiveRecord::Base
  acts_as_authentic do |auth|
    auth.maintain_sessions = false   if Rails.env == "test" # authlogic/issues/262
  end
 end
Joost Baaij

The fix mentioned by @andyferra works and I prefer it too.

As far as I am concerned, the automatic session maintenance is removed from Authlogic completely. It's not how Authlogic is documented anyway, right? The docs always mention creating sessions explicitly, the fact this hook is there surprised me to begin with.

Sam Woodard
Sonny Michaud sonnym referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
Sonny Michaud sonnym referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
Sonny Michaud sonnym referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
Sonny Michaud sonnym referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
Sonny Michaud sonnym referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
Sonny Michaud sonnym referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
Sonny Michaud sonnym referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
Sonny Michaud sonnym referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
Sonny Michaud sonnym referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
Sonny Michaud sonnym referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
Sonny Michaud sonnym referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
Sonny Michaud sonnym referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
Sonny Michaud sonnym referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
Bill Kirtley

This is a dup of #52, no?

Craig Harman charman referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
Vlad v-fedorov referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.