deviseによるLogin認証機能@Heroku

やっぱりログインとか出来ないと駄目だよね、と思って初めは
ASCIIcasts 270: Rails 3.1の認証機能
を参考にして実装してたんだけど、メール認証ってどうやるんだろと思ってググったらdeviseってのが便利すぎて自分で作るのがあほらしいと言うか「道具をうまく使う」的な考えから活用することにした。設定の記録を残しておく。

deviseのインストール

github : plataformatec / devise / getting Started
を見ながら進めれば基本的には問題ない。ここで足りないのはHerokuのセットアップ。
Gemfileに

gem 'devise'

を追記してbundle install。依存関係でbcrypt-rubyは勝手に入るので、Gemfile上ではコメントアウトのままで問題なかった。

$ rails g devise:install

インストール時にマニュアルで「やること」が表示されるので、従いましょう。

railsの設定

1. Ensure you have defined default url options in your environments files. Here
is an example of default_url_options appropriate for a development environment
in config/environments/development.rb:

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

In production, :host should be set to the actual host of your application.

とのことなので
$ vim config/environments/development.rb

# 適当に追加
config.action_mailer.default_url_options = { :host => 'localhost:3000' }

$ vim config/environments/production.rb

# yourappを自分のアプリに変更
config.action_mailer.default_url_options = { :host => 'yourapp.heroku.com' }

2. Ensure you have defined root_url to *something* in your config/routes.rb.
For example:

root :to => "home#index"

# ちゃんとconfig/routes.rbでroot設定しとけよな!

3. Ensure you have flash messages in app/views/layouts/application.html.erb.
For example:

<%= notice %>

<%= alert %>

# メッセージ出したいらしいので、layoutの適当な場所に置きましょう。僕の場合置いたら表示崩れてうざくなった。

4. If you are deploying Rails 3.1 on Heroku, you may want to set:

config.assets.initialize_on_precompile = false

On config/application.rb forcing your application to not access the DB
or load models when precompiling your assets.

# やっとかないとrake asset時に

          • > Preparing app for Rails asset pipeline

Running: rake assets:precompile
rake aborted!
could not connect to server: Connection refused
Is the server running on host "127.0.0.1" and accepting
TCP/IP connections on port 5432?
Tasks: TOP => environment
(See full trace by running task with --trace)
Precompiling assets failed, enabling runtime asset compilation
Injecting rails31_enable_runtime_asset_compilation
Please see this article for troubleshooting help:
http://devcenter.heroku.com/articles/rails31_heroku_cedar#troubleshooting

# つって怒られるのでやっときましょう。

deviseでモデルを作成

$ rails g devise User

すると

create db/migrate/2012*******_devise_create_users.rb
create app/models/user.rb

辺りが作られます。
models/user.rbで細かい設定ができ、その設定に併せてmigrateファイルも変更しないと行けないので気をつけましょう。
今回はメール認証を行い、届いたメールのリンクへ飛んでアカウントの登録が完了する :confirmable オプションを付けます。

# models/user.rb
devise :database_authenticatable, :registerable,
 :recoverable, :rememberable, :trackable, :validatable, :confirmable #新しく追加

# db/migrate/2012ほにゃらら_devise_create_users.rb
## Confirmable
t.string :confirmation_token
t.datetime :confirmed_at
t.datetime :confirmation_sent_at
t.string :unconfirmed_email # Only if using reconfirmable
#コメントアウトを解除します

設定はこんなもん。

viewの生成

$ rails g devise:views

でいろいろ出来ます。

create app/views/devise/shared
create app/views/devise/shared/_links.erb
create app/views/devise/confirmations
create app/views/devise/confirmations/new.html.erb
create app/views/devise/passwords
create app/views/devise/passwords/edit.html.erb
create app/views/devise/passwords/new.html.erb
create app/views/devise/registrations
create app/views/devise/registrations/edit.html.erb
create app/views/devise/registrations/new.html.erb
create app/views/devise/sessions
create app/views/devise/sessions/new.html.erb
create app/views/devise/unlocks
create app/views/devise/unlocks/new.html.erb
create app/views/devise/mailer
create app/views/devise/mailer/confirmation_instructions.html.erb
create app/views/devise/mailer/reset_password_instructions.html.erb
create app/views/devise/mailer/unlock_instructions.html.erb

