When we decide to use Oauth 1 for authentication, we need to answer the first question: ‘How is the signature generated?’. It took me 3 days to work it out before I could get data from our client’s NETSUITE account.

I want to share with you my first raw Ruby code and hope it will save you a bit of time.

# NETSUITE Account vars
client_key = 'xxx'
client_secret = 'xxx'
token_id = 'xxx'
token_secret = 'xxx'
account_id = 'xxx'

# OAuth vars
oauth_signature_method = 'HMAC-SHA256'
oauth_timestamp = Time.now.to_i
oauth_nonce = ERB::Util.url_encode(SecureRandom.base64)
http_request_method = 'POST' #'GET'
version = '1.0'

# Init url
url_text = "https://#{account_id.downcase}.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql"
# URL params
params = { limit: 5 }
params = {}
# We need to percent encode URL params
params_text = params.map {|k, v| "#{k}=#{ERB::Util.url_encode(v.to_s)}" }.join("\&")
url = URI(url_text)
url.query = params_text

# Init request parameters for signature base string
# The key name must be in alphabet order
request_params = {
  oauth_consumer_key: client_key,
  oauth_nonce: oauth_nonce,
  oauth_signature_method: oauth_signature_method,
  oauth_timestamp: oauth_timestamp,
  oauth_token: token_id,
  oauth_version: version
}

request_params_ordered = request_params.dup

# We need to put all URL params to the request parameters of the signature base string
params.each do |k, v|
  request_params_ordered[k] = ERB::Util.url_encode(v.to_s) # Percent encode
end

request_params_ordered = request_params_ordered.sort_by { |k, _| k }
normalized_request_parameters = request_params_ordered.map {|k, v| "#{k}=#{v.to_s}" }.join("\&")
# Init base string for signature
base_string = "#{http_request_method}\&#{ERB::Util.url_encode(url_text)}\&#{ERB::Util.url_encode(normalized_request_parameters)}"
# Init secret key for signature
secret_key = "#{client_secret}\&#{token_secret}"
# Generate signature
hmac_signature = ERB::Util.url_encode(Base64.strict_encode64(OpenSSL::HMAC.digest('sha256', secret_key, base_string)))

# Init Authorization header
authorization = [
  "realm=\"#{account_id.upcase}\"",
  "oauth_consumer_key=\"#{client_key}\"",
  "oauth_token=\"#{token_id}\"",
  "oauth_signature_method=\"#{oauth_signature_method}\"",
  "oauth_timestamp=\"#{oauth_timestamp}\"",
  "oauth_nonce=\"#{oauth_nonce}\"",
  "oauth_version=\"#{version}\"",
  "oauth_signature=\"#{hmac_signature}\""
].join(',')

request_body = {
  q: "SELECT * FROM account"
}

https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
method_class_name = "Net::HTTP::#{http_request_method.titlecase}".constantize
request = method_class_name.new(url)
# request = Net::HTTP::Post.new(url)
# request = Net::HTTP::Get.new(url)
request["Authorization"] = "OAuth #{authorization}"
request["prefer"] = "transient"
request["Content-Type"] = "application/json"
request.body = JSON.dump(request_body)

response = https.request(request)
puts response.read_body

Happy coding!

References: https://developer.twitter.com/en/docs/authentication/oauth-1-0a/creating-a-signature

Categories: Ruby on Rails