Browse files

first commit

  • Loading branch information...
0 parents commit cad79ba2468432367f59b301855878bb16ddf303 @maccman committed Feb 19, 2010
Showing with 151 additions and 0 deletions.
  1. +19 −0 LICENSE
  2. +36 −0 README.markdown
  3. +96 −0 lib/roauth.rb
19 LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2010 Alex MacCaw
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
36 README.markdown
@@ -0,0 +1,36 @@
+Based on SOAuth: http://github.com/tofumatt/SOAuth
+
+ gem install roauth
+
+Example Client:
+
+ uri = 'https://twitter.com/direct_messages.json'
+ oauth = {
+ :consumer_key => "consumer_key",
+ :consumer_secret => "consumer_secret",
+ :token => "access_key",
+ :token_secret => "access_secret"
+ }
+ params = {
+ 'count' => "11",
+ 'since_id' => "5000"
+ }
+ oauth_header = ROAuth.header(oauth, uri, params)
+
+ http_uri = URI.parse(uri)
+ request = Net::HTTP.new(http_uri.host, http_uri.port)
+ request.get(uri.request_uri, {'Authorization', oauth_header})
+
+Example Server:
+
+ request_oauth = ROAuth.parse(request.header['Authorization'])
+
+ # Implementation specific
+ consumer = Consumer.find_by_key(request_oauth['consumer_key'])
+ access_token = AccessToken.find_by_token(request_oauth['token'])
+ oauth = {
+ :consumer_secret => consumer.secret,
+ :token_secret => access_token.secret
+ }
+
+ OAuth.verify(oauth, request.request_uri, request_oauth, params) #=> true/false
96 lib/roauth.rb
@@ -0,0 +1,96 @@
+require "base64"
+require "openssl"
+require "uri"
+
+module ROAuth
+ class UnsupportedSignatureMethod < Exception; end
+ class MissingOAuthParams < Exception; end
+
+ # Supported {signature methods}[http://oauth.net/core/1.0/#signing_process];
+ SIGNATURE_METHODS = {"HMAC-SHA1" => OpenSSL::Digest::Digest.new("sha1")}
+ OAUTH_PARAMS = [:consumer_key, :token, :signature_method, :version, :nonce, :timestamp, :signature]
+
+ # Return an {OAuth "Authorization" HTTP header}[http://oauth.net/core/1.0/#auth_header] from request data
+ def header(oauth, uri, params = {}, http_method = :get)
+ oauth[:signature_method] ||= "HMAC-SHA1"
+ oauth[:version] ||= "1.0" # Assumed version, according to the spec
+ oauth[:nonce] ||= Base64.encode64(OpenSSL::Random.random_bytes(32)).gsub(/\W/, '')
+ oauth[:timestamp] ||= Time.now.to_i
+
+ sig_params = params.dup
+ sig_params.merge!(oauth_params(oauth))
+
+ oauth.merge!(:signature => signature(oauth, uri, params, http_method))
+
+ %{OAuth } + oauth_params(oauth).map {|key, value| [key, value].join("=") }.join(", ")
+ end
+
+ def parse(header)
+ header = header.dup
+ header = header.gsub!(/^OAuth\s/, "")
+ header = header.split(", ")
+ header.inject({}) {|hash, item|
+ key, value = item.split("=")
+ key.gsub!(/^oauth_/, "")
+ hash[key.to_sym] = unescape(value)
+ hash
+ }
+ end
+
+ def verify(oauth, header, uri, params = {}, http_method = :get)
+ header = header.is_a?(String) ? parse(header) : header.dup
+
+ client_signature = header.delete(:signature)
+ oauth[:consumer_key] ||= header[:consumer_key]
+ oauth[:token] ||= header[:token]
+
+ sig_params = params.dup
+ sig_params.merge!(oauth_params(header))
+
+ client_signature == signature(oauth, uri, sig_params, http_method)
+ end
+
+ protected
+ def oauth_params(oauth)
+ oauth = oauth.to_a.select {|key, value|
+ OAUTH_PARAMS.include?(key)
+ }
+ oauth.inject({}) {|hash, (key, value)|
+ hash["oauth_#{key}"] = escape(value)
+ hash
+ }
+ end
+
+ def signature(oauth, uri, params, http_method = :get)
+ sig_base = http_method.to_s.upcase + "&" + escape(uri) + "&" + normalize(params)
+ digest = SIGNATURE_METHODS[oauth[:signature_method]]
+ secret = "#{escape(oauth[:consumer_secret])}&#{escape(oauth[:token_secret])}"
+
+ Base64.encode64(OpenSSL::HMAC.digest(digest, secret, sig_base)).chomp.gsub(/\n/, "")
+ end
+
+ # Escape characters in a string according to the {OAuth spec}[http://oauth.net/core/1.0/]
+ def escape(value)
+ URI::escape(value.to_s, /[^a-zA-Z0-9\-\.\_\~]/) # Unreserved characters -- must not be encoded
+ end
+
+ def unescape(value)
+ URI::unescape(value)
+ end
+
+ # Normalize a string of parameters based on the {OAuth spec}[http://oauth.net/core/1.0/#rfc.section.9.1.1]
+ def normalize(params)
+ params.sort.map do |key, values|
+ if values.is_a?(Array)
+ # Multiple values were provided for a single key
+ # in a hash
+ values.sort.collect do |v|
+ [escape(key), escape(v)] * "%3D"
+ end
+ else
+ [escape(key), escape(values)] * "%3D"
+ end
+ end * "%26"
+ end
+ extend self
+end

0 comments on commit cad79ba

Please sign in to comment.