沢山できました。取り敢えず放置して、views/layouts/application.html.erbでも弄りましょう。
僕の環境ではtwitter bootstrapが用意してくれたナビゲーションバーがあるので、その辺りにでも

<% if user_signed_in? %>< li><%= link_to "Logout", destroy_user_session_path, method: :delete %>
<% else %>< li><%= link_to "Login", new_user_session_path %>
<% end %>

とかって入れておきます。序にapp/controllers/application_controller.rbも弄って

before_filter :authenticate_user!

を記述します。これでサイトへのすべてのアクセスはログインしないとダメになりました。特定のページや特定の行動を制限したい場合は個々のcontrollerに追記すればいいんじゃないかな!(まだ調べてない)異なるアクセス権の管理も出来る(adminとか)らしいので、とても便利ですね。

動作確認

一応もう動く状態にはなってるはずなので

$ rake db:migrate

しましょう。localhostにアクセスでログインを求められたらおkです。メールはrails sのログに

Sent mail to youraddress@email
Date: Wed, 15 Aug 2012 15:14:34 +0900
From: please-change-me-at-config-initializers-devise@example.com
Reply-To: please-change-me-at-config-initializers-devise@example.com
To: youraddress@email
Message-ID: <*****>
Subject: Confirmation instructions
Mime-Version: 1.0
Content-Type: text/html;
charset=UTF-8
Content-Transfer-Encoding: 7bit

Welcome youraddress@email

You can confirm your account email through the link below:

Confirm my account

とかって残るのでアクセスしましょう。無事ログインできたらおめ!

Herokuでエラー

取り敢えずHerokuにpushした所

2012-08-15T06:42:54+00:00 app[web.1]: Started POST "/users" for 219.106.224.237 at 2012-08-15 06:42:54 +0000
2012-08-15T06:42:54+00:00 app[web.1]: Processing by Devise::RegistrationsController#create as HTML
2012-08-15T06:42:54+00:00 app[web.1]: Parameters: {"utf8"=>"✓", "authenticity_token"=>"*****=", "user"=>{"email"=>"****@****.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Sign up"}
2012-08-15T06:42:54+00:00 app[web.1]: Rendered devise/mailer/confirmation_instructions.html.erb (0.8ms)
2012-08-15T06:42:57+00:00 app[web.1]:
2012-08-15T06:42:57+00:00 app[web.1]: Completed 500 Internal Server Error in 3513ms
2012-08-15T06:42:57+00:00 app[web.1]: Sent mail to *****@******.com (3094ms)

とエラーが出てしまいました。そう言えばproductionモードで試してなかったので試した所、同じエラーが。タイミング的にメールを送ろうとして失敗してるのでどうやらその辺りでしょうか。ググったら毎度おなじみのstackoverflowで発見。
Heroku/devise - Missing host to link to! Please provide :host parameter or set default_url_options

Herokuの設定

HerokuでSendgridのaddonがあれば良いらしいので

$ heroku addons:add sendgrid:starter

ホントHerokuは無料でいろいろ遊べるのがすごい。
config/initializers/mail.rbを新規作成して

ActionMailer::Base.smtp_settings = {
:address => 'smtp.sendgrid.net',
:port => '587',
:authentication => :plain,
:user_name => ENV['SENDGRID_USERNAME'],
:password => ENV['SENDGRID_PASSWORD'],
:domain => 'heroku.com'
}
ActionMailer::Base.delivery_method = :smtp

で保存で設定は終わりです。
これでHerokuからもメールが送れるようになりました。
サインアップやらログインを試した所、ちゃんと動くようになりました。やったー!
参考:
github / plataformatec / devise
RailsでDeviseを使ってみた
Rails のユーザー認証機能の新デファクトDeviseでログイン/ログオフ
Heroku/devise - Missing host to link to! Please provide :host parameter or set default_url_options
Heroku : SendGrid
Heroku : Rails 3.1+ Asset Pipeline on Heroku Cedar