Skip to content

Instantly share code, notes, and snippets.

@hannestyden
Created July 14, 2009 09:37
Show Gist options
  • Save hannestyden/146876 to your computer and use it in GitHub Desktop.
Save hannestyden/146876 to your computer and use it in GitHub Desktop.
Reference implementation for OAuth request signing
# Signing an OAuth request
# Hannes Tydén, hannes@soundcloud.com, 2009-07-14
# Updated at 2009-09-03 to support OAuth 1.0a
# This is designed to be executed in an editor evaluating expressions and outputting the result inline.
# The spec:
# http://oauth.net/core/1.0#signing_process
# Read more:
# http://www.hueniverse.com/hueniverse/2008/10/beginners-gui-1.html
require 'rubygems'
require 'cgi'
require 'uri'
require 'base64'
require 'hmac/sha1'
# Constants
ENCODING = {
:form => 'application/x-www-form-urlencoded',
:multipart => 'multipart/form-data'
}
### SETUP
# Change these variables
use_oauth_10a = false
oauth_callback = nil
####
## Consumer specific
# FROM EXAMPLE
consumer_key = "dpf43f3p2l4k3l03"
consumer_secret = "kd94hf93k423kf44"
####
## Token specific
# FROM EXAMPLE
token_key = "nnch734d00sl2jdk"
token_secret = "pfkkdhi9sl3r4s00"
####
## Request specific
request_method = 'GET'
# FROM EXAMPLE
request_url = "http://photos.example.net/photos"
request_content_type = ENCODING[:form]
oauth_nonce = "kllo9940pd9333jh"
oauth_timestamp = "1191242096"
# FROM EXAMPLE
request_uri_parameters = {
"size" => "original",
"file" => "vacation.jpg"
}
request_body_parameters = {
}
# Setup is done, please proceed
### /SETUP
### GENERIC CODE
# Just change below this point if you want to change behavior.
oauth_parameters = {
'oauth_consumer_key' => consumer_key,
'oauth_nonce' => oauth_nonce,
'oauth_timestamp' => oauth_timestamp,
'oauth_signature_method' => "HMAC-SHA1",
'oauth_version' => "1.0"
}
unless token_key == ''
oauth_parameters.merge!(
'oauth_token' => token_key
)
end
if use_oauth_10a
oauth_parameters.merge!(
'oauth_callback' => oauth_callback || 'oob'
)
end
# Merge all parameters together
merged_parameters = request_uri_parameters.merge(oauth_parameters)
# Only use parameters sent in body when encoding is "application/x-www-form-urlencoded" and HTTP method is POST
# See http://groups.google.com/group/oauth/browse_thread/thread/42ef5fecc54a7e9a
if request_content_type == ENCODING[:form] && request_method == 'POST'
merged_parameters.merge!(request_body_parameters)
end
# 9.1. Signature Base String
# 9.1.1. Normalize Request Parameters
normalized_request_parameters = merged_parameters.map { |name, value| "#{CGI.escape(name)}=#{CGI.escape(value)}" }.sort.join("&")
normalized_request_parameters # => "file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original"
# 9.1.2. Construct Request URL
uri = URI.parse(request_url)
normalizied_request_url = "#{uri.scheme}://#{uri.host}#{uri.path}".downcase
normalizied_request_url # => "http://photos.example.net/photos"
# 9.1.3. Concatenate Request Elements
normalizied_request_method = request_method.upcase
signature_base_string = [ normalizied_request_method, normalizied_request_url, normalized_request_parameters ].map { |element| CGI.escape(element) }.join("&")
signature_base_string # => "GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal"
# 9.2. HMAC-SHA1
# 9.2.1. Generating Signature
concatenated_secrets = "#{consumer_secret}&#{token_secret}"
concatenated_secrets # => "kd94hf93k423kf44&pfkkdhi9sl3r4s00"
hmac_sha1_digest = HMAC::SHA1.digest(concatenated_secrets, signature_base_string)
oauth_signature = Base64.encode64(hmac_sha1_digest).strip
oauth_signature # => "tR3+Ty81lMeYAr/Fid0kMTYa/WM="
# Signed request
uri = URI.parse(request_url)
uri_query = uri.query.gsub(/\&$/, "") # Remove trailing ampersands
uri.query = [ uri_query, normalized_request_parameters, "oauth_signature=#{CGI.escape(oauth_signature)}" ].reject { |s| s.to_s == "" }.join("&")
signed_request_url = uri.to_s
signed_request_url # => "http://photos.example.net/photos?file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original&oauth_signature=tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D"
# "http://photos.example.net/photos?file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original&oauth_signature=tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment