[Technical Users - OE Development] Webserver Method REST

Posted by wsartorelli on 11-Jun-2018 14:36

Hi everyone, I need to set up a REST method to consume webservices in Progress.
I need to make the first connection to get a "Token", after making another connection to gain access to the system using this "Token".
Below is the manual on how to get the "Token".
You can help me do it in Progress.
Thank you.
 
 
Overview
Currently, Cornerstone REST Services layer requires you to authenticate every request with a digital signature. The signature is calculated using a cryptographic hash function, a one way encryption which returns a unique hash based on the input. The input includes data from your HTTP request. The signature is included in your request as a header.
After receiving your request, the Cornerstone REST Services layer recalculates the signature using the same hash function and input sent in the request. The resulting hash is compared to the attached signature, if they match, the request is accepted, otherwise it is rejected.
Authentication Dataflow
https://apiexplorer.csod.com/apiconnectorweb/Content/apiexplorer/images/authentication-dataflow.png
API Key and Secret
Before you can sign your requests you need to acquire an API Key and Secret. The pair identifies your application to the platform. These credentials can be retrieved within the Manage API tab in Integration Center. Integration Center can be accessed from the CSOD App via Tools > Admin > Integration Center.
With an API Key and Secret in hand, you can begin your service call workflow.
  1. Construct your canonical request for a session token
  2. Sign it with your API secret
  3. Send the request
  4. Receive a session token info
  5. Construct your canonical service call request
  6. Sign it with your session token secret
  7. Send the request
Construct your request
Format your message in an unambiguous format, to ensure the Cornerstone REST Services layer can calculate the same signature for your payload. Start with your standard HTTP request, which consists of the HTTP Method, URL, HTTP Headers.
Part 1: Session Token Acquisition
A session token can be acquired by calling the Session Token Service (STS): /STS/Session REST service.
The URL pattern is:
ype-pilot.csod.com/.../session
Field
Description
Restrictions
Corpname
Client assigned hostname for CSOD application
Username
A valid username in the portal.
Must be an active account in the portal.
Alias
An arbitrary title for your session.
Must be unique among active sessions in the system.
Authentication for the STS REST service follows the convention described below under signature calculation in this document.
Example of a full HTTP POST Request:
https://apiexplorer.csod.com/apiconnectorweb/Content/apiexplorer/images/authentication-http-post-request-example.png
Step 1: Construct your string to sign
The string to sign consists of the following:
  • HTTP Method followed by carriage return "\n" (i.e. "GET", "POST", "PUT")
  • The "x-csod-" headers, excluding the x-csod-signature, all lower cased and sorted alphabetically. For each header include the key and the value, separated by colon and followed by a carriage return "\n".
    • The relativeURL, without the query string parameters or the protocol and host (i.e. /services/api/sts/session)
The string to sign for our above example:
  • "POST" + "\n" + "x-csod-api-key:1lie8ficql9h5" + "\n" + "x-csod-date:2015-09-08T11:27:32.000" + "\n" + "/services/api/sts/session"
Step 2: Sign your string
Once you have your string to sign, generate the signature as follows:
  • Apply HMAC SHA-512 algorithm using your secret key.
    • HMAC SHA-512 (string to sign, API Secret)
  • Base64 encode the hash into a string
  • Append the signature to the headers as x-csod-signature: {base64 encoded signature string}
STS Responses
The response to a successful request
<cornerstoneApi
    xmlns:i="www.w3.org/.../XMLSchema-instance">
    <Validations/>
    <status>201</status>
    <timeStamp>2015-09-08T14:45:02+0000</timeStamp>
    <createdRecords>1</createdRecords>
    <data
        xmlns:a="www.CornerStoneOnDemand.com/Core/">
        <a:Session>
            <a:Token>1v28u30hyoxsn</a:Token>
            <a:Secret>N1mxiOOtzc4BBg7XIX2ROc0aN0d0J6vVy6U02CC+DWZl4GuUNEbMtRmVlP7Sb/+9u12RJM+58YphBIsoOldubA==</a:Secret>
            <a:Alias>soapadmin1</a:Alias>
            <a:ExpiresOn>2015-09-08T14:45:02+0000</a:ExpiresOn>
        </a:Session>
    </data>
</cornerstoneApi>
The session information:
  • Token: 1albor72nb8d
    • The token is valid for 24 hour period.
  • Secret: J83wHa9InegK+imWvBcp9g7nwN7+s1MwD3dCjw25h629q1rhajL8EmF92cpjwGSvwQfWMaymdfCYJoBueC+5jA==
    • The secret is a Base64 encoded string.
  • Expires: 2015-09-08T11:27:58.000
    • Indicates when the token expires, after which the client must request another session token to continue to use the services If the request is invalid, the following response is returned:
<cornerstoneApi>
    <status>401</status>
    <timeStamp>2013-01-12T22:38:23+0000</timeStamp>
    <error>
        <errorId>b5be2e70-8728-47c1-95bd-ab64f69e6d26</errorId>
        <message>CSOD Unauthorized Exception:Check your credentials.</message>
        <code>401</code>
        <description>Check your access URL</description>
    </error>
