Posts tagged "devise"

Last week I spoke at Silicon Valley Ruby Group about PlataformaTec’s open source tools, mainly Devise, Simple Form and Responders.

When talking about Devise, I’ve mentioned that, before creating Devise, we were alternating between using Authlogic or Clearance in our projects. However, we soon realized that we needed a solution that was as customizable as Authlogic (allowing us to choose behaviors and several configuration options) and as complete as Clearance (whole MVC stack). It is fun to remember this happened more than 2 years ago.

After the presentation, someone came to talk to me about Sorcery and said it would be nice if Devise provided the same kind of tooling, allowing someone to build their own controllers and views around Devise instead of using Devise built-in controllers and views. His proposal surprised me, because this approach is totally possible with Devise and it was one of our design goals since day one.

That said, we realized that we were probably not “advertising” the bare-bone, stripped-down aspect of Devise well enough. That’s why I am writing this blog post. Devise already makes it easy for you to customize your own views, using the generator rails g devise:views which copies the views to your application. But what if you want to roll out your own views AND controllers?

To show you how we can achieve that, let’s write some code! The first step is to create a Rails application:

rails new devise-only-model

Next, we will add Devise to the Gemfile:

gem "devise", "~> 1.4.6"

And run the installation generator:

bundle install && rails g devise:install

The installation generator is going to give you some extra instructions, so don’t forget to do that as well. Next, let’s generate our basic User model, but we will pass an extra parameter called --skip-routes:

rails g devise User --skip-routes

By passing this extra parameter, Devise is going to generate everything as usual, with a small difference on config/routes.rb:

devise_for :users, :skip => :all

This parameter tells Devise to not generate any route at all. You can check that by executing bundle exec rake routes. However, you may be wondering: why can’t we simply remove the devise_for call? If we remove the route, Devise wouldn’t actually know that you have added Devise configuration to the User model, as all models are lazy loaded. So we need the route to tell Devise it needs to setup the appropriate helpers for the user, like authenticate_user!.

With Devise configured, we are ready to create the controllers and views on our own. In this blog post, we are going to create the SessionsController as an example allowing us to sign in and sign out. First, let’s add our routes:

root :to => "sessions#new"
post "/users/sign_in"    => "sessions#create"
delete "/users/sign_out" => "sessions#destroy"

Our SessionsController at app/controllers/sessions_controller looks like:

class SessionsController < ApplicationController
  # For security purposes, Devise just authenticates an user
  # from the params hash if we explicitly allow it to. That's
  # why we need to call the before filter below.
  before_filter :allow_params_authentication!, :only => :create
 
  def new
    @user = User.new(params[:user])
  end
 
  def create
    # Since the authentication happens in the rack layer,
    # we need to tell Devise to call the action "sessions#new"
    # in case something goes bad. Feel free to change it.
    user = authenticate_user!(:recall => "sessions#new")
    flash[:notice] = "You are now signed in!"
    sign_in user
    redirect_to root_path
  end
 
  def destroy
    sign_out
    flash[:notice] = "You are now signed out!"
    redirect_to root_path
  end
end

The controller implementation is quite straightforward. With the controller in hands, we just need to generate the view for the new action at app/views/sessions/new.html.erb:

<% if user_signed_in? %>
  You are signed in as <%= current_user.email %>. <%= link_to "Sign out", users_sign_out_path, :method => :delete %>.
<% else %>
  <%= form_for @user, :url => users_sign_in_path do |f| %>
  <div><%= f.label :email %><br />
  <%= f.email_field :email %></div>
  <div><%= f.label :password %><br />
  <%= f.password_field :password %></div>
  <div><%= f.check_box :remember_me %> <%= f.label :remember_me %></div>
  <div><%= f.submit "Sign in" %></div>
  <% end %>
<% end %>

The view shows a message if the user is signed in, otherwise it shows a sign in form. Now we are almost ready to check if it works. First, we need to run the migrations:

bundle exec rake db:migrate

Remove the index page:

rm public/index.html

And create a user in the database so we can sign in. We can do that in rails console:

User.create!(:email => "jose@example.com", :password => "123456")

Now, start the server and you are ready to sign in and sign out. You can also block user access in any controller by calling authenticate_user! in a before filter. Just remember that, if you add the filter to your application controller, remember to skip the filter on the sessions controller, otherwise you won’t be able to sign in in the first place.

