4.3. OAuth¶
4.3.1. OAuth Overview¶
This page allows you to generate OAuth signatures using a known good OAuth 1.0a library, https://oauth.net/1/. This way you can quickly identify if 403 Forbidden error received from the MoneyNetint API server is due to a bad signature or another issue.
4.3.2. OAuth Implementation¶
Key:
HMAC key value is the concatenation of consumer secret (Merchant control key) + & + token secret (empty string).
For example, it will look as follows:
F9F65098-1111-1111-1111-621611111111&
Request body and data string to sign:
Parameters in request body must be arranged in lexicographical order.
Unique random nonce and current timestamp are generated by merchant.
OAuth parameters must be included in request body (oauth_consumer_key, oauth_nonce, oauth_signature_method, oauth_timestamp, oauth_version).
Whole request is used as data string to calculate HMAC-SHA1 signature.
URL and parameters in data string must be percent-encoded and have the following structure:
POST&URL¶meters
Headers and signature:
Authorization header must start from Authorization: OAuth, have oauth_signature and the same OAuth parameters and values as were included in request body (oauth_consumer_key, oauth_nonce, oauth_signature_method, oauth_timestamp, oauth_version).
Merchant login is used as oauth_consumer_key in data string and authorization header, but it is not used as a part of the signing HMAC key.
HMAC-SHA1 signature in binary format must be Base64 and percent-encoded. The signature is sent in request header named ‘Authorization’.
For example, it will look as follows:
9bSeUoR5yJSSJ4KPhotT%2BofEHSQ%3D
Authorization header example:
OAuth realm="",
oauth_version="1.0",
oauth_signature_method="HMAC-SHA1",
oauth_consumer_key="merchantlogin",
oauth_timestamp="1513785920",
oauth_nonce="EqINVv5rkhx",
oauth_signature="9bSeUoR5yJSSJ4KPhotT%2BofEHSQ%3D"
Java implementation example¶
Here is an example of how to make a signed request in Java with help of Scribe library:
public String doPost(String url, Map<String, String>parameters) throws ConnectionIOException, ConnectionTimeoutException {
OAuthConfig config = new OAuthConfig(apiToken, merchantControlKey, OAuthConstants.OUT_OF_BAND,
SignatureType.Header, null,null);
OAuthService service = new OAuth10aServiceImpl(new HmacSha1Mapi(), config);
OAuthRequest request = new OAuthRequest(Verb.POST,url);
for (Map.Entry < String,String >;entry :parameters.entrySet()){
request.addBodyParameter(entry.getKey(), entry.getValue());
} // empty token for 'two-legged'
OAuth Token token = new Token("", "");
service.signRequest(token, request);
Response response = request.send();
return response.getBody();
}
private static class Mapi extends DefaultApi {
@Override
public String getRequestTokenEndpoint() {
return null; // not used
} @Override public String getAccessTokenEndpoint() {
return null; //not used
}
@Override
public String getAuthorizationUrl(Token requestToken) {
return null; // not used
}
@Override
public SignatureService getSignatureService() {
return new HMACSha1SignatureService();
}
}
C# implementation example¶
This example shows the C# OAuth implementation for Payout integration:
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Net;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string apiUrl = "https://pne-sandbox.moneynetint.com/paynet/api/v2/payout/123";
string consumerKey = "merchantlogin";
string consumerSecret = "1EF4D28C-1111-2222-3333-444487505555";
/* for oauth_timestamp */
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
string timestamp = Convert.ToInt64(ts.TotalSeconds).ToString();
/* for oauth_nonce */
/* (obviously you wouldn't create a new Random class every time!) */
string nonce = new Random().Next(123400, 9999999).ToString();
//oauth parameters, need sort alpabetical, use SortedDictionary
IDictionary<string,string> oauthParams = new SortedDictionary<string, string>();
oauthParams.Add("oauth_consumer_key",consumerKey);
oauthParams.Add("oauth_nonce",nonce);
oauthParams.Add("oauth_timestamp",timestamp);
oauthParams.Add("oauth_signature_method","HMAC-SHA1");
oauthParams.Add("oauth_version","1.0");
//Prepare POST request params , add oauth params
IDictionary<string,string> requestParameters = new SortedDictionary<string, string>(oauthParams);
requestParameters.Add("account_number", "1234567890");
requestParameters.Add("amount", "100");
requestParameters.Add("bank_branch", "test_branch");
requestParameters.Add("bank_name", "test_bank");
requestParameters.Add("client_orderid", "12345");
requestParameters.Add("currency", "USD");
StringBuilder builder = new StringBuilder();
foreach (KeyValuePair<string,string> pair in requestParameters)
{
if (builder.Length > 0)
{
builder.Append("&");
}
//ATTENTION, call Uri.EscapeDataString
builder.Append(Uri.EscapeDataString(pair.Key)).Append("=").Append(Uri.EscapeDataString(pair.Value));
}
string normalizedParameters = builder.ToString();
//create signature base string
builder = new StringBuilder();
builder.Append("POST").Append("&");
builder.Append(Uri.EscapeDataString(apiUrl)).Append("&");
builder.Append(Uri.EscapeDataString(normalizedParameters));
string signatureBaseString = builder.ToString();
// calculate signature, always add & to end consumerSecret
byte[] signatureKeyBytes = Encoding.UTF8.GetBytes(consumerSecret + "&");
HMACSHA1 sha1 = new HMACSHA1(signatureKeyBytes);
/* generate the signature and add it to our parameters */
byte[] baseStringBytes = Encoding.UTF8.GetBytes(signatureBaseString);
byte[] baseStringHash = sha1.ComputeHash(baseStringBytes);
string base64StringHash = Convert.ToBase64String(baseStringHash);
oauthParams.Add("oauth_signature", base64StringHash);
//create oauth Header
string authHeader = createOAuthHeader(oauthParams);
/* we are ready to send the request! */
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiUrl);
request.Headers.Add("Authorization", authHeader);
var data = Encoding.UTF8.GetBytes(normalizedParameters);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
request.Timeout = 30*1000;
request.UserAgent = "DotNet";
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
var response = (HttpWebResponse) request.GetResponse();
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
System.Console.WriteLine(responseString);
}
private static string createOAuthHeader(IDictionary<string, string> oauthParams)
{
StringBuilder builder = new StringBuilder("OAuth realm=\"\"");
foreach (KeyValuePair<string, string> pair in oauthParams)
{
if (builder.Length > 0)
{
builder.Append(",");
}
//ATTENTION, call Uri.EscapeDataString
builder.Append(string.Format("{0}=\"{1}\"", Uri.EscapeDataString(pair.Key), Uri.EscapeDataString(pair.Value)));
}
return builder.ToString();
}
}
}
4.3.3. Debug¶
To reproduce your API call, input all of the data from your original request, including the authentication tokens. Don’t forget to set the nonce and timestamp to the values you used. An OAuth signed URL should match regardless of the generating library. If the signatures differ, you know there is a bug in your OAuth signature code.
normalized parameters |
---|
signature base string |
---|
signature |
---|
authorization header |
---|
Curl |
---|
|
4.3.4. Libraries¶
We use a simplified flow in which there’s no external authenticator, and there’s no access token and its secret; only the last step of the flow is used.
Merchant login is specified as client identifier (aka consumer key or API token).
A lot of OAuth 1.0a libraries for various programming languages can be found here (last checked 21.12.2017).
Some libraries which may be used: