Validating a call signature

Eloqua signs all outgoing calls with OAuth 1.0a so the receiving system, in this case your app, can validate that the call was sent by Eloqua. As an app provider, it is your responsibility to ensure the validity of all inbound calls. Validation should be performed on every inbound call your app receives with OAuth parameters.

When your app receives a call from Eloqua, there are certain steps that must be taken to validate it. To illustrate these steps, let's say a POST request is received by your app from this URL:

https://example.com/eloqua/action/create?Special!Character=test@test&AssetName=Campaign+With+Spaces&oauth_consumer_key=test_client_id&oauth_nonce=1234567&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1427308921&oauth_version=1.0&oauth_signature=WeeqcIooECjp2LEGPlkabKVhkEo%3D

Note: Any spaces in the URL are changed to a "+". For example, the Asset Name in Eloqua of "Campaign With Spaces" will be "Campaign+With+Spaces" in the request.

To validate a call signature:

  1. Select the oauth_consumer_key value and ensure it matches your app's Client Id found in your app's configuration under Settings > AppCloud Developer in your Eloqua instance. If this value does not match your app's Client Id the call is invalid and should be discarded.
  2. Select the oauth_timestamp value and check that it is no more than 5 minutes older than your current server time. The timestamp will be in UTC in UNIX format (the number of seconds since January 1, 1970 00:00:00 GMT). If this value is more than 5 minutes old the call is invalid and should be discarded.
  3. Select the oauth_nonce value and compare it to oauth_nonce values for calls with the same oauth_timestamp. If it matches any other oauth_nonce values from a call with the same oauth_timestamp, the request is invalid and should be discarded.
  4. Cache the oauth_nonce and the corresponding oauth_timestamp values for 5 minutes so you can check the nonce against future calls.
  5. Calculate the first chunk of the signature base string. The signature base string consists of three parts, separated by ampersands ("&"):
    • The HTTP method (GET, POST, PUT, DELETE, etc.)
    • The URL endpoint without query string parameters, percent-encoded
    • The query parameters, omitting the oauth_signature parameter, sorted alphabetically, percent-encoded

    Using the https://example.com... example (see above), the first chunk of the signature base string, the HTTP method, is: POST.

  6. Calculate the second chunk of the signature base string. The second chunk consists of the URL endpoint without query parameters.

    Important: You need to ensure the URL does not include the port number. For example: https://example.com and not https://example.com:443. Notably, Java EE’s native HttpServletRequest.getRequestURL() function returns a URL including the protocol, server name, port number, and server path.

    Using the above example we get: https://example.com/eloqua/action/create. This URL endpoint then needs to be percent-encoded. Using the above example we get:

    https%3A%2F%2Fexample.com%2Feloqua%2Faction%2Fcreate

    Important: You need to ensure this string is encoded with percent codes and uppercase letters. For example, https://example.com should encode to https%3A%2F%2Fexample.com and not https%3a%2f%2fexample.com. Case sensitivity is not important in regular HTTP transport, but it is important when hashing to generate a signature. Notably, .NET's native HttpUtility.UrlEncode() function encodes strings with lower-case percent codes -- these will need to adjusted manually by looping through the string and ensuring the two characters after every percentage sign ("%") are in uppercase.

  7. Calculate the third chunk of the signature base string. The third chunk consists of the call's query parameters. Make sure not to include the question mark between endpoint URL and query string and to remove the oauth_signature parameter. Using the above example we get:

    Special!Character=test@test&AssetName=Campaign+With+Spaces&oauth_consumer_key=test_client_id&oauth_nonce=1234567&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1427308921&oauth_version=1.0

    Then sort the parameters alphabetically. Using the above example we get:

    AssetName=Campaign+With+Spaces&oauth_consumer_key=test_client_id&oauth_nonce=1234567&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1427308921&oauth_version=1.0&Special!Character=test@test
    

    Before percent-encoding the whole URL change any "+" to "%20", and percent-encode any special characters in all parameter keys and values. Using the above example you'd get:

    AssetName=Campaign%20With%20Spaces&oauth_consumer_key=test_client_id&oauth_nonce=1234567&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1427308921&oauth_version=1.0&Special%21Character=test%40test
    

    Finally, percent-encode the above URL to get the third chunk of the signature base string. Using the above example we get:

    AssetName%3DCampaign%2520With%2520Spaces%26oauth_consumer_key%3Dtest_client_id%26oauth_nonce%3D1234567%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1427308921%26oauth_version%3D1.0%26Special%2521Character%3Dtest%2540test
    
  8. Complete the signature base string by using ampersands to concatenate the three signature base string chunks: HTTP method, URL endpoint and query parameters. Using the above example we get the final signature base sting:

    POST&https%3A%2F%2Fexample.com%2Feloqua%2Faction%2Fcreate&AssetName%3DCampaign%2520With%2520Spaces%26oauth_consumer_key%3Dtest_client_id%26oauth_nonce%3D1234567%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1427308921%26oauth_version%3D1.0%26Special%2521Character%3Dtest%2540test

    Note: There should be two and only two ampersands in the resultant string. Any ampersands in the query string should have been percent-encoded into %26 strings, and any equal signs in the query string should have been percent-encoded into %3D strings.

    This signature base string will be used as your hash Message in subsequent validation steps. The following steps require a SHA1 cryptographic hash function (HMAC-SHA1) to create a keyed-hash message authentication code (known as the Message). Most programming languages offer easy-to-use libraries which accept a Message and Key to create the SHA1 hash.

    Important: With Eloqua, the hash Message = the call's signature base string and the Key = your app's Client Secret with an appended ampersand.

    Note: Any special characters that were in parameter keys or values, and any "+" that were changed to a "%20", will be double encoded. For example, the "+" that was changed to a "%20" will now be "%2520".

  9. Create a hash Key by appending an ampersand to your app's Client Secret. Your app's Client Secret can be found in your app's configuration under Settings > AppCloud Developer in your Eloqua instance. In your real-world app the Client Secret will be a string consisting of 100 random characters. In our example however, we'll say the Client Secret is: test_client_secret. For the purposes of creating the authentication code, you must append an ampersand to your app's Client Secret, so in this example we get: test_client_secret&
  10. Enter your appropriately formatted Message and Key values into a SHA1 hash compute function and run. The computed HMAC value is commonly returned as a Hex value. In the above example, the following Hex value is returned:

    59e7aa708a281028e9d8b1063e591a6ca561904a
    
  11. Convert the computed HMAC Hex value to Base64 string. Converting the Hex value for our example (59e7aa708a281028e9d8b1063e591a6ca561904a) results in:

    WeeqcIooECjp2LEGPlkabKVhkEo=
    
  12. Decode the percent-encoded oauth_signature value (WeeqcIooECjp2LEGPlkabKVhkEo%3D). For our example, the decoded oauth_signature value is:

    WeeqcIooECjp2LEGPlkabKVhkEo=
    
  13. Compare to computed HMAC value that's been converted to Base64 string to the decoded oauth_signature value. If the values match, the call is valid and your app can use it freely. If the values do not match the request is invalid and should be discarded.

Refer to the OAuth 1.0a spec or OAuth 1.0 RFC for more information.

Learn more

OAuth signing

Authenticate using OAuth 2.0