You can now freely proceed to implement the other controllers and views in your application. Keep in mind that if you have devise :recoverable in your model, all the related methods like User.send_reset_password_instructions will already be available in your model, so you can use them straight away to implement your own reset password feature. Since Devise use all those methods internally, if you have any questions about implementing your own reset password feature, you can always take a look at Devise own controllers for some help.

I hope this post can help you to roll out your own controllers if this is the kind of feature you expect from Devise. Also, if you are worried about the overhead of using Devise even if you are not using its controllers, there is no need to worry at all. Devise does the smart thing and only loads the controllers you are actually using. Also, it lazily loads all behaviors, so if you are not using recoverable, no code related to recoverable will be loaded at all.

It is important to keep in mind that Devise was built by us to be flexible and capable of handling different requirements from different clients, so it is PlataformaTec’s priority to have it as flexible as possible! So, what would you like to see in “bare-bone, stripped-down Devise” in order to better use it in your applications?

Note: Devise 1.1.6 broke compatibility with Rails versions prior to 3.0.4, this has been fixed on Devise 1.1.7.

Devise 1.1.6 has just been released and it follows Rails 3.0.4 release. Rails 3.0.4 changes how CSRF works and adds a new method called handle_unverified_request that should be properly overridden by authentication frameworks. Devise 1.1.6 implements this method and others small security fixes.

If you have updated to Rails 3.0.4, you must update to Devise 1.1.6. Those using Devise 1.2.rc should use master for awhile, another RC is coming soon. For more information, check out the CHANGELOG.

Devise 1.0.10 was also released with the same fixes for Rails 2.3.11.

This year is coming to an end and it was amazing for us at Plataforma Tecnologia. We are proud to share with you, faithful reader, our accomplishments in 2010, which weren’t few.

Open Source

The year has begun on fire. In February, José Valim made his way into the Rails Core team and has been doing a great work since then, including many contributions to the Rails 3 release.

We are also really proud with Devise. It got very mature, achieving the 1.0 version this February. Since then, Devise is becoming one of the best solutions for authentication in Rails and also one of the most watched repositories on GitHub.

In August, Devise 1.1 was released with Rails 3 support and a bunch of cool features. Recently, a lot of work is being done towards the 1.2 version, which includes full support to the awesome OmniAuth gem.

We also released SimpleForm this year. SimpleForm is our take on building forms in a simplified way lead by Carlos Antônio who recently did a great work on HTML 5 support.

Other gems were released, such as ShowFor, which is a DSL to simplify how you show your objects in views and also Responders, a collection of Rails 3 responders.

Book

Eariler this month, José Valim’s book entitled Crafting Rails Applications has been released by the highly acclaimed publisher Pragmatic Bookstore! The book covers internal aspects of Rails 3 and how you can bend it to your will. The reviews and buzz has been great so far, even though still in beta. Grab your copy now!

Events

This year was also very productive in terms of events. We’ve been to a lot of events, heck, George was even able to go to RailsConf, in Baltimore! Also, José Valim has spoken in various events, including Euruko 2010 (includes video), RubyConf Brazil 2010 (talk in portuguese) and OxenteRails 2010.

Carlos, Hugo and George did their share as well, speaking at various events, from smaller and user-group events to bigger ones such as OxenteRails, RS on Rails and QCon SP.

Company

The company itself is getting more mature and increasing. At January 2010, PlataformaTec was composed of 6 people, and up until December 2010, 5 other people joined the company (including myself)! We also have started playing with iOS development, something we believe to have a great future, expect future blog posts on the subject.

Great 2011!

Have a great New Year! We wish you all the best for year to come. We have high expectations for 2011, so stay tuned!

One of the great gems released in the past few months was OmniAuth. It is very easy to use, it works without tons of configurations (unless configuring XML files is your thing) and there are already plenty of resources about it on the internet.

However, it is not easy to do acceptance tests with Omniauth as it depends on external services to work. So what should we do? When I face a scenario like this, I split the acceptance test in two parts: one before the external service and one after the external service response.

