2011年12月20日火曜日

OpenID Connectについて勉強する

①OpenID Connectとは
OpenID Connectは、OAuth2.0仕様をベースとした認証連携用のプロトコルであり、レストフルなAPIを介して認証情報を交換するフレームワークを提供する。
クライアントにおいては、すべての種類(ブラウザ、携帯端末、javascriptなど)に対応し、認証連携が可能である。
オプション的に、認証情報の暗号化、OPのディスバリ、セッション管理(ログアウトを含む)をサポートしていている。

②OpenID 2.0との違い
OpenID Connectを使用すれば、OpenID2.0と同等のことが可能であるが、APIがよりfriendlyである。また、OpenID Connectは、署名/暗号化に関して、よりロバストなメカニズムを提供している。
一番の違いは、プロトコルでOAuth2.0仕様を取り入れていることにある。


③OpenID Connectの規約
OpenID Connectでは、大きく分けて以下の6つの仕様を規定する。

  • Basic Client Profile - Webサービス(RP)がOPと認証連携する際のプロトコルを規定する。
  • Discovery – (オプション) OPのエンドポイントを検索する際のプロトコルを規定する。
  • Dynamic Registration – (オプション) 動的にOPにRPを登録するプロトコルを規定する。
  • Standard – RP、OPとユーザ間のHTTPバインディング仕様を規定する。
  • Messages – OpenID Connectで使用するメッセージの規約を規定する。
  • Session Management – (オプション) セッション管理の仕様を規定する。


2011年12月16日金曜日

OAuth2.0 プロトコルを理解する

http://memoyasu.blogspot.com/2011/12/oauth20-facebook.html」で作成したサンプルアプリを使用し、OAuth2.0のプロトコルの中身をみてみる。


①ユーザがブラウザを使用してサンプルアプリにアクセスする。

②サンプルアプリからFacebookに認可要求する。
------------------------------------------------------------
Started GET "/client/oauth" for 127.0.0.1 at 2011-12-15 23:29:57 +0900
  Processing by ClientController#oauth as HTML
Redirected to https://graph.facebook.com/oauth/authorize?response_type=code&clie
nt_id=*************&redirect_uri=http%3A%2F%2Fopenam.yasuyasu.com%3A3001%2Fcli
ent%2Fcallback&scope=offline_access%2Cpublish_stream
------------------------------------------------------------
★reponse_typeでは、認可コードを返すように要求する。
★client_idには、Facebookに登録した際に作成されたクライアントアプリのIDを指定する。
★redirect_uriでは、戻り先URLを指定する。

③ユーザがブラウザを使用してFacebookとクレデンシャル情報をやりとりする。また、サンプルアプリに対してサービス利用を許可するかを認可する。

④Facebookがサンプルアプリに認可コードを返す。
------------------------------------------------------------
Started GET "/client/callback?code=AQDp......."
for 127.0.0.1 at 2011-12-15 23:29:58 +0900
------------------------------------------------------------

⑤サンプルアプリはFacebookに対して認可コードに基づき、アクセストークンを要求(POST)する。
------------------------------------------------------------
{"grant_type"=>"authorization_code", "code"=>"AQBaL_ue......", "client_id"=>"****", "client_secret"=>"****", :redirect_uri=>"http://openam.yasuyasu.com:3001/client/callback"}
------------------------------------------------------------
★codeには、Facebookから取得した認可コードを指定する。
★client_idには、Facebookに登録した際に作成されたクライアントアプリのIDを指定する。
★client_secretには、Facebookに登録した際に作成されたクライアントアプリのパスワードを指定する。
★redirect_uriでは、戻り先URLを指定する。

⑥Facebookは、サンプルアプリにアクセストークンを返す。
------------------------------------------------------------
access_token=AAAE.......
------------------------------------------------------------

⑦アンプルアプリは、Facebookに対して、アクセストークンに基づきサービスを要求(GET)する。
------------------------------------------------------------
https://graph.facebook.com/me?access_token=AAAE.......
------------------------------------------------------------

