3 Comments

Basic Devise and Mass Assignment – Am I Missing Something?

This week I’ve started adding real authentication to our application. We’re running the latest and greatest Rails, and we’re using ActiveRecord, so Devise looks like an excellent choice for authentication.

Except I’m running into a problem getting the basics wired up. In fact, mass assignment rules are getting in my way. Am I missing something?

Context

Instead of getting into unnecessary details about my application, I’m going to use Daniel Kehoe’s handy rails3-devise-rspec-cucumber Rails application as a starting point.

With the application setup and running, view the Login page and click the “Sign in” button with an empty form. You’ll get an “Invalid email or password” error. Cool. But now open up app/models/user.rb and remove password and password_confirmation fields from the attr_accessible list. Now submit the empty for again. Uh oh! Now we get an application error about how password is not mass assignable.

My first reaction to this was “why the hell is Devise trying to mass assign the password, especially on login?!?” I did some digging and found that there’s an interaction going on between Devise’s Devise::SessionsController#create and Devise::SessionsController#new. The create action is rejecting the login, as it should, and then running the new action. The new action then creates a new User object from the parameters. Now the first time you hit the new action, there are no parameters, so all is well. But when new is run in the context of create‘s parameters, we get a mass assignment error since password is in the parameters.

The Workaround

Assuming I want to keep password off the attr_accessible list, the best I could think of was to create my own controller that derives from Devise::SessionsController and configure Devise to use it for my session routes. Then, based on an idea Patrick gave me, I created a before_filter for the new action that strips the user parameter. Here’s the code.


class SessionsController < Devise::SessionsController
  before_filter only: :new do
    params.delete(:user)
  end
end

Pretty small. The downside here is that the other form fields, like Email and Remember me don’t get re-rendered. That’s a little obnoxious, but not the end of the world. At a later date I can write some fancier code to preserve those values.

A Question

So now my question is: am I missing something? It seems like password is an excellent candidate for a field that should not be mass assignable1. But perhaps in this context it’s ok? Or am I misusing Devise’s built in controllers?

Any insight here is appreciated.

1 I know little about the details, but GitHub’s recent mass assignment exploit has my mind particularly attuned to mass assignment protection, especially since I happen to be implementing login this week.