Posts tagged "devise"

After Devise was released, there were some misunderstandings about Devise roles and how to use it. And the best way to understand it is explaining which problem we wanted to solve when we designing Devise.

In most applications developed at Plataforma, we usually have two actors: one which represents the client who hired us and another which is the end-user, the audience of the developed app.

Before Devise, we used Authlogic or Clearance as authentication solutions. Authlogic does not say anything about controllers, so we usually had two models (Admin and User) and some controllers to handle sign in, password reset and so forth for each model, which required expressive effort to handle and maintain.

On the other hand, we had Clearance. Clearance deals completely with the User model, but we needed to work on the Admin model almost from scratch.

With Devise, we can have a full stack solution for both User and Admin. But that only works if User and Admin does not need to share a lot of responsibilities.

For instance, if you are working on a blogging system with different roles as “editor”, “author” and “contributor”, but they all share a lot of activities in common, as writing a post, handling such roles with Devise can add a great of complexity to your code, mainly because you will need to use Single Table Inheritance (STI) and/or polymorphic relationships very frequently.

To handle such cases, you can use any of the authorization libraries out there for Rails (remember that Devise is mainly an authentication tool).

Scoped authentication

On the same line, there were some requests to provide authentication by username, instead of e-mail, or providing a subdomain as scope for the authentication. You can now do that using Devise 0.5.1 or higher by just setting the authentication keys in your model:

class User < ActiveRecord::Base
 devise :all, :authentication_keys => [ :username, :subdomain ]
end

Now you user needs an username and subdomain to authenticate and their respective value should be sent as parameters when signing in. Such values are converted to conditions when retrieving the user from the database for authentication. For example, the following path with query string:

/users/sign_in?user[username]=josevalim&user[subdomain]=plataformatec

Is converted to the following query with ActiveRecord:

User.first(:conditions => { :username => "josevalim", :subdomain => "plataformatec" })

And only after retrieving the user we check for password validity. However, keep in mind that those conditions are used only when signing in. If an user is already authenticated, it will be retrieved from session, where such conditions are not used. In other words, you still need a filter in your controllers to verify that the user accessing a given subdomain, is really allowed to access that subdomain.

Such configuration should handle most of the cases, but if you still need more customization, you can overwrite three class methods exposed for exactly this purpose: User.authenticate, User.serialize_into_session and User.serialize_from_session.

Enjoy!

It has been a couple weeks since we first bloged about Devise. At that time, we released version 0.1 and now, after some great feedback, some enhancements and a few bugs fixes, we reached Devise 0.4. So, what changed since then?

I’m lazy, you’re lazy

Devise now comes with generators, so adding up authentication to your app is even easier and quicker to do. First, let’s install Devise if you haven’t yet:

gem sources -a http://gemcutter.org/
gem install devise

And let’s add it to your environment, all together with warden:

config.gem "warden", :version => "0.5.1"
config.gem "devise", :version => "0.4.1"

After setting the gem up, the first generator can be invoked:

script/generate devise_install

And it simply places an initializer at config/initializers/devise.rb. You can check there all devise configuration options, so the initializer fits well as documentation tool too. Some of the new things you can configure since 0.1 release is the :confirm_within period (the time the user can access the site even without confirming his account) and :remember_for period (the time the remember token is valid).

After we configured Devise, we are able create our models. And how hard can that be if we have a generator? So just do:

script/generate devise User

It’s going to create a model User, add map.devise_for :users in routes.rb and a migration file. And before we proceed, we just need to configure default_url_options for ActionMailer in config/environments/development.rb (the config below is for development, be sure to properly set them for test and production too):

config.action_mailer.default_url_options = { :host => 'localhost:3000' }

And have at least a route named root in our config/routes.rb (by default, devise will look for user_root_path, if none is defined, is uses root_path):

map.root :controller => "your_controller"

Now we just need to run migrations and we will be able to create our first user in the console:

User.create!(:email => "your@email.com", :password => "secret")

Now just start your web server and you will be able to sign your user in at /users/sign_in, request for another confirmation code to be sent, request a code to reset your password and so forth.

Just remember that Devise does not say anything about the sign up process, that’s why you have to create your users in console.

Customization

We also know that you want to customize your views, to use something like Formtastic instead of the default markup. Well, guess who is here to help you?

Generators! The command below will make a copy of all devise views to your application, including locale files, for flash messages configuration:

script/generate devise_views

Laziness for all!

We know that you are lazy and not just with ActiveRecord. You want to be lazy with Datamapper, Mongomapper, Couchrest… This is why we worked on making Devise a little bit more agnostic, all your ORM has to do is have an API similar to ActiveRecord (finder and callbacks mainly).