⑧Facebookはアクセストークンを検証し、OKであれば、サンプルアプリにサービス情報を返す。
------------------------------------------------------------
{\"id\":\"10..............\",\"name\":\"**********\",.............}
------------------------------------------------------------

2011年12月13日火曜日

OAuth2.0 Facebookと連携してみる

OAuth2.0を使用して、Facebookと連携するrailsアプリを作成する。
なお、クライアントのサンプルアプリでは、Facebookが公開しているOAuth2.0用ライブラリを使用する(実は、Facebookのライブラリでは、OAuth2.0ライブラリを呼び出している)。

①以下のサイトにアクセスし、アプリを登録する。
https://developers.facebook.com/apps
Client IDとClient secretが作成される。



②gemコマンドを使用し、Facebookが公開しているOAuth2.0ライブラリをインストールする。

C:\Users\yasu\clientapp\facebook>gem install facebook_oauth
Fetching: facebook_oauth-0.3.0.gem (100%)
Successfully installed facebook_oauth-0.3.0
1 gem installed
Installing ri documentation for facebook_oauth-0.3.0...
Installing RDoc documentation for facebook_oauth-0.3.0...


③railsのサンプルアプリケーションを作成する。
c:\Users\yasu\clientapp>rails new facebook
       exist
      create  README
      create  Rakefile
      create  config.ru
      create  .gitignore
      create  Gemfile
      create  app
      create  app/assets/images/rails.png
      create  app/assets/javascripts/application.js
      create  app/assets/stylesheets/application.css
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb
      create  app/mailers
      create  app/models
      create  app/views/layouts/application.html.erb
      create  app/mailers/.gitkeep
      create  app/models/.gitkeep
      create  config
      create  config/routes.rb
      create  config/application.rb
      create  config/environment.rb
      create  config/environments
      create  config/environments/development.rb
      create  config/environments/production.rb
      create  config/environments/test.rb
      create  config/initializers
      create  config/initializers/backtrace_silencers.rb
      create  config/initializers/inflections.rb
      create  config/initializers/mime_types.rb
      create  config/initializers/secret_token.rb
      create  config/initializers/session_store.rb
      create  config/initializers/wrap_parameters.rb
      create  config/locales
      create  config/locales/en.yml
      create  config/boot.rb
      create  config/database.yml
      create  db
      create  db/seeds.rb
      create  doc
      create  doc/README_FOR_APP
      create  lib
      create  lib/tasks
      create  lib/tasks/.gitkeep
      create  lib/assets
      create  lib/assets/.gitkeep
      create  log
      create  log/.gitkeep
      create  public
      create  public/404.html
      create  public/422.html
      create  public/500.html
      create  public/favicon.ico
      create  public/index.html
      create  public/robots.txt
      create  script
      create  script/rails
      create  test/fixtures
      create  test/fixtures/.gitkeep
      create  test/functional
      create  test/functional/.gitkeep
      create  test/integration
      create  test/integration/.gitkeep
      create  test/unit
      create  test/unit/.gitkeep
      create  test/performance/browsing_test.rb
      create  test/test_helper.rb
      create  tmp/cache
      create  tmp/cache/assets
      create  vendor/assets/stylesheets
      create  vendor/assets/stylesheets/.gitkeep
      create  vendor/plugins
      create  vendor/plugins/.gitkeep
         run  bundle install
Fetching source index for http://rubygems.org/
Using rake (0.9.2.2)
Using multi_json (1.0.4)
Using activesupport (3.1.2)
Using builder (3.0.0)
Using i18n (0.6.0)
Using activemodel (3.1.2)
Using erubis (2.7.0)
Using rack (1.3.5)
Using rack-cache (1.1)
Using rack-mount (0.8.3)
Using rack-test (0.6.1)
Using hike (1.2.1)
Using tilt (1.3.3)
Using sprockets (2.1.2)
Using actionpack (3.1.2)
Using mime-types (1.17.2)
Using polyglot (0.3.3)
Using treetop (1.4.10)
Using mail (2.3.0)
Using actionmailer (3.1.2)
Using arel (2.2.1)
Using tzinfo (0.3.31)
Using activerecord (3.1.2)
Using activeresource (3.1.2)
Using ansi (1.4.1)
Using bundler (1.0.21)
Using coffee-script-source (1.1.3)
Installing execjs (1.2.11)
Using coffee-script (2.2.0)
Using rack-ssl (1.3.2)
Using json (1.6.3)
Using rdoc (3.11)
Using thor (0.14.6)
Using railties (3.1.2)
Using coffee-rails (3.1.1)
Using jquery-rails (1.0.19)
Using rails (3.1.2)
Using sass (3.1.11)
Using sass-rails (3.1.5)
Installing sqlite3 (1.3.5)
Using turn (0.8.3)
Using uglifier (1.1.0)
Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem
is installed.
c:\Users\yasu\clientapp>cd facebook
c:\Users\yasu\clientapp\facebook>rails generate controller Client index callback
      create  app/controllers/client_controller.rb
       route  get "client/callback"
       route  get "client/index"
      invoke  erb
      create    app/views/client
      create    app/views/client/index.html.erb
      create    app/views/client/callback.html.erb
      invoke  test_unit
      create    test/functional/client_controller_test.rb
      invoke  helper
      create    app/helpers/client_helper.rb
      invoke    test_unit
      create      test/unit/helpers/client_helper_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/client.js.coffee
      invoke    scss
      create      app/assets/stylesheets/client.css.scss

④client_controller.rb(C:\Users\yasu\clientapp\facebook\app\controllers配下)を修正する。
----------------------------------------------------------
require "oauth2"
require 'facebook_oauth'
class ClientController < ApplicationController

  # コールバック先URL
  CALLBACK_URL    = "http://openam.yasuyasu.com:3001/client/callback"
  # CONSUMER KEY
  CONSUMER_KEY    = "id" ←★Client IDを設定
  # CONSUMER SECRET
  CONSUMER_SECRET = "secret" ←★Client secretを設定

  # サンプルアプリがFacebookからアクセストークンを取得する際、
  # クライアント側でサーバ証明書を検証する(デフォルトの設定)。
  # (よくない対応だが、)ここではサーバ証明書を検証しないようにする。
  OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE

  def index
  end

  def oauth
    client = FacebookOAuth::Client.new(
      :application_id     => CONSUMER_KEY,
      :application_secret => CONSUMER_SECRET,
      :callback           => CALLBACK_URL
    )
    # 認可コードの要求
    redirect_to client.authorize_url
  end

  def callback
    @client = FacebookOAuth::Client.new(
      :application_id     => CONSUMER_KEY,
      :application_secret => CONSUMER_SECRET,
      :callback           => CALLBACK_URL
    )
    # アクセストークンの取得
    @client.authorize(:code => params[:code])
  end
end
----------------------------------------------------------

⑤index.html.erb(C:\Users\yasu\clientapp\facebook\app\views\client配下)を修正する。以下を追加する。
<%= link_to 'OAuth', '/client/oauth' %>

⑥callback.html.erb(C:\Users\yasu\clientapp\facebook\app\views\client配下)を修正する。以下を追加する。
<%= @client.me.info %>

WEBrickを起動する。

C:\Users\yasu\clientapp\facebook>rails  server -p 3001
=> Booting WEBrick
=> Rails 3.1.2 application starting in development on http://0.0.0.0:3001
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2011-12-12 23:02:08] INFO  WEBrick 1.3.1
[2011-12-12 23:02:08] INFO  ruby 1.9.3 (2011-10-30) [i386-mingw32]
[2011-12-12 23:02:08] INFO  WEBrick::HTTPServer#start: pid=6300 port=3001



 ブラウザで「http://openam.yasuyasu.com:3001/client/index」にアクセスする。Facebook側でサンプルアプリと連携してよいかを確認されるため、認可する。
