Module: Pesapal::Oauth
- Defined in:
- lib/pesapal/oauth.rb
Class Method Summary collapse
-
.generate_encoded_params_query_string(params = {}) ⇒ Object
generate query string from parameters hash.
-
.generate_nonce(length) ⇒ Object
generate oauth nonce.
-
.generate_oauth_signature(http_method, absolute_url, params, consumer_secret, token_secret = nil) ⇒ Object
generate the oauth signature using hmac-sha1 algorithm.
-
.generate_signable_encoded_params_query_string(params = {}) ⇒ Object
generate query string from signable parameters hash.
-
.generate_signature_base_string(http_method, absolute_url, params) ⇒ Object
generate the oauth signature.
-
.generate_signing_key(consumer_secret, token_secret = nil) ⇒ Object
generate signing key.
-
.normalized_request_uri(absolute_url) ⇒ Object
normalize request absolute URL.
-
.parameter_encode(string) ⇒ Object
percentage encode value as per the oauth spec.
Class Method Details
.generate_encoded_params_query_string(params = {}) ⇒ Object
generate query string from parameters hash
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/pesapal/oauth.rb', line 6 def Oauth.generate_encoded_params_query_string(params = {}) # 1) percent encode every key and value that will be signed # 2) sort the list of parameters alphabetically by encoded key # 3) for each key/value pair # - append the encoded key to the output string # - append the '=' character to the output string # - append the encoded value to the output string # 4) if there are more key/value pairs remaining, append a '&' # character to the output string # the oauth spec says to sort lexigraphically, which is the default # alphabetical sort for many libraries. in case of two parameters # with the same encoded key, the oauth spec says to continue # sorting based on value queries = [] params.each do |k,v| queries.push "#{self.parameter_encode(k.to_s)}=#{self.parameter_encode(v.to_s)}" end # parameters are sorted by name, using lexicographical byte value # ordering queries.sort! queries.join('&') end |
.generate_nonce(length) ⇒ Object
generate oauth nonce
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/pesapal/oauth.rb', line 33 def Oauth.generate_nonce(length) # the consumer shall then generate a nonce value that is unique for # all requests with that timestamp. a nonce is a random string, # uniquely generated for each request. the nonce allows the service # provider to verify that a request has never been made before and # helps prevent replay attacks when requests are made over a non- # secure channel (such as http). chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ0123456789' nonce = '' length.times { nonce << chars[rand(chars.size)] } "#{nonce}" end |
.generate_oauth_signature(http_method, absolute_url, params, consumer_secret, token_secret = nil) ⇒ Object
generate the oauth signature using hmac-sha1 algorithm
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/pesapal/oauth.rb', line 50 def Oauth.generate_oauth_signature(http_method, absolute_url, params, consumer_secret, token_secret = nil) # the signature is calculated by passing the signature base string # and signing key to the hmac-sha1 hashing algorithm. the output of # the hmac signing function is a binary string. this needs to be # base64 encoded to produce the signature string. # for pesapal flow we don't have a token secret to we will set as # nil and the appropriate action will be taken as per the oauth # spec. see notes in the method that creates signing keys # prepare the values we need digest = OpenSSL::Digest::Digest.new('sha1') signature_base_string = self.generate_signature_base_string(http_method, absolute_url, params) signing_key = self.generate_signing_key(consumer_secret, token_secret) hmac = OpenSSL::HMAC.digest(digest, signing_key, signature_base_string) Base64.encode64(hmac).chomp end |
.generate_signable_encoded_params_query_string(params = {}) ⇒ Object
generate query string from signable parameters hash
71 72 73 74 75 76 77 78 |
# File 'lib/pesapal/oauth.rb', line 71 def Oauth.generate_signable_encoded_params_query_string(params = {}) # oauth_signature parameter MUST be excluded, assumes it was already # initialized by calling set_parameters params.delete(:oauth_signature) self.generate_encoded_params_query_string params end |
.generate_signature_base_string(http_method, absolute_url, params) ⇒ Object
generate the oauth signature
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/pesapal/oauth.rb', line 81 def Oauth.generate_signature_base_string(http_method, absolute_url, params) # three values collected so far must be joined to make a single # string, from which the signature will be generated. This is # called the signature base string by the OAuth specification # step 1: convert the http method to uppercase http_method = http_method.upcase # step 2: percent encode the url url_encoded = self.parameter_encode(self.normalized_request_uri(absolute_url)) # step 3: percent encode the parameter string parameter_string_encoded = self.parameter_encode(self.generate_signable_encoded_params_query_string params) # the signature base string should contain exactly 2 ampersand '&' # characters. The percent '%' characters in the parameter string should # be encoded as %25 in the signature base string "#{http_method}&#{url_encoded}&#{parameter_string_encoded}" end |
.generate_signing_key(consumer_secret, token_secret = nil) ⇒ Object
generate signing key
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/pesapal/oauth.rb', line 104 def Oauth.generate_signing_key(consumer_secret, token_secret = nil) # the signing key is simply the percent encoded consumer secret, # followed by an ampersand character '&', followed by the percent # encoded token secret # note that there are some flows, such as when obtaining a request # token, where the token secret is not yet known. In this case, the # signing key should consist of the percent encoded consumer secret # followed by an ampersand character '&' # "#{@credentials[:consumer_secret]}" consumer_secret_encoded = self.parameter_encode(consumer_secret) token_secret_encoded = "" unless token_secret.nil? token_secret_encoded = self.parameter_encode(token_secret) end "#{consumer_secret_encoded}&#{token_secret_encoded}" end |
.normalized_request_uri(absolute_url) ⇒ Object
normalize request absolute URL
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/pesapal/oauth.rb', line 127 def Oauth.normalized_request_uri(absolute_url) # the signature base string includes the request absolute url, tying # the signature to a specific endpoint. the url used in the # signature base string must include the scheme, authority, and # path, and must exclude the query and fragment as defined by # [rfc3986] section 3. # if the absolute request url is not available to the service # provider (it is always available to the consumer), it can be # constructed by combining the scheme being used, the http host # header, and the relative http request url. if the host header is # not available, the service provider should use the host name # communicated to the consumer in the documentation or other means. # the service provider should document the form of url used in the # signature base string to avoid ambiguity due to url normalization. # unless specified, url scheme and authority must be lowercase and # include the port number; http default port 80 and https default # port 443 must be excluded. u = URI.parse(absolute_url) scheme = u.scheme.downcase host = u.host.downcase path = u.path port = u.port port = (scheme == 'http' && port != 80) || (scheme == 'https' && port != 443) ? ":#{port}" : "" path = (path && path != '') ? path : '/' "#{scheme}://#{host}#{port}#{path}" end |
.parameter_encode(string) ⇒ Object
percentage encode value as per the oauth spec
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/pesapal/oauth.rb', line 162 def Oauth.parameter_encode(string) # all parameter names and values are escaped using the [rfc3986] # percent-encoding (%xx) mechanism. characters not in the unreserved # character set ([rfc3986] section 2.3) must be encoded. characters # in the unreserved character set must not be encoded. hexadecimal # characters in encodings must be upper case. text names and values # must be encoded as utf-8 octets before percent-encoding them per # [rfc3629]. # reserved character regexp, per section 5.1 reserved_characters = /[^a-zA-Z0-9\-\.\_\~]/ URI::escape(string.to_s.force_encoding(Encoding::UTF_8), reserved_characters) end |