SitePoint Sponsor

User Tag List

Results 1 to 9 of 9
  1. #1
    SitePoint Enthusiast
    Join Date
    Jun 2009
    Posts
    84
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Log Out won't work correctly

    Hey all,

    I had a fairly simple setup for authentication that was working but wanted to add a remember me radio button. Since adding the radion button, adding a column called Auth_Token to my DB and using it to store cookies I can't seem to get my log out button to actually log the user out.

    I keep receiving the following message:

    ArgumentError in SessionsController#destroy

    wrong number of arguments (0 for 1)


    My setup is as follows:

    Sessions Controller

    Code:
    class SessionsController < ApplicationController
    	def new
    	end
    	
    	def create
    		user = User.find_by_email(params[:email])
    		if user && user.authenticate(params[:password])
    			if params[:remember_me]
    			cookies.permanent[:auth_token]
    			else
    			cookies[:auth_token]
    			end
    			redirect_to root_url
    		else
    			flash.now.alert = "Invalid email or password!"
    			render "signup"
    		end
    	end
    	
    	def destroy
    		cookies.delete[:auth_token]
    		redirect_to root_url
    	end
    	
    end
    Application Controller:

    Code:
    class ApplicationController < ActionController::Base
      protect_from_forgery
      
      private
      
      def current_user
        @current_user ||= User.find_by_auth_token!(cookies[:auth_token]) if cookies[:auth_token]
        end
        
      helper_method :current_user
    end
    Relevant element of my pages#home page:

    Code:
    <div class="login">
    					<li><% if :current_user %>
      						<!-- user is logged in -->
      						<%= link_to "Logout", logout_path %>
    						<% else %>
      					<!-- user is not logged in -->
      						<%= link_to "Login", login_path %>
    						/
    						<%= link_to "Sign up", signup_path %>
    					<% end %></li>
    				</div>

    Routes File:

    Code:
    MadeByV2::Application.routes.draw do
    
        get "sessions/new"
        get "logout" => "sessions#destroy"
        
        controller :pages do
        get "home" => "pages#home"
        get "about" => "pages#about"
        get "contact" => "pages#contact"
        end
        
        controller :user do
        get "signup" => "user#new"
        end
        
        resources :users, :controller => 'user'
         
        controller :sessions do
          get "login" => "sessions#new"
          post "login" => "sessions#create"
          delete "logout" => "sessions#destroy"
        end
    
       root :to => "pages#home"
    
    end
    I can't seem to work out at all how to the log out button to work correctly and also ensure that in Pages#Home Logout is shown if a user is logged in and Log in/ Sign up is shown if a user isn't logged in.

    Any help people can offer really would be much appreciated.

    Thanks in Advance,
    Tom

  2. #2
    SitePoint Zealot
    Join Date
    Feb 2003
    Location
    Akron, OH, USA
    Posts
    106
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    Going to try and walk through the code you have posted here. I'm assuming this is the most recent version.


    Code Ruby:
    class SessionsController < ApplicationController
        def new
        end
     
        def create
            user = User.find_by_email(params[:email])
            if user && user.authenticate(params[:password])
                if params[:remember_me]
                cookies.permanent[:auth_token]
                else
                cookies[:auth_token]
                end
                redirect_to root_url
            else
                flash.now.alert = "Invalid email or password!"
                render "signup"
            end
        end
     
        def destroy
            cookies.delete[:auth_token]
            redirect_to root_url
        end
     
    end
    This looks fine, however, I don't see any code to generate your auth_token. Do you have something in your user model to do that? I would have a method that looks something like this:

    Code Ruby:
    def generate_token(column)
      begin
        self[column] = SecureRandom.urlsafe_base64
      end while User.exists?(column => self[column])
    end

    And then your sessions controller create method would look like this (only slightly different):

    Code Ruby:
        def create
            user = User.find_by_email(params[:email])
            if user && user.authenticate(params[:password])
                if params[:remember_me]
                cookies.permanent[:auth_token] = user.auth_token
                else
                cookies[:auth_token] = user.auth_token
                end
                redirect_to root_url
            else
                flash.now.alert = "Invalid email or password!"
                render "signup"
            end
        end


    Code Ruby:
    class ApplicationController < ActionController::Base
      protect_from_forgery
     
      private
     
      def current_user
        @current_user ||= User.find_by_auth_token!(cookies[:auth_token]) if cookies[:auth_token]
        end
     
      helper_method :current_user
    end

    I would rearrange this so that you call protect_from_forgery, register your helper methods, and then call private, then your actual helper methods. Hope that makes sense.


    Code Ruby:
    <div class="login">
      <li><% if :current_user %>
        <!-- user is logged in -->
       <%= link_to "Logout", logout_path %>
                            <% else %>
                          <!-- user is not logged in -->
                              <%= link_to "Login", login_path %>
                            /
                            <%= link_to "Sign up", signup_path %>
                        <% end %></li>
                    </div>

    This isn't going to work. On line two of this snippet is the problem. (The HTML here is also a little strange to me.) You are calling a symbol by using :current_user write it like this:

    Code Ruby:
    <div class="login">
     <% if current_user %>
       Hi, <%= user.email %>! <br />
       <%= link_to "Logout", logout_path %> <% else %> <%= link_to "Login", login_path %> or <%= link_to "Sign up", signup_path %>
    <% end %>
    </div>

    Your routes file looked fine to me. Though I didn't look really close at them. The error you were receiving was because you were trying to call a symbol as opposed to the actual current_user method.

    Good luck and continue to ask questions of you need further help!

  3. #3
    SitePoint Enthusiast
    Join Date
    Jun 2009
    Posts
    84
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi Scannon,

    Thanks so much for your reply. Unfortunately having gone through and updated my code with your suggestions I am still finding when I login and am forwarded back to my root_url that Login or Sign up is still being displayed. I can only assume it's not saving the session properly because having checked the Sqlite database the auth_token fields are definitely filling correctly when a user signs up.

    Is there something I can do to test whether the session controller is working correctly.

    My current setup is as follows:

    Pages#Home

    Code:
    <div class="login">
     					<% if current_user %>
       						Hi, <%= user.email %>! <br />
       					<%= link_to "Logout", logout_path %> 
    					<% else %> <%= link_to "Login", login_path %> or <%= link_to "Sign up", signup_path %>
    					<% end %>
    				</div>
    Sessions Controller

    Code:
    class SessionsController < ApplicationController
    	def new
    	end
    	
    	def create
    		user = User.find_by_email(params[:email])
    		if user && user.authenticate(params[:password])
    			if params[:remember_me]
    			cookies.permanent[:auth_token]
    			else
    			cookies[:auth_token]
    			end
    			redirect_to root_url
    		else
    			flash.now.alert = "Invalid email or password!"
    			render "signup"
    		end
    	end
    	
    	def destroy
    		cookies.delete[:auth_token]
    		redirect_to root_url
    	end
    	
    end
    User Model

    Code:
    class User < ActiveRecord::Base
    	has_secure_password
    	before_create { generate_token(:auth_token) }
    	
    	validates_presence_of :password, :on => :create
    	validates_presence_of :email
    	validates_length_of :email, :within => 6..50
      	validates_length_of :password, :within => 6..30
    	validates_uniqueness_of :email, :case_sensitive => false, :on => :create
    	validates_format_of :email,    :with => /^[A-Z0-9_.%-]+@([A-Z0-9_]+\.)+[A-Z]{2,4}$/i,
    									:message => "must be a valid e-mail address"
    
    def generate_token(column)
    	begin
    		self[column] = SecureRandom.urlsafe_base64
    	end while User.exists?(column => self[column])
    end
    
    end
    Application Controller

    Code:
    class ApplicationController < ActionController::Base
      protect_from_forgery
      
      private
      
      def current_user
        @current_user ||= User.find_by_auth_token!(cookies[:auth_token]) if cookies[:auth_token]
        end
        
      helper_method :current_user
    end
    This is really confusing me as I can't see anything wrong myself.

    Any help you can offer would be awesome

    Thanks,
    Tom

  4. #4
    SitePoint Zealot
    Join Date
    Feb 2003
    Location
    Akron, OH, USA
    Posts
    106
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    Alright a couple of things still wrong.

    First, your sessions controller CREATE method needs to be changed.

    This line
    Code Ruby:
    cookies.permanent[:auth_token]
    needs to be changed to this
    Code Ruby:
    cookies.permanent[:auth_token] = user.auth_token

    also this line...
    Code Ruby:
    cookies[:auth_token]
    needs to be changed to this
    Code Ruby:
    cookies[:auth_token] = user.auth_token

    (See my first post to copy and paste the create method code.)

    Second, your application controller should be ordered like this...

    Code Ruby:
     
    class ApplicationController < ActionController::Base
      protect_from_forgery
     
      helper_method :current_user
     
     
      private
     
      def current_user
        @current_user ||= User.find_by_auth_token!(cookies[:auth_token]) if cookies[:auth_token]
       end
     
     
    end

    Now for a quick explanation of what I think is happening here. The session isn't being created properly because of the two lines that I pointed out. Basically you aren't telling the cookie what it should contain. By saying cookies.permanent[:auth_token] = user.auth_token you are telling the cookie to save the current users auth token in the cookie. Without the equals the cookie contains nothing.

    As far as testing things that is beyond the scope of this post, but TDD (test driven development) and BDD (behaviour driven development) are VERY prevalent within the Ruby and Rails communities. There are literally tons of testing frameworks and ideas behind how it should be done. Do some googling and make decisions for yourself.

    Personally, if you are planning on doing any kind of extensive work with Rails you will eventually need to have a solid grasp of Ruby. I recommend you check out Chris Pines "Learn to Program, 2nd Ed." and Zed Shaw's "Learn Ruby the Hard Way." Both will help you with understanding Ruby and programming basics. Obviously, I don't know what kind of programming experience you have but these two resources will be invaluable to you if you are just starting out.

    Good luck, and let us know if this works!

  5. #5
    SitePoint Enthusiast
    Join Date
    Jun 2009
    Posts
    84
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hey again scannon,

    Thanks so much for all your help here. You will be pleased to hear that it is all working

    The only issue I am having now is that the cookies aren't being destroyed and the browser redirected to the root URL when hitting Logout.

    Instead I am getting the following error:


    Code:
    wrong number of arguments (0 for 1)
    Rails.root: C:/Sites/Made_By_v2
    
    Application Trace | Framework Trace | Full Trace
    app/controllers/sessions_controller.rb:21:in `destroy'
    Would this be to do with the fact it can't find the :auth_token?

    Session controller is as follows:

    Code:
    class SessionsController < ApplicationController
    	def new
    	end
    	
    	def create
    		user = User.find_by_email(params[:email])
    		if user && user.authenticate(params[:password])
    			if params[:remember_me]
    			cookies.permanent[:auth_token] = user.auth_token
    			else
    			cookies[:auth_token] = user.auth_token
    			end
    			redirect_to root_url
    		else
    			flash.now.alert = "Invalid email or password!"
    			render "signup"
    		end
    	end
    	
    	def destroy
    		cookies.delete[:auth_token]
    		redirect_to root_url
    	end
    	
    end
    Thanks again so much for your help

    Tom

  6. #6
    SitePoint Zealot
    Join Date
    Feb 2003
    Location
    Akron, OH, USA
    Posts
    106
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    The following line in your destroy method needs to be changed:

    Code Ruby:
    cookies.delete[:auth_token]

    to

    Code Ruby:
    cookies.delete(:auth_token)

    It should also then be able to take you to the root url, which in your case, is pages#home.

  7. #7
    SitePoint Enthusiast
    Join Date
    Jun 2009
    Posts
    84
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Amazing! It's finally fully working logging in and out and holding onto the cookies, thank you so much for all your help!

    Just for those that find this post and are interested the final solution from scannon was as follows.

    Sessions Controller

    Code:
    class SessionsController < ApplicationController
    	def new
    	end
    	
    	def create
    		user = User.find_by_email(params[:email])
    		if user && user.authenticate(params[:password])
    			if params[:remember_me]
    			cookies.permanent[:auth_token] = user.auth_token
    			else
    			cookies[:auth_token] = user.auth_token
    			end
    			redirect_to root_url
    		else
    			flash.now.alert = "Invalid email or password!"
    			render "signup"
    		end
    	end
    	
    	def destroy
    		cookies.delete(:auth_token)
    		redirect_to root_url
    	end
    	
    end
    Application controller

    Code:
    class ApplicationController < ActionController::Base
      protect_from_forgery
     
      helper_method :current_user
     
     
      private
     
      def current_user
        @current_user ||= User.find_by_auth_token!(cookies[:auth_token]) if cookies[:auth_token]
       end
     
     
    end
    User Model

    Code:
    class User < ActiveRecord::Base
    	has_secure_password
    	before_create { generate_token(:auth_token) }
    	
    	validates_presence_of :password, :on => :create
    	validates_presence_of :email
    	validates_length_of :email, :within => 6..50
      	validates_length_of :password, :within => 6..30
    	validates_uniqueness_of :email, :case_sensitive => false, :on => :create
    	validates_format_of :email,    :with => /^[A-Z0-9_.%-]+@([A-Z0-9_]+\.)+[A-Z]{2,4}$/i,
    									:message => "must be a valid e-mail address"
    
    def generate_token(column)
    	begin
    		self[column] = SecureRandom.urlsafe_base64
    	end while User.exists?(column => self[column])
    end
    
    end
    User new

    Code:
    <div id="signup-area">
    	<h1>Sign Up</h1>
    <div class="signup-fields">
    <%= form_for @user do |f| %>
    
    	<% if @user.errors.any? %>
    		<div class="error_messages">
    		 <h2>Form is invalid</h2>
    			<ul>
    				<% for message in @user.errors.full_messages %>
    					<li><%= message %></li>
    				<% end %>
    			</ul>
    		</div>
    	<% end %>	
    	<div class = "field">
    		<%= f.label :email %>
    		<%= f.text_field :email %>
    	</div>
    	
    	<div class = "field">
    		<%= f.label :password %>
    		<%= f.password_field :password %>
    	</div>
    	
    	<div class = "field">
    		<%= f.label :password_confirmation %>
    		<%= f.password_field :password_confirmation %>
    	</div>
    	
    	<div class="actions"><%= f.submit "Submit" %></div>
    </div>
    <% end %>
    </div>
    Sessions New

    Code:
    <div id="login-area">
    <div id="title">
    	<h1>Log In</h1>
    </div>
    
    <%= form_tag login_path do %>
    <div class="login-fields">
    	<div class="field">
    			<%= label_tag :email %>
    		
    			<%= text_field_tag :email, params[:email] %>
    	</div>
    	
    	<div class ="field">
    		<%= label_tag :password %>
    		<%= password_field_tag :password %>
    	</div>
    	
    	<div class="actions"><%= submit_tag "Log in" %></div>
    </div>
    <%end%>
    <div class = "field">
    	<%= label_tag :remember_me %>
    	<%= check_box_tag :remember_me, 1, params[:remember_me] %>
    </div>
    <div class="Not-registered">
    	<p>Not yet registered? <%= link_to "Sign up here", signup_path %></p>
    </div>	
    </div>
    Pages#Home login and logout element

    Code:
    <div class="login">
     					<% if current_user %>
       						Hi, <%= current_user.email %>! <br />
       					<%= link_to "Logout", logout_path %> 
    					<% else %> <%= link_to "Login", login_path %> or <%= link_to "Sign up", signup_path %>
    					<% end %>
    				</div>

    Thanks again scannon for some amazing help!

    Tom

  8. #8
    SitePoint Member
    Join Date
    Mar 2012
    Posts
    1
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The only issue I am having now is that the cookies aren't being destroyed and the browser redirected to the root URL when hitting Logout.

  9. #9
    SitePoint Zealot
    Join Date
    Feb 2003
    Location
    Akron, OH, USA
    Posts
    106
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    When you hit "Log Out" the session is destroyed and the cookie for the "Remember Me" feature is deleted. Meaning that when you revisit the site, you should have to supply your log in credentials. What, exactly, is the unexpected behavior that you are experiencing?

    With the redirect, it is as simple as changing the following line in the Sessions Controller destroy method:

    Code Ruby:
    redirect_to root_url

    Just change the root_url to whatever path name you want.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •