2011年11月8日火曜日

OpenIDプラグインを使ってみる 外部のパブリックなOP(Yahoo!)との認証編

http://memoyasu.blogspot.com/2011/11/openid.html」で構築したRPを使って、OPとOpenID認証を実施し、OpenIDのプロトコルの中身を覗いている。

実機確認の流れは以下になる。

(1)ユーザは、ブラウザからRPにログインする。
(2)ユーザは、ブラウザにOpenID(URL)を入力する。
(3)RPは、OP(認証サーバ)のURLをディカバリ(Discovery)する。
(4)RPは、OPと共有鍵(HMAC)を交換する。
(5)RPは、OPにRequestを送信する。
(6)ユーザは、OPでパスワード認証する。
(7)認証に成功すれば、OPはRPにOpenIDを提示してよいかをユーザに同意を求める。
(8)OPはRPにResponseを送信する。
(9)RPは、OPから受信したレスポンスを検証する。なお、OPから提示されたOpenIDを信頼するか否かはRP次第である。

①「http://localhost:3000/consumer/login」にアクセスする。



②Login欄に、OP(ここではYahoo!)のURLを入力し、Loginボタンを押下する。ログインボタンを押下すると、RPからOPに対して以下のようなプロトコルが流れる。

Redirected to https://open.login.yahooapis.jp/openid/op/auth?openid.assoc_handle
=mTEYfFlb3TVUGrw7LOgeGxlo_4ZouoM8C9k.MTwkb7C0uou_vBLXDIlQwzjR9934QJIJPI6zEgaQtuC
B_kAFYQjtl6sSnYP.f5Tg1GTJv5CNfrCasGsT.8BO3JjQUXlA&openid.claimed_id=http%3A%2F%2
Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.identity=http%3A%2F%2F
specs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.mode=checkid_setup&open
id.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.realm=http%3A%2F%2Flocal
host%3A3000%2Fconsumer&openid.return_to=http%3A%2F%2Flocalhost%3A3000%2Fconsumer
%2Fcomplete

(ポイント1)
実際に、OpenIDのconsumer.rbの中身を見てみると、まずdiscovery_managerが呼ばれている。OPのURLがみつかれば、begin_without_discoveryメソッドが呼ばれる。
-----------------------------------------------------------------------
    def begin(openid_identifier, anonymous=false)
      manager = discovery_manager(openid_identifier)
      service = manager.get_next_service(&method(:discover))

      if service.nil?
        raise DiscoveryFailure.new("No usable OpenID services were found "\
                                   "for #{openid_identifier.inspect}", nil)
      else
        begin_without_discovery(service, anonymous)
      end
    end
--------------------------------------------------------------------------

(ポイント2)
begin_without_discoveryメソッドでは、association_managerがよばれている。
--------------------------------------------------------------------------
    def begin_without_discovery(service, anonymous)
      assoc = association_manager(service).get_association
      checkid_request = CheckIDRequest.new(assoc, service)
      checkid_request.anonymous = anonymous

      if service.compatibility_mode
        rt_args = checkid_request.return_to_args
        rt_args[Consumer.openid1_return_to_nonce_name] = Nonce.mk_nonce
        rt_args[Consumer.openid1_return_to_claimed_id_name] =
          service.claimed_id
      end

      self.last_requested_endpoint = service
      return checkid_request
    end
------------------------------------------------------------------------
associationmanager.rbの中身をみてみると、ここで共有鍵が生成されているようだ。なお、SHA2にも対応している模様。
------------------------------------------------------------------------
    class AssociationManager
      def self.create_session(session_type)
        case session_type
        when 'no-encryption'
          NoEncryptionSession.new
        when 'DH-SHA1'
          DiffieHellmanSHA1Session.new
        when 'DH-SHA256'
          DiffieHellmanSHA256Session.new
        else
          raise ArgumentError, "Unknown association session type: "\
                               "#{session_type.inspect}"
        end
      end
