#250 Authentication from Scratchの改良

Ruby on Railsで、パスワードを非可逆暗号化する場合、has_secure_passwordを使うと便利です。しかし、私は、twilogサービスのように、メールアドレスを入力しない場合、暗号化したバスワードをシステムに保存しないようなRailsアプリを作りたく思っています。has_secure_passwordは、Rails 4では、validationsというオプションを持っていて、値falseを渡すと、has_secure_passwordがスキップされます。しかし、Rails 3.2では、そのようなオプションがなく、スキップが可能ではありません。一つの回避策として、stackoverflowに、How to skip has_secure_password validationsというスレッドがあり、それを試してみたのですが、コードが足りないようで、動作しませんでした。さらに、ググった所、RailsCastに、#250 Authentication from Scratchで、スクラッチから認証を実装しているサンプルを見つけました。

#250 Authentication from Scratchのサンプルを、Rails 3.2で動かそうとすると、問題にぶつかりました。サンプルでは、passwordにattr_accessorを使っていますが、これは、Rails 3.2では使用を推奨されていません。かわりに、attr_accessible を使用すべきですが、それが定義された属性は、データベーステーブルのカラムにならなければいけません(後記、ここの記述は間違いかもしれません。ググっていて、そういう情報に触れたのですが、そのurlを失念しました。また、カラムにならなくても、問題無いようです)。平文のpasswordやpassword_confirmationは、データベースに保存すべきではないので、attr_accessibleを使用できないということになります。そこで、Usersクラスでは、emailのみ、attr_accessibleを使用し、passwordとpassword_confirmationに対しては、アクセサをあらわに書きました。


  def password
    @password
  end

  def password=(password_str)
    @password = password_str
  end

  def password_confirmation
    @password_confirmation
  end

  def password_confirmation=(password_confirmation_str)
    @password_confirmation = password_confirmation_str
  end

そして、メールアドレス、パスワード入力フォームから呼ばれるcontroller、UsersControllerクラスで、


  def create
    email = params[:user][:email]
    password = params[:user][:password]
    password_confrimation = params[:user][:password_confirmation]
    @user = User.new(:email => email)
    @user.password = password
    @user.password_confirmation = password_confrimation
    if @user.save
      redirect_to root_url, notice: "signed up!"
    else
      render "new"
    end
  end

のように書き、Userクラスを生成するとき、emailの値のみを渡すようにしました。このようにすることで、パスワードが平文でデータベースに保存されることはなくなりました。

改良したサンプルを、GitHubに置きました。 andropenguin/auth_from_scratch

あとは、メールアドレス、パスワードの入力がない場合、パスワードの保存を行わず、入力がある場合、暗号化したパスワードをシステムに保存するようにしたいですが、それは多分、そんなに難しくはないでしょう。