Testing the first one is trivial: you only have to ensure there is an <a> tag with href equals to “/auth/facebook” (or “/auth/#{insert_your_provider_here}” if you use another one). We don’t test any of the redirects done by OmniAuth internals, because it is already tested in gem’s tests, so we go to the next step: testing the OmniAuth callback.

Testing OmniAuth callbacks is in general cumbersome but for OAuth2 providers it is a bit easier as it uses Faraday internally to connect to the provider. With Faraday, we can configure a test adapter and stub calls to return what we want.

The OmniAuth strategy provides an entry point to the Faraday connection, but we don’t have an access to the strategy directly, so we need to store it globally. For a Facebook strategy, we can achieve it as below whenever configuring Omniauth middleware:

module OmniAuth
  mattr_accessor :facebook_strategy
end
 
middleware.use OmniAuth::Strategies::Facebook, "APP_ID", "APP_SECRET" do |strategy|
  OmniAuth.facebook_strategy = strategy
end

Note you will need OmniAuth 0.2.0 (which currently is OmniAuth master) as previous versions don’t yield a block on initialization like above.

With access to the strategy, we do the acceptance test. First, we define the hashes we will return on the Faraday responses.

FACEBOOK_INFO = {
  :id =>; '12345',
  :link => 'http://facebook.com/plataformatec',
  :email => 'myemail@example.com',
  :first_name => 'Plataforma',
  :last_name => 'Tecnologia',
  :website => 'http://blog.plataformatec.com.br'
}
 
ACCESS_TOKEN = {
  :access_token => "nevergonnagiveyouup"
}

Now, we will define the stubs. OAuth2 strategies do two requests: one to retrieve the access token and another to retrieve the user information. In this example, let’s stub the Facebook requests and assign these stubs to a new connection.

stubs = Faraday::Adapter::Test::Stubs.new do |b|
  b.post('/oauth/access_token') { [200, {}, ACCESS_TOKEN.to_json] }
  b.get('/me?access_token=nevergonnagiveyouup') { [200, {}, FACEBOOK_INFO.to_json] }
end
 
OmniAuth.facebook_strategy.client.connection = Faraday::Connection.new do |builder|
  builder.adapter :test, stubs
end

For each provider the URLs may differ, so an idea is to do this on a TDD way (or you can browse through the OmniAuth source code and see the url that it requests):

  1. Assign the Faraday fake connection without stubs.
  2. Run your test
  3. See the test to raise an exception like “No stubbed request for DESIRED_URL”.
  4. Add the stubbed request with the response that you want.
  5. Repeat this process until the test pass

This is what we do on acceptance tests with OmniAuth: testing before and after the access to the external services.

Another approach is to do only one test by short-circuiting the provider authentication URLs. To do that on a Rails application, you can store the provider URL on a method like “OmniAuth.facebook_url” and stub the method to return the callback URL on your test. If you happen to be using Devise, the upcoming 1.2 version does the short-circuiting automatically for you, as you can see in Devise integration tests.

What about you? How do you stub OmniAuth requests and responses on your applications?

There is a vulnerability in Devise source code that allows someone to steal your session through session fixation attacks.

Who is affected?

This vulnerability is present in all Devise versions, in both 1.0 and 1.1 branches. However, you are only affected if you are using a Active Record ou Memcached or other server persistent session store. Projects using cookie stores (Rails default) are not affected.

If you are using Devise defaults which requires the user a password to update his account, it is unlikely the target account can be stolen, however the attacker will be able to normally perform actions in the website.

Releases

To fix this vulnerability, we released Devise 1.0.9 for Rails 2.3.x applications and Devise 1.1.4 for Rails 3.x.

In another note, Devise 1.2.rc was also released, which includes this security fix and Omniauth support. Check the CHANGELOG for more information.

Credit

Thanks to Olivier Dembour and Stephen Touset for reporting the vulnerability.

A couple weeks ago we finally released Devise 1.1 which is fully-compatible with Rails 3! Not only that, we’ve been working with Rails 3 since the first betas and several features were added along the way! Let’s take a look at those, some architectural changes and see how Devise 1.1 and Rails 3 will change how you handle authentication.

Pretty URLs with Metal

A common complaint in Devise 1.0 (for Rails 2.3) was, in order to know which message to show to the user when sign in failed, we had to pass a parameter in the URL as in /users/sign_in?unauthenticated=true while one would expect us to simply use flash messages. This happened because the redirection was done not from inside a controller, but a Rack application set up in Warden (a Rack authentication framework Devise relies on) and we could not access flash messages from it.

However, since Rails 3 moved several responsibilities to the Rack layer, including flash messages, we can easily access flash messages from any Rack application, allowing us to remove the parameter from the URL! Even more, Rails 3 provides small, fast, bare bone controllers through ActionController::Metal, which we used in Devise to clean and speed up the code considerably.

Locking goodness

The lockable module in Devise also went through a major overhaul. Previously, it already supported :unlock_strategy as option, allowing you to specify if the user could be automatically unlocked after a time period, through an e-mail token or both. Now, it also supports :none as option, meaning that all unlocking should be done manually.

Even more, there is a new option called :lock_strategy, that allows you to specify whether the lock happens only manually or after an amount of invalid sign in attempts.

HTTP Authentication on by default

In Devise 2.3, you may remember that we had a module called :http_authenticable along with :database_authenticatable and :token_authenticatable. While all three worked great, it was confusing that all HTTP authentication features were built on top of the database authentication and it was not possible to do HTTP authentication using a token unless we created a forth module called :http_token_authenticatable. We quickly noticed this could be improved by providing a better design and better abstract Devise authentication strategies.

And that is what happened in Devise 1.1. Now both database and token authentication work through HTTP with no extra work and the http authenticatable module was deprecated. Besides, if you are creating a new strategy on your own, you get both authentication through parameters (form) and HTTP with no extra work!

Routing customizations

We built Devise to be a full stack solution with customization in mind. In Devise 1.1, the customization abilities from Devise were taken to the next level. Now the devise_for method in routes accepts to extra options: :skip and :controllers. The first one allows you to skip the routes generation for a given controller/module in case you want to define them on your own, while the second allows you to change the router to point to a given controller in your application, like Users::ConfirmationsController instead of Devise’s internal controller.

Talking about Devise’s internal controller, Devise 1.1 namespaced all controllers classes, so now we have Devise::ConfirmationsController instead of ConfirmationsController.

Another limitation removed from Devise in this new version is related to URLs customizations. In prior versions, Devise used the URL to retrieve which scope is being accessed. That said, if you were accessing “/users/sign_in”, Devise had to inspect this URL and find the “/users” bit to specify the current scope is “users”. The same happened to “/admin/sign_in”.

This had a huge impact in URL customization, because if you wanted to have an URL like “/some_prefix/users/sign_in”, you had to tell Devise you were appending a prefix. Things could get even uglier if you wanted to prepend dynamic prefixes like “/:locale”.

In Devise 1.1, we use the new contraints API and Rack capabilities from the new router to specify which scope to use. So, instead of inspecting the URL, Devise retrieves the user from the request’s env hash as request.env["devise.mapping"].

For all the routes generated by devise_for, Devise automatically sets this value in the env hash. However, if you are creating your own routes, you need to set it manually using the constraints API:

constraints lambda { |r| r.env["devise.mapping"] = Devise.mappings[:user] } do
  # Add a custom sign in route for user sign in
  get "/sign_in", :to => "devise/sessions"
end

Of course, since this is rather a common pattern, we encapsulated it in a nice API:

devise_scope :user do
  # Add a custom sign in route for user sign in
  get "/sign_in", :to => "devise/sessions"
end

You can simply give a block to devise_for as well and get the same result:

devise_for :users do
  # Add a custom sign in route for user sign in
  get "/sign_in", :to => "devise/sessions"
end

All the routes specified in the block have higher priority than the ones generated by devise_for.

Awesomeness pack

The last feature we want to discuss is also a routing customization, but we decided to leave it up for last because it shows all the potential coming with Rails 3 and Devise 1.1.

In Devise 1.1, we added the ability to require authentication for a given url in the router, besides the existing before filters in controllers. This allow us to easily require authentication for third party rack application without a need to hack into them. Kisko Labs posted an interesting case where you can use Devise to require authentication to a Resque application in very few lines of code:

authenticate :admin do
  mount Resque::Server.new, :at => "/resque"
end

Devise simply uses the constraints API discussed above, allowing the request to continue only if the user is already authenticated. Otherwise, it redirects the admin to the sign page managed by Devise inside your Rails application. Indeed, when you have Rack, Rails 3 and Devise 1.1 playing along, great things can be accomplished quite easily!

There are several other features, bug fixes and deprecations included in this release, we invite you to check the CHANGELOG and take a look at them!

And we are happy to say this is not all, there is much more to come in Devise 1.2, including OAuth2 support which is already added in the master branch. Enjoy!