Besides, if you already travelled a bit on Warden world, you will see that there is a lot more than Devise, including some strategies for OAuth. So we are also working on making Devise compatible with such new strategies as well, that’s why you can already see a config.warden hook on the initializer.

Deprecations

Since things are getting really easy, we need to ask something back from you. Just watch out for to deprecations in Devise 0.4.0:

1) :authenticable is a typo, so we fixed that renaming it to :authenticatable. If you used Devise 0.3.x, you could see some deprecation warnings. However, in Devise 0.4.0 such warnings were removed. Tip: be sure to check your migration!

2) We had a notifier inside Devise called Notifier. We decided to follow Clearance convention and rename it to DeviseMailer. You will need to change your views from notifier to devise_mailer and your locale yml from notifier to mailer.

We hope you enjoy Devise as much as we do! And, as previously, we also have an example app in Github to help you get started too.

UPDATE: This post was an introduction to Devise and a couple of things changed since then. There is a more recent post which describes the same steps as below using generators and, for a more complete and always updated explanation, please check the README.

In Rails Summit Latin America 2009, we showed Devise in a lightning talk and today we are officially releasing it! Before we show you some code, we are going to explain what we want to achieve with Devise, starting with the most used authentication solution nowadays: Clearance and Authlogic.

Clearance

Clearance is a full stack authentication solution, implementing all Model, View and Controller layers using Rails Engines. It deals with account confirmation and password recovery. You just need to plug and play! However, you are required to use the model User and it does not allow you have add and/or customize different roles.

Authlogic

When comes to the Model, Authlogic is definitely the most complete solution out there. It handles several cryptography providers and many other goodies which are completely configurable. However, it’s not a full stack solution (it does not say how users should confirm their account or recover their password) and it has a little bit of controversy since it handles the session in a model. So here is the question, where the session could be handled then?

Here comes Warden!

Warden is a general rack authentication framework, developed by Daniel Neighman, which handles the session in a rack middleware. The main benefit from it is that you can share your authentication strategies with several apps, so if you are using Sinatra, Rails and some others middlewares at the same time, they all rely on the same rules!

Devise: strategies for authentication

After we fell in love with Warden and used it in some projects, we decided to create a full stack solution as Clearance, but flexible as Authlogic, on top of Warden. The solution is Devise, a Rails Engine which handles multiple roles, each one of them with different strategies. Devise currently comes with 5 strategies:

  • Authenticatable: responsible for encrypting password and validating authenticity of a user while signing in;
  • Confirmable: responsible for verifying whether an account is already confirmed to sign in, and to send emails with confirmation instructions;
  • Recoverable: takes care of reseting the user password and send reset instructions;
  • Rememberable: generates and clears a token to remember the user from a saved cookie;
  • Validatable: creates all needed validations for email and password. It’s totally optional, so you’re able to to customize validations by yourself.

The nice thing is: imagine that you are building an app which needs to handle invitations. You just need to create a Invitable strategy on Devise and never implement it again!

Show me the code!

In the README, you will find all the information you need to start using Devise in your projects, so here we are going to cover the main aspects of it. Let’s suppose you are creating an user model, which needs to be authenticated and recover his password. The first step is to create the columns using Devise migration helpers:

create_table :users do |t|
  # creates email, encrypted_password and password_salt
  t.authenticatable
 
  # creates reset_password_token
  t.recoverable
end

Then you need to declare inside your model which strategies you want to use:

class User < ActiveRecord::User
  # Authenticatable is always included
  devise :recoverable, :validatable
end

And create the routes:

ActionController::Routing::Routes.draw do |map|
  # Check for configuration params on README
  map.devise_for :users
end

The route will access your model and create only the routes for the strategies declared. That ensures that your user won’t access the confirmations controller inside Devise. Devise also adds a couple of helpers and filters to be used inside your controllers:

  # Inside your protected controller
  before_filter :authenticate_user!
 
  # Inside your controllers and views
  user_signed_in?
  current_user
  user_session

user_session is a hash scoped only to the user. So if you have two roles, they will have different session hashes and their data won’t conflict! This awesome feature come straights from Warden!

Devise also has I18n support and since it’s an engine, you can customize your views just by placing a copy of it in your application. A small application build as example is also available on Github!

What’s more to come?

We are planning to add several other strategies to Devise, including brute force protection, session timeouts and also other features, as generators. You can spy our TODO list whenever you want.

Our many thanks to

Carlos Antônio which worked on Devise and made it ready for prime time! Jonas Nicklas, which introduced us to Warden and Daniel Neighman for building and maintaining it!

We also want to thank Thoughtbot guys, which wrote several decisions and tips they took while developing Clearance which helped us while building Devise.

Finally, thanks to Fábio Akita for giving us the chance to release it at Rails Summit and Gregg Pollack for releasing Devise on Ruby 5!