ラベル OAuth の投稿を表示しています。 すべての投稿を表示
ラベル OAuth の投稿を表示しています。 すべての投稿を表示

2011-03-22

twitterやfacebookのOAuthをつかってrails+omniauthでログイン機能を実装するメモ その2

前回(http://taksatou.blogspot.com/2011/03/twitterfacebookrails.html) 、OAuthで認証するところまでできたので、今回はtwitterアカウントでログインするところを作ります。

omniauthのrailsチュートリアルビデオのpart2に大体対応してますが、ここでの内容はちょっと変えてます。
- http://railscasts.com/episodes/236-omniauth-part-2


rails generate

以下のようにしてdeviseのセットアップとmigrationをします。
emailとpasswordはつかわないので消します。

rails g devise:install
rails g devise user
rails g migration AddOauthTokenAndOauthTokenSecretToAuthentications oauth_token:string oauth_token_secret:string
rails g migration RemoveEmailAndEncryptedPasswordAndPasswordSaltFromUsers email:string encrypted_password:string password_salt:string

rake db:migrate

model

authenrication.rb

class Authentication < ActiveRecord::Base
  belongs_to :user
end
  • attr_accessibleは必要ないので消します

user.rb

class User < ActiveRecord::Base
  has_many :authentications

  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable

  def password_required?
    false
  end
end
  • 今回の場合、userは一つのtwitter認証情報をもつだけなのでhas_manyはおかしいと思うかもしれないですが、今後facebookとかとも連動させたくなったときのためにこうしてます。
  • devise の :validatable パラメータは削除してます。

controller

前回編集したAuthenticationsControllerをさらに以下のように編集します。

class AuthenticationsController < ApplicationController

def create
  omniauth = request.env['omniauth.auth']
  authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])

  if authentication
    sign_in_and_redirect(:user, authentication.user)
  elsif current_user          # 既にログインしてるけど、facebookとかの権限も追加するとき
    current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'],
                                         :oauth_token => omniauth['credentials']['token'],
                                         :oauth_token_secret => omniauth['credentials']['secret'])
    redirect_to authentications_url
  else                        # 新規ユーザのとき
    user = User.new
    user.authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'],
                               :oauth_token => omniauth['credentials']['token'],
                               :oauth_token_secret => omniauth['credentials']['secret'])
    user.save!
    sign_in_and_redirect(:user, user)
  end
end

view

確認用にauthenticateのviewを以下のように編集します

<h1>Authentications</h1>

<% if user_signed_in? %>
  Signed in as <%= current_user.id %>
  Not you ? <%= link_to "Sign out", destroy_user_session_path %>
<% else %>
  <%= link_to "Sign in", "/auth/twitter" %>
<% end %>


<table>
  <tr>
    <th>User</th>
    <th>Provider</th>
    <th>Uid</th>
  </tr>
  <% if @authentications %>
    <% for authentication in @authentications %>
      <tr>
        <td><%= authentication.user_id %></td>
        <td><%= authentication.provider %></td>
        <td><%= authentication.uid %></td>
        <td><%= link_to "Destroy", authentication, :confirm => 'Are you sure?', :method => :delete %></td>
      </tr>
    <% end %>
  <% end %>
</table>

routes.rb

前回設定した内容にdevise用の設定も追加します

devise_for :users
resources :authentications
match '/auth/:provider/callback' => 'authentications#create'
root :to => "authentications#index"

確認

以上でセットアップはおわりです。
実際に http://localhost:3000/authentications にアクセスしてsign in をクリックしてうまくログインできれば成功です。

以下のようにすればコンソールでtwitterにポストの確認ができるはずです。

$ rails c
Twitter.configure do |config|
  config.consumer_key = 'CONSUMER_KEY'
  config.consumer_secret = 'CONSUMER_SECRET'
  config.oauth_token = User.all[0].authentications[0].oauth_token
  config.oauth_token_secret = User.all[0].authentications[0].oauth_token_secret
end
Twitter.update 'hello, omniauth!'

まとめ

  • 結構大変でした
  • deviseとかomniauthとかそもそもrailsのことをよくわかってないので間違ってたら教えてください

twitterやfacebookのOAuthをつかってrails+omniauthでログイン機能を実装するメモ その1

OAuthをつかっていろんなサービスと連動したアプリケーションがつくりたくなったので調査。
railsでやるにはoauth_pluginとかwarden_oauthとか色々なプラグインがあるみたいだけど、omniauthがよさそうです。
rails初心者なので基本的にはomniauthのチュートリアルビデオに沿って作業します。
- https://github.com/intridea/omniauth
- http://railscasts.com/episodes/235-omniauth-part-1

自前でパスワードやメールアドレスは保持せずに、twitterやfacebookのアカウントを使ってログインできるようにするのが目標です。
ちなみにtwitter anywhereを使えば似たようなことはできそうですが、twitterに依存してしまうので今回はパス。

--

インストール

とりあえず以下を追記。nifty-generators, twitterはお好みで。deviseは後半で使います

gem 'devise'
gem 'omniauth'
gem 'nifty-generators', :group => :development
gem 'twitter'

設定

omniauth.rb作成

config/initializers/omniauth.rbを作成