</cornerstoneApi>
The following possible response status and messages are returned:
Status
Message
Description
401
CSOD Unauthorized Exception: Check your credentials.
Indicates that any one of following values passed in were invalid: Headers (x-csod-api-key, x-csod-date, x-csod-signature), HTTP Method is missing (GET, POST or PUT)
500
Application Error Occurred
Indicates that any of the following was invalid: Alias (May already be in use), API Key (Is inactive)
Authentication Validation
  1. The date passed in the x-csod-date header, is the current Universal time (GMT) date and time in the following format: YYYY-DD-MMThh:mm:ss.000
    • It cannot exceed 20 minutes difference from current GMT time
  2. API ID associated with your account must be active and enabled
    • The API ID can be directly referenced when passed in the header x-csod-api-key, or it can be related to a session you previously acquired. In both cases the same restriction applies
  3. The user associated with the request must be a valid and active account in the portal you are attempting to access
    • When requesting a session token, a username is passed in. The Security Token Service, will issue an access token for that specific user. All action performed using that token, are performed on behalf of that user. All interactions are audited and logged for that user. If the user is deactivated, access rights using that token are revoked.
  4. Signature verification is performed against the incoming request
    • If any of the values included in the string to sign are altered post signature generation, the signature is no longer valid, and the request is rejected.
Sample Code: API Session Signature
Sample C# code to calculate the signature (API Key request)
//signature to acquire a session
string apiId = "1lie8ficql9h5";
string apiSecret = "j6hriaKY2iZi+Y2uo9JJldmO1Bq79XB8d1v2uHzAK0Zvy972mIs8ThsJSQeDlZJz+HzmLD6Q1MUZb5X1Zf9MzQ==";
 