以下の画面が表示されれば連携が完了である。





2011年12月8日木曜日

OAuth2.0について勉強する

以下のURLが、OAuth1.0との違いを述べられていて理解しやすかった。
http://www.atmarkit.co.jp/fsmart/articles/oauth2/02.html

【図の抜粋】






















上記のシーケンスを見ても分かるとおり、OAuth2.0は1.0と比較して以下の違いがある。
●リクエストトークンがまるごとなくなった。かわりに、認可コードを使用して、アクセストークンを取得している。
●OAuth1.0では、リクエストトークン、アクセストークンの取得時には必ず署名しなければならない仕様だった。OAuth2.0では、HTTPS通信を必須にすることで、署名しなくてよくなった(改ざんを防ぐことができる)。

<感想>
OAuth2.0は、SAML2.0で規定するWebブラウザSSOプロファイル(Artifactバインディング)に非常によく似たシーケンスである(認可コード=Artifact、アクセストークン=SAMLアサーションのようなイメージ)。
SAMLの認可アサーションも、他のWebサービスに伝播させれば、アクセストークンみたいな使い方ができると思う。ただし、SAMLはXMLベースなので、やっぱり軽量なのは、OAuth2.0だと考える。

2011年12月5日月曜日

OAuth1.0 Twitterと連携してみる

