Creating a Login System


You want your application to support a login system based on user accounts. Users will log in with a unique username and password, as in most commercial and community web sites.


Create a users table that contains nonnull username and password fields. The SQL to create this table should look something like this MySQL example:

	use mywebapp_development;
	 username VARCHAR(255) NOT NULL,
	 password VARCHAR(40) NOT NULL,

Enter the main directory of the application and generate a User model corresponding to this table:

	$ ./script/generate model User
	 exists app/models/
	 exists test/unit/
	 exists test/fixtures/
 create app/models/user.rb
	 create test/unit/user_test.rb
	 create test/fixtures/users.yml

Open the generated file app/models/user.rb and edit it to look like this:

	class User < ActiveRecord::Base
	 validates_uniqueness_of :username
	 validates_confirmation_of :password, :on => :create
	 validates_length_of :password, :within => 5..40

	 # If a user matching the credentials is found, returns the User object.
	 # If no matching user is found, returns nil.
	 def self.authenticate(user_info)

Now youve got a User class that represents a user account, and a way of validating a username and password against the one stored in the database.


The simple User model given in the Solution defines a method for doing username/password validation, and some validation rules that impose limitations on the data to be stored in the users table. These validation rules tell User to:

  • Ensure that each username is unique. No two users can have the same username.
  • Ensure that, whenever the password attribute is being set, the password_confirmation attribute has the same value.
  • Ensure that the value of the password attribute is between 5 and 40 characters long.

Now lets create a controller for this model. Itll have a login action to display the login page, a process_login action to check the username and password, and a logout action to deauthenticate a logged-in session. So that the user accounts will actually do something, well also add a my_account action:

	$ ./script/generate controller user login process_login logout my_account
	 exists app/controllers/
	 exists app/helpers/
	 create app/views/user
	 exists test/functional/
	 create app/controllers/user_controller.rb
	 create test/functional/user_controller_test.rb
	 create app/helpers/user_helper.rb
	 create app/views/user/ 
	 create app/views/user/process_login.rhtml
	 create app/views/user/logout.rhtml

Edit app/controllers/user_controller.rb to define the three actions:

	class UserController < ApplicationController
	 def login
	 @user =
	 @user.username = params[:username]
	 def process_login
	 if user = User.authenticate(params[:user])
	 session[:id] = # Remember the users id during this session
	 redirect_to session[:return_to] || /
	 flash[:error] = Invalid login.
	 redirect_to :action => login, :username => params[:user][:username]

	 def logout
	 flash[:message] = Logged out.
	 redirect_to :action => login

	 def my_account

Now for the views. The process_login and logout actions just redirect to other actions, so we only need views for login and my_account. Heres a view for login:

	<% if @flash[:message] %><%= @flash[:message] %><% end %>
	<% if @flash[:error] %><%= @flash[:error] %><% end %>

	<%= form_tag :action => process_login %>
	 Username: <%= text_field "user", "username" %>

	 Password: <%= password_field "user", "password" %>

	 <%= submit_tag %>
	<%= end_form_tag %>

The @flash instance variable is a hashlike object used to store temporary messages for the user between actions. When the logout action sets flash[:message] and redirects to login, or process_login sets flash[:error] and redirects to login, the results are available to the view of the login action. Then they get cleared out.

Heres a very simple view for my_account:


Account Info

Your username is <%= User.find(session[:id]).username %>

Create an entry in the users table, start the server, and youll find that you can log in from http://localhost:3000/user/login , and view your account information from http://localhost:3000/user/my_account.

	$ ./script/runner User.create(:username => "johndoe", 
	 :password => "changeme")

Theres just one missing piece: you can visit the my_account action even if you e not logged in. We don have a way to close off an action to unauthenticated users. Add the following code to your app/controllers/application.rb file:

	class ApplicationController < ActionController::Base
	 before_filter :set_user

	 def set_user
	 @user = User.find(session[:id]) if @user.nil? && session[:id]

	 def login_required
	 return true if @user
	 return false

	 def access_denied
	 session[:return_to] = request.request_uri
	 flash[:error] = Oops. You need to login before you can view that page.
	 redirect_to :controller => user, :action => login

This code defines two filters, set_user and login_required, which you can apply to actions or controllers. The set_user filter is run on every action (because we pass it into before_filter in ApplicationController, the superclass of all our controllers). The set_user method sets the instance variable @user if the user is logged in. Now information about the logged-in user (if any) is available throughout your application. Action methods and views can use this instance variable like any other. This is useful even for actions that don require login: for instance, your main layout view might display the name of the logged-in user (if any) on every page.

You can prohibit unauthenticated users from using a specific action or controller by passing the symbol for the login_required method into before_filter. Heres how to protect the my_account action defined in app/controllers/user_controller.rb:

	class UserController < ApplicationController
	 before_filter :login_required, :only => :my_account

Now if you try to use the my_account action without being logged in, youll be redirected to the login page.

See Also

  • Recipe 13.14, "Validating Data with ActiveRecord"
  • Recipe 15.6, "Integrating a Database with Your Rails Application"
  • Recipe 15.9, "Storing Hashed User Passwords in the Database"
  • Recipe 15.11, "Setting and Retrieving Session Information"
  • Rather than doing this work yourself, you can install the login_generator gem and use its login generator: it will give your application a User model and a controller that implements a password-based authentication system; see; also see for other generators (including the more sophisticated model_security_generator)



Date and Time



Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming


Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration

Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Similar book on Amazon © 2008-2017.
If you may any questions please contact us: