iOS で Digest認証するコードを書きました。

サンプルコードの作成は頼まれて作成しただけです。 折角なので、簡単な説明を 記事にしておきます。

サンプルコードはこちら

テストサーバ構築

まずは動作確認をできるようにしないといけないので Digest認証 をするためのウェブサーバがないと困ります。Ruby の rack を使いました。

Digest認証をするには Rack のミドルウェア Rack::Auth::Digest::MD5 を使用しました。 Digest認証は ハッシュ化アルゴリズムの選択できるようになっているので、ミドルウェアはこのような名前になっているようです。 Rackのソースコードみにいったら、最初気づかなくて困りました。

config.ru は以下のように書きました。

use Rack::Auth::Digest::MD5, "auth", '' do |username|
  "password"
end

run proc { [200, {'Content-Type' => 'text/html'}, ['hoge']] }

use の第三引数は opaque となります。デフォルトでは nil で、設定しないと動きません。はまりました。

蛇足ですが、 use の仕組みをよくしらなかったのでソースコードをちら見しました。 lib/builder.rb に実装があります。

    def use(middleware, *args, &block)
      if @map
        mapping, @map = @map, nil
        @use << proc { |app| generate_map app, mapping }
      end
      @use << proc { |app| middleware.new(app, *args, &block) }
    end

@map が nil の場合は middleware.new する処理が割り込むだけですね。引数はまるまる渡しています。

use する時の引数は 使用するミドルウェア の initialize メソッドをみればよいことがわかります。 Digest認証の場合は (ソースコード)

        def initialize(app, realm=nil, opaque=nil, &authenticator)
          @passwords_hashed = nil
          if opaque.nil? and realm.respond_to? :values_at
            realm, opaque, @passwords_hashed = realm.values_at :realm, :opaque, :passwords_hashed
          end
          super(app, realm, &authenticator)
          @opaque = opaque
        end

第2引数、第3引数、ブロックを用意すればよさそうです。rdoc にも書いてありますが、block は username を引数にうけとります。 確認しないと心配なら valid_digest? で利用されています。

        def valid_digest?(auth)
          pw = @authenticator.call(auth.username)
          pw && digest(auth, pw) == auth.response
        end

iOS側

使い方は サンプルコードの README.md にかいています。

AFNetworking は iOS と Mac OS X で利用できるライブラリで block や NSOperation を利用して ネットワークの処理がかけるライブラリみたいです。

Digjest 認証をしたい場合は `AFHTTPRequestOperation#setAuthenticationChallengeBlock に認証が必要なときに呼び出される処理を block で登録しておけばよいようです。

なので、あらかじめ登録しておいた username と password を利用して、 認証する処理を登録した状態の AFHTTPRequestOperation を返すようなファクトリを用意してやりました。

あと雑談ですが、

block 内で self を利用したい場合は JavaScript の影響で that にして使っています。 一般的な変数名はなんなのでしょうか。

参考リンク