サンプルアプリ(Consumer)とTwitter(Service Provider)をOAuth1.0で連携させ、Twitterのつぶやきをアプリで表示してみる。

①gemコマンドを使用し、oauthをインストールする。
c:\>gem install oauth
Fetching: oauth-0.4.5.gem (100%)
Successfully installed oauth-0.4.5
1 gem installed
Installing ri documentation for oauth-0.4.5...
Installing RDoc documentation for oauth-0.4.5...


②Twitterの以下のOAuthページにサンプルアプリケーション(Consumer)を登録する。登録した後、Consumer keyとConsumer sercretが作成される。
https://dev.twitter.com/apps

③Railsアプリケーションを作成する。
C:\Users\yasu\clientapp>rails new twitoauth
      create
      create  README
      create  Rakefile
      create  config.ru
      create  .gitignore
      create  Gemfile
      create  app
      create  app/assets/images/rails.png
      create  app/assets/javascripts/application.js
      create  app/assets/stylesheets/application.css
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb
      create  app/mailers
      create  app/models
      create  app/views/layouts/application.html.erb
      create  app/mailers/.gitkeep
      create  app/models/.gitkeep
      create  config
      create  config/routes.rb
      create  config/application.rb
      create  config/environment.rb
      create  config/environments
      create  config/environments/development.rb
      create  config/environments/production.rb
      create  config/environments/test.rb
      create  config/initializers
      create  config/initializers/backtrace_silencers.rb
      create  config/initializers/inflections.rb
      create  config/initializers/mime_types.rb
      create  config/initializers/secret_token.rb
      create  config/initializers/session_store.rb
      create  config/initializers/wrap_parameters.rb
      create  config/locales
      create  config/locales/en.yml
      create  config/boot.rb
      create  config/database.yml
      create  db
      create  db/seeds.rb
      create  doc
      create  doc/README_FOR_APP
      create  lib
      create  lib/tasks
      create  lib/tasks/.gitkeep
      create  lib/assets
      create  lib/assets/.gitkeep
      create  log
      create  log/.gitkeep
      create  public
      create  public/404.html
      create  public/422.html
      create  public/500.html
      create  public/favicon.ico
      create  public/index.html
      create  public/robots.txt
      create  script
      create  script/rails
      create  test/fixtures
      create  test/fixtures/.gitkeep
      create  test/functional
      create  test/functional/.gitkeep
      create  test/integration
      create  test/integration/.gitkeep
      create  test/unit
      create  test/unit/.gitkeep
      create  test/performance/browsing_test.rb
      create  test/test_helper.rb
      create  tmp/cache
      create  tmp/cache/assets
      create  vendor/assets/stylesheets
      create  vendor/assets/stylesheets/.gitkeep
      create  vendor/plugins
      create  vendor/plugins/.gitkeep
         run  bundle install
Fetching source index for http://rubygems.org/
Installing rake (0.9.2.2)
Installing multi_json (1.0.4)
Using activesupport (3.1.2)
Using builder (3.0.0)
Using i18n (0.6.0)
Using activemodel (3.1.2)
Using erubis (2.7.0)
Using rack (1.3.5)
Using rack-cache (1.1)
Using rack-mount (0.8.3)
Using rack-test (0.6.1)
Using hike (1.2.1)
Using tilt (1.3.3)
Installing sprockets (2.1.2)
Using actionpack (3.1.2)
Using mime-types (1.17.2)
Using polyglot (0.3.3)
Using treetop (1.4.10)
Using mail (2.3.0)
Using actionmailer (3.1.2)
Using arel (2.2.1)
Using tzinfo (0.3.31)
Using activerecord (3.1.2)
Using activeresource (3.1.2)
Installing ansi (1.4.1)
Using bundler (1.0.21)
Installing coffee-script-source (1.1.3)
Installing execjs (1.2.9)
Installing coffee-script (2.2.0)
Using rack-ssl (1.3.2)
Installing json (1.6.3) with native extensions
Using rdoc (3.11)
Using thor (0.14.6)
Using railties (3.1.2)
Installing coffee-rails (3.1.1)
Installing jquery-rails (1.0.19)
Using rails (3.1.2)
Installing sass (3.1.11)
Installing sass-rails (3.1.5)
Using sqlite3 (1.3.4)
Installing turn (0.8.3)
Installing uglifier (1.1.0)
Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem
is installed.
C:\Users\yasu\clientapp>cd twitoauth

