Authentication filter for handling OAuth negotiation. Used in WWWAuth.
CAUTION: This impl only support '#7 Accessing Protected Resources' in OAuth Core 1.0 spec for now. You need to obtain Access token and Access secret by yourself.
CAUTION: This impl does NOT support OAuth Request Body Hash spec for now. oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html
Authentication scheme.
Creates new DigestAuth filter.
# File lib/httpclient/auth.rb, line 802 def initialize super @config = nil # common config @auth = {} # configs for each site @challenge = {} @nonce_count = 0 @signature_handler = { 'HMAC-SHA1' => method(:sign_hmac_sha1) } @scheme = "OAuth" end
Challenge handler: remember URL for response.
challenge() in OAuth handler always returns false to avoid connection retry which should not work in OAuth authentication context. This method just remember URL (nil means 'any') for the next connection. Normally OAuthClient handles this correctly but see how it uses when you need to use this class directly.
# File lib/httpclient/auth.rb, line 875 def challenge(uri, param_str = nil) synchronize { if uri.nil? @challenge[nil] = true else @challenge[urify(uri)] = true end false } end
# File lib/httpclient/auth.rb, line 797 def escape(str) self.class.escape(str) end
Response handler: returns credential. It sends cred only when a given uri is;
child page of challengeable(got *Authenticate before) uri and,
child page of defined credential
# File lib/httpclient/auth.rb, line 856 def get(req) target_uri = req.header.request_uri synchronize { return nil unless @challenge[nil] or @challenge.find { |uri, ok| Util.uri_part_of(target_uri, uri) and ok } config = do_get_config(target_uri) || @config return nil unless config calc_cred(req, config) } end
Get authentication credential.
# File lib/httpclient/auth.rb, line 846 def get_config(uri = nil) synchronize { do_get_config(uri) } end
Resets challenge state. Do not send '*Authorization' header until the server sends '*Authentication' again.
# File lib/httpclient/auth.rb, line 816 def reset_challenge synchronize do @challenge.clear end end
Set authentication credential. You cannot set OAuth config via HTTPClient::WWWAuth#set_auth. Use OAuth#config=
# File lib/httpclient/auth.rb, line 824 def set(*args) # not supported end
have we marked this as set - ie that it's valid to use in this context?
# File lib/httpclient/auth.rb, line 829 def set? true end
Set authentication credential.
# File lib/httpclient/auth.rb, line 834 def set_config(uri, config) synchronize do if uri.nil? @config = config else uri = Util.uri_dirname(urify(uri)) @auth[uri] = config end end end
# File lib/httpclient/auth.rb, line 899 def calc_cred(req, config) header = {} header['oauth_consumer_key'] = config.consumer_key header['oauth_signature_method'] = config.signature_method header['oauth_timestamp'] = config.debug_timestamp || Time.now.to_i.to_s header['oauth_nonce'] = config.debug_nonce || generate_nonce() header['oauth_token'] = config.token if config.token header['oauth_version'] = config.version if config.version header['oauth_callback'] = config.callback if config.callback header['oauth_verifier'] = config.verifier if config.verifier header['oauth_session_handle'] = config.session_handle if config.session_handle signature = sign(config, header, req) header['oauth_signature'] = signature # no need to do but we should sort for easier to test. str = header.sort_by { |k, v| k }.map { |k, v| encode_header(k, v) }.join(', ') if config.realm str = %Q(realm="#{config.realm}", ) + str end str end
# File lib/httpclient/auth.rb, line 948 def create_base_string(config, header, req) params = encode_param(header) query = req.header.request_query if query and HTTP::Message.multiparam_query?(query) params += encode_param(query) end # captures HTTP Message body only for 'application/x-www-form-urlencoded' if req.header.contenttype == 'application/x-www-form-urlencoded' and req.http_body.size params += encode_param(HTTP::Message.parse(req.http_body.content)) end uri = req.header.request_uri if uri.query params += encode_param(HTTP::Message.parse(uri.query)) end if uri.port == uri.default_port request_url = "#{uri.scheme.downcase}://#{uri.host}#{uri.path}" else request_url = "#{uri.scheme.downcase}://#{uri.host}:#{uri.port}#{uri.path}" end [req.header.request_method.upcase, request_url, params.sort.join('&')].map { |e| escape(e) }.join('&') end
# File lib/httpclient/auth.rb, line 888 def do_get_config(uri = nil) if uri.nil? @config else uri = urify(uri) Util.hash_find_value(@auth) { |cand_uri, cred| Util.uri_part_of(uri, cand_uri) } end end
# File lib/httpclient/auth.rb, line 927 def encode_header(k, v) %Q(#{escape(k.to_s)}="#{escape(v.to_s)}") end
# File lib/httpclient/auth.rb, line 931 def encode_param(params) params.map { |k, v| [v].flatten.map { |vv| %Q(#{escape(k.to_s)}=#{escape(vv.to_s)}) } }.flatten end
# File lib/httpclient/auth.rb, line 920 def generate_nonce @nonce_count += 1 now = "%012d" % Time.now.to_i pk = Digest::MD5.hexdigest([@nonce_count.to_s, now, self.__id__, Process.pid, rand(65535)].join)[0, 32] [now + ':' + pk].pack('m*').chop end
# File lib/httpclient/auth.rb, line 939 def sign(config, header, req) base_string = create_base_string(config, header, req) if handler = config.signature_handler[config.signature_method] || @signature_handler[config.signature_method.to_s] handler.call(config, base_string) else raise ConfigurationError.new("Unknown OAuth signature method: #{config.signature_method}") end end
# File lib/httpclient/auth.rb, line 972 def sign_hmac_sha1(config, base_string) unless SSLEnabled raise ConfigurationError.new("openssl required for OAuth implementation") end key = [escape(config.consumer_secret.to_s), escape(config.secret.to_s)].join('&') digester = OpenSSL::Digest::SHA1.new [OpenSSL::HMAC.digest(digester, key, base_string)].pack('m*').chomp end