Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

#<UserSession: {:unauthorized_record=>"<protected>"}>

I'm trying to build a "just click on your name to login" system using auth_logic. My user model has an email and name field. To login, I simply do:

UserSession.create(@user, true)

Unfortunately that doesn't result in a session being created. Using a debugger I found this message:

#<UserSession: {:unauthorized_record=>"<protected>"}>

My user model just has one line:

acts_as_authentic

User session line has this, which I found somewhere. I'm not sure what it does and I've tried with and without:

class UserSession < Authlogic::Session::Base
  def to_key
     new_record? ? nil : [ self.send(self.class.primary_key) ]
  end
end

The database (I'm also not sure if that user_sessions table is needed):

create_table "sessions", :force => true do |t|
  t.string   "session_id", :null => false
  t.text     "data"
  t.datetime "created_at"
  t.datetime "updated_at"
end

add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at"

create_table "user_sessions", :force => true do |t|
  t.datetime "created_at"
  t.datetime "updated_at"
end

create_table "users", :force => true do |t|
  t.datetime "created_at"
  t.datetime "updated_at"
  t.string   "persistence_token"
  t.string   "email"
  t.string   "name"
end

I'm using Rails 3.0.9 and my Gemfile says (I tried both the normal and the Github authlogic gem):

gem 'rails', '3.0.9'
gem 'sqlite3'
gem "authlogic" #, :git => 'git://github.com/odorcicd/authlogic.git', :branch => 'rails3'

Here's the rest of the source code.

I had this problem a few days ago on a similar project and it "just went away" at some point. I just don't remember how.

Any ideas? This is driving me nuts...

like image 368
Sjors Provoost Avatar asked Jul 10 '11 03:07

Sjors Provoost


2 Answers

Possible solution... it looks like there are two versions of those helper method examples floating around. There is the one that we used:

@current_user = current_user_session && current_user_session.user

and then there is the newer version that is in some newer tutorials and examples:

@current_user = current_user_session && current_user_session.record

Apparently, when .user (or whatever the login field is) is called on the session object, it returns the unauthorized_record, whereas .record.user returns the appropriate data.

like image 196
jordanpg Avatar answered Nov 16 '22 13:11

jordanpg


I experienced this issue recently and was confused as well. I have figured out my code's specific problem so this is another potential solution.

tl;dr

I did not understand that #<UserSession: {:unauthorized_record=>"<protected>"}> is still valid user session, just an unauthed one that we created ourselves. You can confirm this by calling user on it and you should get whatever User instance you passed into UserSession.create.

Cause

The real issue is two fold. The current_user method was built with the assumption that the current user would not change during the lifetime of a request and I was calling current_user before creating one one to ensure I didn't have one already. It can be simplified to the following:

def current_user
  return @current_user if defined?(@current_user)

  @current_user = some_method_that_finds_a_proper_user
end

The key here is that my method for finding a user can return nil. When it does, it will define @current_user as nil and thus that cached value will always be returned on subsequent calls.

Solutions

Here's where it gets a bit harder as it really depends on what your code needs.

  1. If you do not need the current_user after signing a user model in with UserSession.create then you don't need to do anything but wait for the render or redirect. On your next request, your current_user will be properly set.

  2. If you do not need to check for an already logged in user, remove any calls to current_user before UserSession.create if able and your first call to current_user will work as expected.

  3. Ensure your method chain that produces current_user either does not use caching, does not cache nils, or change the guard for these cached values to determine if the value is truthy instead of if the instance variable is defined. The guard could be changed to:

    return @current_user if !@current_user.nil? # or @current_user.present? in Rails
    

    Or you can use Ruby's ||= operator and implicit return. My simplified example above could instead be:

    def current_user
      @current_user ||= some_method_that_finds_a_proper_user
    end
    

    or if it's not as simple

    def current_user
      @current_user ||= begin
        some_code
        doing_many
        things_here
      end
    end
    
  4. Or your problem could be that you're having trouble writing a test for this behavior. In that case I'd rather just put an expectation on the code like

    expect(UserSession).to receive(:create).with(user)
    
like image 24
Aaron Avatar answered Nov 16 '22 13:11

Aaron