OpenID Authentication

いますぐ使えるOpenID:第4回 Railsで作るOpenID対応アプリケーション実践(前編)|gihyo.jp

$ rails openid_test
$ cd openid_test/
$ script/plugin install git://github.com/rails/open_id_authentication.git
$ rake open_id_authentication:db:create
$ script/generate scaffold user nickname:string identity_url:string
$ script/generate controller sessions new create destroy

app/controllers/sessions_controller.rb

class SessionsController < ApplicationController
  def login
  end

  def create
    if using_open_id?
      open_id_authentication
    else
      failed_login 'OpenIDを入力してください'
    end
  end

  def logout
    session[:user_id] = nil
    flash[:notice] = 'ログアウトしました'
    redirect_to(login_url)
  end

protected
  def open_id_authentication
    authenticate_with_open_id do |result, identity_url|
      if result.successful?
        if @current_user = User.find_by_identity_url(identity_url)
          successful_login
        else
          session[:identity_url] = identity_url
          redirect_to(new_user_url)
        end
      else
        failed_login result.message
      end
    end
  end

private
  def successful_login
    session[:user_id] = @current_user.id
    redirect_to(home_user_url)
  end

  def failed_login(message)
    flash[:error] = message
    redirect_to(login_url)
  end
end

app/controllers/users_controller.rb

class UsersController < ApplicationController
  # GET /users/new
  def new
    redirect_to(login_url) unless session[:identity_url]
    @user = User.new
  end

  # POST /users
  def create
    @user = User.new(params[:user])
    @user.identity_url = session[:identity_url]

    if @user.save
      session[:identity_url] = nil
      session[:user_id] = @user.id
      flash[:notice] = 'ユーザを作成しました'
      redirect_to(home_user_path)
    elsif @user.errors[:identity_url]
      redirect_to(login_url)
    else
      render :action => 'new'
    end
  end

  # GET /users/home
  def home
    unless @user = User.find(session[:user_id]) rescue nil
      redirect_to(login_url)
    end
  end
end

app/models/user.rb

class User < ActiveRecord::Base
  validates_presence_of :nickname
  validates_uniqueness_of :nickname

  validates_presence_of :identity_url
  validates_uniqueness_of :identity_url
end

app/views/sessions/login.html.erb

<% form_for(:session, :url => { :action => 'create' }) do |f| %>
  <p style="color: red"><%= flash[:error] %></p>

  <p>
    <%= f.label :openid_url, 'OpenID' %><br />
    <%= f.text_field :openid_url, :name => 'openid_url' %>
  </p>
  <p>
    <%= f.submit "ログイン", :disable_with => "ログイン中" %>
  </p>
<% end %>

app/views/users/new.html.erb

<h1>ユーザ登録</h1>

<% form_for(@user) do |f| %>
  <%= f.error_messages %>

  <p>
    <%= f.label :nickname %><br />
    <%= f.text_field :nickname %>
  </p>
  <p>
    <%= f.submit "登録" %>
  </p>
<% end %>

app/views/users/home.html.erb

<p>
  <b>ニックネーム:</b>
  <%=h @user.nickname %>
</p>

<p>
  <b>Identity url:</b>
  <%=h @user.identity_url %>
</p>

config/routes.rb

ActionController::Routing::Routes.draw do |map|
  map.login  'login',  :controller => 'sessions', :action => 'login'
  map.logout 'logout', :controller => 'sessions', :action => 'logout'
  map.connect 'session', :controller => 'sessions', :action => 'create', :conditions => { :method => :post }

  map.resources :users, :only => [ :new, :create ]
  map.home_user 'users/home', :controller => 'users', :action => 'home'

  map.root :controller => 'sessions', :action => 'new'
end

db/migrate/XXXXXXXXXXXXXX_create_users.rb

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string :nickname,     :null => false
      t.string :identity_url, :null => false

      t.timestamps
    end
    add_index(:users, :identity_url, :unique => true)
  end

  def self.down
    drop_table :users
  end
end