---------------------------------------------------------------------
また、associationmanager.rbのrequest_associationメソッドをみてみると、共有鍵を交換するためのRequestを作成し、OPにPOSTしていることがわかる。
---------------------------------------------------------------------
     def request_association(assoc_type, session_type)
        assoc_session, args = create_associate_request(assoc_type, session_type)

        begin
          response = OpenID.make_kv_post(args, @server_url)
          return extract_association(response, assoc_session)
        rescue HTTPStatusError => why
          Util.log("Got HTTP status error when requesting association: #{why}")
          return nil
        rescue Message::KeyNotFound => why
          Util.log("Missing required parameter in response from "\
                   "#{@server_url}: #{why}")
          return nil

        rescue ProtocolError => why
          Util.log("Protocol error processing response from #{@server_url}: "\
                   "#{why}")
          return nil
        end
      end

      def create_associate_request(assoc_type, session_type)
        assoc_session = self.class.create_session(session_type)
        args = {
          'mode' => 'associate',
          'assoc_type' => assoc_type,
        }

        if !@compatibility_mode
          args['ns'] = OPENID2_NS
        end

        # Leave out the session type if we're in compatibility mode
        # *and* it's no-encryption.
        if !@compatibility_mode ||
            assoc_session.class.session_type != 'no-encryption'
          args['session_type'] = assoc_session.class.session_type
        end

        args.merge!(assoc_session.get_request)
        message = Message.from_openid_args(args)
        return assoc_session, message
      end
---------------------------------------------------------------------

③OPのログイン画面で表示されるので、ID/PASSを入力する。

④ログイン後に、RPに対してOpenID URL(ユーザー識別子)を提供するかの同意画面が表示されるため、「同意する」ボタンを押下する。「同意する」ボタンを押下後に、OPからRPに対して以下のプロトコルが流れる(黒色箇所がOpenID URL)。

Redirected to http://localhost:3000/consumer/welcome
Completed in 427ms (DB: 0) | 302 Found [http://localhost/consumer/complete?open
d.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.mode=id_res&openid.retur
_to=http%3A%2F%2Flocalhost%3A3000%2Fconsumer%2Fcomplete&openid.claimed_id=https
3A%2F%2Fme.yahoo.co.jp%2Fa%2XXXXXXX&o
enid.identity=https%3A%2F%2Fme.yahoo.co.jp%2Fa%2FMNAnzbpeepqPaCIj0I8Z.S0TcK57zj
bk_PtlWn5&openid.assoc_handle=mTEYfFlb3TVUGrw7LOgeGxlo_4ZouoM8C9k.MTwkb7C0uou_v
LXDIlQwzjR9934QJIJPI6zEgaQtuCB_kAFYQjtl6sSnYP.f5Tg1GTJv5CNfrCasGsT.8BO3JjQUXlA&
penid.realm=http%3A%2F%2Flocalhost%3A3000%2Fconsumer&openid.response_nonce=2011
11-08T13%3A18%3A21Z8UhliKInrVWQXk3Spdb5DmwiWQvhrXO9NA--&openid.signed=assoc_han
le%2Cclaimed_id%2Cidentity%2Cmode%2Cns%2Cop_endpoint%2Cresponse_nonce%2Creturn_
o%2Csigned%2Cns.pape%2Cpape.auth_policies%2Cpape.auth_level.ns.nist%2Cpape.auth
level.nist&openid.op_endpoint=https%3A%2F%2Fopen.login.yahooapis.jp%2Fopenid%2F
p%2Fauth&openid.ns.pape=http%3A%2F%2Fspecs.openid.net%2Fextensions%2Fpape%2F1.0
openid.pape.auth_policies=http%3A%2F%2Fschemas.openid.net%2Fpape%2Fpolicies%2F2
07%2F06%2Fnone&openid.pape.auth_level.ns.nist=http%3A%2F%2Fcsrc.nist.gov%2Fpubl
cations%2Fnistpubs%2F800-63%2FSP800-63V1_0_2.pdf&openid.pape.auth_level.nist=0&
penid.sig=4NLJAGAcfhR1BCdaGlPDbkqn1Ks%3D]

⑤OPからRPへ遷移し、RP側で以下のような画面が表示されれば、実機確認は完了である。


0 件のコメント: