JugemKey認証API Ruby版モジュール

comatta.jpにJugemKey認証APIを導入する為にRuby版モジュールを書きました。PerlPHPは公式版があるが、Rubyは無いので公開してみます。なお、後述しますが、公式版より機能は少ないです。

使い方は、はてな認証API Ruby版モジュールと殆ど同じです。なお、ソースも殆ど同じです。

はてな認証API Ruby 版モジュール作りました - 2nd life

ただし、はてな認証API

hatena_auth = Hatena::API::Auth.new(:api_key => API_KEY, :secret => SECRET)

の部分が

jugem_auth = Jugem::Auth.new(:api_key => API_KEY, :secret => SECRET, 
                             :callback_url => CALLBACK_URL, :perms => PERMS)

となります。これはJugemKeyのインターフェイスはてな認証APIと違うためです。

JugemKey認証APIが想定する利用法は最初にTokenを取得した後、そのTokenを使って個別のAPIにアクセスするようです。ただし、現在利用可能なAPIはユーザ名取得のみなので、loginメソッドで直接ユーザ名を返却しています。従って、Tokenを取得できる公式版より機能が少ないです。

なお、ライセンスはMITライセンスとします。

require 'net/http'
require 'openssl'
require 'rexml/document'
require 'uri'

module Jugem
  class AuthError < RuntimeError;end
  class Auth
    API_URI = 'https://secure.jugemkey.jp/'
    AUTH_URI = 'http://api.jugemkey.jp/api/auth/token'
    VERSION = '0.0.1'
    
    attr_accessor :api_key, :secret, :callback_url, :perms
    def initialize(options = {})
      @api_key = options[:api_key]
      @secret  = options[:secret]
      @callback_url = options[:callback_url]
      @perms = options[:perms]
    end
    
    def uri_to_login
      uri = URI.parse API_URI
      uri.query = query_with_api_sig
      uri
    end
    
    def login(frob)
      time = Time.now.getutc.xmlschema
      header = {
        'X-JUGEMKEY-API-CREATED' => time,
        'X-JUGEMKEY-API-KEY' => @api_key,
        'X-JUGEMKEY-API-FROB' => frob,
        'X-JUGEMKEY-API-SIG' => auth_sig(frob, time),
        'User-Agent' => "#{self.class}/#{VERSION} - Ruby"
      }
      uri = URI.parse(AUTH_URI)

      Net::HTTP.start(uri.host) do |http|
        response = http.get(uri.path, header)
        unless response.code.to_i == 200
          response.body =~ /<error>([^<]*)<\/error>/i
          raise AuthError.new($1)
        end
        doc = REXML::Document.new(response.body)
        login = doc.elements["entry/title"].text
      end
    end
    
    private
    def api_sig
      OpenSSL::HMAC.hexdigest( OpenSSL::Digest::SHA1.new, 
                               @secret, @api_key + @callback_url + @perms) 
    end
    
    def auth_sig(frob, time)
      OpenSSL::HMAC.hexdigest( OpenSSL::Digest::SHA1.new, 
                               @secret, @api_key + time + frob) 
    end

    def query_with_api_sig
      query = [
               [:mode, 'auth_issue_frob'],
               [:api_key, @api_key],
               [:perms,  @perms],
               [:callback_url, URI.escape(@callback_url)],
               [:api_sig, api_sig],
              ]
      query.map {|i| i.join '=' }.join('&')
    end
  end
end