Rails.application.config.middleware.use OmniAuth::Builder do
  privider :twitter, 'CONSUMER_KEY', 'CONSUMER_SECRET'
end

ここでセットするoauthアプリケーションは、twitter側で正しくcallback urlを設定しておく必要があります。
通常は http://yourhost.com/auth/twitter/callback のようなURLです。
そうしておかないと、twitterにリダイレクトされる前で 401 unauthorized となってはじかれます。

controller

ここではnifty generatorをつかいます

rails g nifty:scaffold authentication user_id:integer provider:string uid:string index create destroy

できたAuthenticationsControllerのcreateを以下のように書き換えます。

class AuthenticationsController < ApplicationController

def create
  render :text => request.env['omniauth.auth'].to_yaml
end

request.env['omniauth.auth']の部分は過去のバージョンではrequest.env['rack.auth']なので、うまくいかない場合はバージョンを確認してください。

routes.rb

callback用に以下を追記します

match '/auth/:provider/callback' => 'authentications#create'

ここまでやって http://localhost:3000/auth/twitter にアクセスしてtwitterで認証すると、yamlで認証情報が取得できているのが確認できます。

まとめ

以上でとりあえずOAuthでユーザ情報やcredentialをとることができました。
次回( http://taksatou.blogspot.com/2011/03/twitterfacebookoauthrailsomniauth.html )後半でtwitterでログインするところをつくります。

その他

  • rails 3.0.3, ruby 1.8.7 を使用

2010-04-17

OAuth Request Body HashをつかうためにOAuthクライアントを実装してみた時のメモ

OAuth Request Body Hashとは、OAuthの拡張仕様の一つです。
OAuth Coreではapplication/x-www-form-urlencodedなリクエストに対してのみ正当性を保証します。そのためPOSTでXMLを送信したいときなどには別の手段が必要になります。OAuth Request Body Hashは、そのようなnon-form-encodedなリクエストボディに対する正当性を保証するための仕組みです。
詳細は下記をどうぞ。
http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html
(oauth body hashでググるとドラフトがトップに出るようですが(2010-04-17現在)、Finalバージョンはすでにあがっているようです)

YahooのTech Blogも参考になります。
http://techblog.yahoo.co.jp/web/openid/oauth_1/


このoauth_body_hashなのですが、日本語の情報が少なく、今のところライブラリで対応しているものがあまり無いようなので自分で実装してみました。
せっかくなので2-legged OAuthでoauth_body_hashを使う場合のサンプルコードをさらしておきます。
<?php

$consumer_key = "consumer_key";
$consumer_secret = "consumer_secret";
$version = "1.0";
$timestamp = time();
$nonce = (string)rand();
$signature_method = "HMAC-SHA1";
$url = "http://example.com/";
$request_body = '<?xml version="1.0" encoding="utf-8"?><foo>bar</foo>';

$body_hash = urlencode(base64_encode(hash("SHA1", $request_body, true)));

$params = sprintf('oauth_body_hash=%s&oauth_consumer_key=%s&oauth_nonce=%s&oauth_signature_method=%s&oauth_timestamp=%d&oauth_version=%s',
                  $body_hash, $consumer_key, $nonce, $signature_method, $timestamp, $version);
$sbs = sprintf('POST&%s&%s', urlencode($url), urlencode($params));
$signature = urlencode(base64_encode(hash_hmac("SHA1", $sbs, $consumer_secret."&",  true)));
$auth_header = sprintf('OAuth oauth_body_hash="%s",oauth_consumer_key="%s",oauth_version="%s",oauth_timestamp="%d",oauth_nonce="%s",oauth_signature_method="%s",oauth_signature="%s',
                       $body_hash, $consumer_key, $version, $timestamp, $nonce, $signature_method, $signature);

$headers = array("Host: example.com",
                 "Accept: */*",
                 "Authorization: $auth_header",
                 "Content-Type: text/xml; charset=utf-8",
                 "Content-Length: ".strlen($request_body));

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $request_body);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_exec($ch);
curl_close($ch);


なお、PHPに限らずOAuthを自分で実装する時の注意点としては以下のような感じでしょうか

* oauth_body_hashの値はurlencodeした値です。signatureを計算する時はsignature base stringに組み込んだあともう一度urlencodeします。
* oauth_body_hashの計算に秘密鍵は必要ありません。つまりHMACではなく、単なるダイジェスト値です。
* base64 encodeの実装によっては末尾に改行を付加するものがありますが、改行は不要です。(PHPの場合は上記のサンプルと通りでOKですが、Javaのcom.sun.mail.util.BASE64EncoderStreamなどでは改行を付加するものがあるようです)
* timestampは秒までの整数値です。当然と言えば当然なのですが、なにも知らずに時間取得関数を使うとミリ秒まで返してくるものがあるので、はまると気づきにくいかもしれません。
* 2-legged OAuthの場合はaccess tokenが不要ですので、signatureを計算する時の秘密鍵はconsumer_secretに&をつけたものになります。
* signature base stringをつくるときのパラメータはアルファベット順にソートする必要があります。
* signature base string自体はurlencodeする必要はありません。urlencodeするのはURLとパラメータです。



参考:http://oauth.net/core/1.0/

ZenBackWidget