C:\Users\yasu\clientapp\twitoauth>rails generate controller index
      create  app/controllers/index_controller.rb
      invoke  erb
      create    app/views/index
      invoke  test_unit
      create    test/functional/index_controller_test.rb
      invoke  helper
      create    app/helpers/index_helper.rb
      invoke    test_unit
      create      test/unit/helpers/index_helper_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/index.js.coffee
      invoke    scss
      create      app/assets/stylesheets/index.css.scss



④C:\Users\yasu\clientapp\twitoauth\app\controllers\IndexController.rbを修正し、IndexContollerクラスを作成する。
--------------------------------------------------------

require 'json'
require "oauth"

class IndexController < ApplicationController
  def self.consumer
    OAuth::Consumer.new(

      "Consumer key", ←★Twitter画面で表示されたConsumer Keyを設定。
      "Consumer secret", ←★Twitter画面で表示されたConsumer secretを設定。

      { :site => "http://twitter.com" }
    )
  end

  def index
  end

  def oauth

    # 未認可のRequestTokenの取得
    request_token = IndexController.consumer.get_request_token(
      :oauth_callback => "http://#{request.host_with_port}/oauth_callback"
    )
    session[:request_token] = request_token.token
    session[:request_token_secret] = request_token.secret

    # RequestTokenの認可要求
    redirect_to request_token.authorize_url
    return
  end

  def oauth_callback
    consumer = IndexController.consumer
    # 認可済みRequestTokenの取得
    request_token = OAuth::RequestToken.new(
      consumer,
      session[:request_token],
      session[:request_token_secret]
    )

    # AccessTokenの取得
    access_token = request_token.get_access_token(
      {},
      :oauth_token => params[:oauth_token],
      :oauth_verifier => params[:oauth_verifier]
    )

    # AccessTokenをもとに、ServiceProviderサービスを呼び出す
    response = consumer.request(
      :get,
      '/account/verify_credentials.json',
      access_token, { :scheme => :query_string }
    )
    case response
    when Net::HTTPSuccess
      @user_info = JSON.parse(response.body)
      unless @user_info['screen_name']
        flash[:notice] = "Authentication failed"
        redirect_to :action => :index
        return
      end
    else
      RAILS_DEFAULT_LOGGER.error "Failed to get user info via OAuth"
      flash[:notice] = "Authentication failed"
      redirect_to :action => :index
      return
    end
  end
end

----------------------------------------

⑤indexアクションに対応するviewを作成する(C:\Users\yasu\clientapp\twitoauth\app\views\index\index.erbを作成)。

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>OAuth Sample App</title>
</head>
<body>
<a href="/oauth">Authentication at Twitter</a>
</body>
</html>


⑥indexアクションとURLをマッピングする。C:\Users\yasu\clientapp\twitoauth\config\routes.rbに以下を追加する。
----------------------------------
   get "oauthclient" => "index#index"
   get "oauth"       => "index"
   get "oauth_callback" => "index"
----------------------------------

c:\Users\yasu\clientapp\twitoauth>rake routes
   oauthclient GET /oauthclient(.:format)    {:action=>"index", :controller=>"index"}
         oauth GET /oauth(.:format)          {:action=>"oauth", :controller=>"index"}
oauth_callback GET /oauth_callback(.:format) {:action=>"oauth_callback", :controller=>"index"}

⑦Service Provider(Twitter)からAccess Tokenを取得した後のConsumerの戻り先erbを作成する。
(C:\Users\yasu\clientapp\twitoauth\app\views\index\oauth_callback.erb)
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Access succeed!</title>
</head>
<body>
<h1><%= @user_info['name'] %> 's tubuyaki</h1>
<%= @user_info['status']['text'] %>
</body>
</html>

⑧WEBrickを起動する。
C:\Users\yasu\clientapp\twitoauth>rails server -p 3001
=> Booting WEBrick
=> Rails 3.1.2 application starting in development on http://0.0.0.0:3001
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2011-12-02 23:58:49] INFO  WEBrick 1.3.1
[2011-12-02 23:58:52] INFO  ruby 1.9.3 (2011-10-30) [i386-mingw32]
[2011-12-02 23:58:52] INFO  WEBrick::HTTPServer#start: pid=8656 port=3001

⑨「http://openam.yasuyasu.com:3001/oauthclient」にアクセスし、以下の画面が表示されることを確認する。



また、「連携アプリを認証」ボタンを押下した後、最新のつぶやきが表示されることを確認する。