//build the string to sign
//note the order of the entries is important.
//The http headers must be in alphabetical order by key name
string httpMethod = "POST";
string httpUrl = "/services/api/sts/session";
StringBuilder stringToSign = new StringBuilder();
stringToSign.Append(httpMethod).Append("\n")
                    .AppendFormat("x-csod-api-key:{0}", apiId).Append("\n")
    .AppendFormat("x-csod-date:{0}", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.000")).Append("\n")
    .Append(httpUrl);
 
/* produces the following string:
*  POST\nx-csod-api-key:1lie8ficql9h5\nx-csod-date:2015-09-08T11:27:32.000\n/services/api/sts/session
*/
 
//Generate the signature
string signature = string.Empty;
 
byte[] secretkeyBytes = Convert.FromBase64String(apiSecret);
byte[] inputBytes = Encoding.UTF8.GetBytes(stringToSign.ToString());
using (var hmac = new HMACSHA512(secretkeyBytes))
{
    byte[] hashValue = hmac.ComputeHash(inputBytes);
    signature = System.Convert.ToBase64String(hashValue);
}
 
/*
* signature produced:
* 3x5ETGSoqJa4vLl8gOFzdhxReOS0k8Nk2CpKVFN2A60ItF8wfP2tr+GUY2mELXjL90B57B5imLIrzou3ZQMfqQ==
*/
Sample PHP code to calculate the signature (API Key request)
//signature to acquire a session
$apiId = '1lie8ficql9h5';
$apiSecret = 'j6hriaKY2iZi+Y2uo9JJldmO1Bq79XB8d1v2uHzAK0Zvy972mIs8ThsJSQeDlZJz+HzmLD6Q1MUZb5X1Zf9MzQ==';
 
//build the string to sign
//note the order of the entries is important.
//The http headers must be in alphabetical order by key name
$httpMethod = 'POST';
$apiKey = 'x-csod-api-key:'.$apiId;
$httpUrl = '/services/api/sts/session';
 
date_default_timezone_set('UTC');
$date = 'x-csod-date:'.date('Y-m-d').'T'.date('H:i:s').'.000';
$stringToSign = $httpMethod."\n".$apiKey."\n".$date."\n".$httpUrl;
 
/* produces the following string:
*  POST\nx-csod-api-key:1lie8ficql9h5\nx-csod-date:2015-09-08T11:27:32.000\n/services/api/sts/session
*/
 
//Generate the signature
$secretKey = base64_decode($apiSecret);
$signature = base64_encode(hash_hmac('sha512', $stringToSign, $secretKey, true));
 
/*
* signature produced:
* 3x5ETGSoqJa4vLl8gOFzdhxReOS0k8Nk2CpKVFN2A60ItF8wfP2tr+GUY2mELXjL90B57B5imLIrzou3ZQMfqQ==
*/
 
 
 
 
Assina-email
Vem aí o 6º Movimento Você e a Paz, em Amparo/SP. Data: 29/07/2018, Domingo || Local: Praça Pádua Salles || Reserve este momento!
Esta mensagem, incluindo seus anexos, tem caráter confidencial e seu conteúdo é restrito ao destinatário da mensagem. Caso você tenha recebido esta mensagem por engano, queira por favor retorná-la ao destinatário e apagá-la de seus arquivos. Qualquer uso não autorizado, replicação ou disseminação desta mensagem ou parte dela é expressamente proibido. A Ypê não é responsável pelo conteúdo ou a veracidade desta informação


Confidentiality Notice: The information contained in this email message, including any attachment, is confidential and is intended only for the person or entity to which it is addressed. If you are neither the intended recipient nor the employee or agent responsible for delivering this message to the intended recipient, you are hereby notified that you may not review, retransmit, convert to hard copy, copy, use or distribute this email message or any attachments to it. If you have received this email in error, please contact the sender immediately and delete this message from any computer or other data bank. Thank you.

All Replies

Posted by Peter Judge on 11-Jun-2018 15:04

The ABL HTTP Client can make the calls you need for both getting the token and for passing it.  There are a number of examples on Github at github.com/.../http_client  .
 
It’s relatively simple to construct requests and consume responses in general. For this kind of API call, you need to be careful about how you build the token (the x-csod-signature header value). Look at using the MESSAGE-DIGEST() function to perform the SHA-512 hashing .

Posted by jquerijero on 11-Jun-2018 15:17

Do you expect this to run on non-Windows platform? If not, it's very easy using the .NET framework.

Posted by wsartorelli on 12-Jun-2018 10:16

I need to get a Token by sending the URL ype-pilot.csod.com/.../session, along with Date and Key and Signature parameters. Taking the Token response and other signature.

I tried to send this information and I did not succeed.

Do you have any practical examples for this?

Thank you.

Posted by Peter Judge on 12-Jun-2018 10:26

What did your request look like? You should remove your credentials before posting, but it’s really hard to figure it out without code.

Posted by wsartorelli on 12-Jun-2018 15:51

My friend, I need to put the code below in Progress, can you help me?

using System;

using System.Collections.Generic;

using System.Collections.Specialized;

using System.IO;

using System.Linq;

using System.Net;

using System.Security.Cryptography;

using System.Text;

using System.Threading.Tasks;

namespace Sample

{

   class Program

   {

       static void Main(string[] args)

       {

           var uri = new Uri(@"qa01.csod.com/.../Session

           var request = (HttpWebRequest)WebRequest.Create(uri);

           var apiId = "wa2vbauugyvt";

           var apiSecret = "tDCBh1b8tDO8lGVvieNqVCFUZgsPm1OoxYW4OM3WgEt21BSBpICULBe9sYaNZAEoRLVe49Fply/zgjKBP6V4gg==";

           request.Headers.Add("x-csod-date", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.000"));

           request.Headers.Add("x-csod-api-key", apiId);

           request.Method = "POST";

           var stringToSign = ConstructStringToSign(request.Method, request.Headers, uri.AbsolutePath);

           var sig = SignString512(stringToSign, apiSecret);

           request.Headers.Add("x-csod-signature", sig);

           request.ContentType = "text/xml";

           request.Timeout = 999999;

           request.ContentLength = 0;

           request.Accept = "text/xml";

           using (var response = request.GetResponse())

           {

               using (StreamReader reader = new StreamReader(response.GetResponseStream()))

               {

                   string responseFromServer = reader.ReadToEnd();

                   Console.WriteLine(responseFromServer);

               }

           }

           Console.WriteLine();

           Console.WriteLine("Enter To Exit");

           Console.ReadLine();

       }

       public static string ConstructStringToSign(string httpMethod, NameValueCollection headers, string pathAndQuery)

       {

           StringBuilder stringToSign = new StringBuilder();

           var httpVerb = httpMethod.Trim() + "\n";

           var csodHeaders = headers.Cast<string>().Where(w => w.StartsWith("x-csod-"))

                                                   .Where(w => w != "x-csod-signature")

                                                   .Distinct()

                                                   .OrderBy(s => s)

                                                   .Aggregate(string.Empty, (a, l) => a + l.ToLower().Trim() + ":" + headers[l].Trim() + "\n");

           stringToSign.Append(httpVerb);

           stringToSign.Append(csodHeaders);

           stringToSign.Append(pathAndQuery);

           return stringToSign.ToString();

       }

       public static string SignString512(string stringToSign, string secretKey)

       {

           byte[] secretkeyBytes = Convert.FromBase64String(secretKey);

           byte[] inputBytes = Encoding.UTF8.GetBytes(stringToSign);

           using (var hmac = new HMACSHA512(secretkeyBytes))

           {

               byte[] hashValue = hmac.ComputeHash(inputBytes);

               return System.Convert.ToBase64String(hashValue);

           }

       }

   }

}

Posted by jquerijero on 12-Jun-2018 16:13

Are you planning on calling this on a non-Windows platform? I have something similar that I coded, but it will only work for Windows because it is using .NET Framework.

Posted by wsartorelli on 13-Jun-2018 07:01

Hi, I will use it in Windows, my ERP is in Windows.

Tanks.

This thread is closed