How to add x-csf-token to webservice?

Which IIQ version are you inquiring about?

Unsure

Share all details related to your problem, including any error messages you may have received.

Hi,

I’m having issue for a web service with the error Unauthorized error 401 for sap web service connector.
Both test connection and schema preview are working I think because the connector needs a x-csf-token for it to proceed as how can I fetch and call the generated x-csf-token in SailPoint?

Using postman to post,put are working as long as it has x-csf-token added to it’s header.

Hi Jomar,
Don’t know if that solves your problem - generaly you can add whatever you want into the header of your operation just follow
https://documentation.sailpoint.com/connectors/webservices/help/integrating_webservices/headers.html

Hi @jomartolosa , this can be achieved using a Web Services’ Before Operation Rule.
Web Services Before Operation Rule.

Within the rule, you need to make an additional API call to the target system to fetch the “x-csrf-token” and then update the header in the requestEndPoint map.

@jomartolosa - In General, 401 Unauthorized client error status response code indicates that the client request has not been completed because it lacks valid authentication credentials for the requested resource.
What kind of authentication scheme you are using here? Are you using OAuth or basic authentication?

Hi @gauravsajwan1 ,

I’m still looking for reference on how to add it via Before Operation Rule if you can provide a reference for me to look it would help a lot.

Hi @officialamitguptaa
We are using OAuth2 as our authentication method I have a quick question in postman we need to fetch the x-csrf-token and and use it for POST, PUT and DELETE method.

This blog is inspired by an excellent blog “Just a single click to test SAP OData Service which needs CSRF token validation” authored by jerry.wang

I liked the approach Jerry shared. Each time you need to create, update or delete some data via (SAP) oData API you need to use CSRF token (e.g. it’s applicable to C4C oData API). It used to be quite a pain in Postman. Jerry suggested using an environment variable in Postman to share CSRF token between 2 (or more) requests. Where the first request is getting CSRF token for you and stores it in an environment variable while subsequent requests consume this CSRF token via the variable. Sounds logical.

However, in my case, the need to run a collection (of requests) each time when I need to do a quick and simple POST or PUT or PATCH to C4C oData API was not something I would be comfortable with.

I would prefer “real one-click”. Just hit the Send button in Postman and here we go. Something similar to OData Explorer tool available in C4C system where you don’t need to care about CSRF token at all. Frankly, it’s a great tool, but it has some performance issues when you launching it or navigating from one “heavy” entity type to another. And the error handling is another question which, in my opinion, oData Explorer needs to address to show the complete error message produced by the backend of C4C.

Postman beast is still a preference of mine.
So I wanted to improve Jerry’s approach to make it a “real one-click”.

A bit of research and play with Postman on one of business trips’ flights got me to the idea. And the idea was to use Pre-requests Script in Postman. They are powerful. As powerful as Test scripts. Or even more.

Here is the pre-request script I’ve put together. Console logs are there just for test purposes. Feel free to remove them if you’re clear on what the script is doing and when. You can see those logs in Postman Console if you open it before doing the call to your oData API. Postman Console is available either via menu View → Show Postman Console or hotkey Alt+Ctrl+C.

console.log('Pre-request Script from Request start');

// We don't need to do anything if it's GET or x-csrf-token header is explicitly presented
if (pm.request.method !== 'GET' && !(pm.request.headers.has('x-csrf-token'))) {

  var csrfRequest = pm.request.clone();
  csrfRequest.method = 'GET';
  if (pm.request.method === 'POST') {
    // for POST method usually it is ....<something>Collection in the URL
    // so we add $top=1 just to quickly get csrf token; 
    // for PUT, PATCH or DELETE the same URL would be enough,
    // because it points to the actual entity
    csrfRequest.url = pm.request.url + '?$top=1';
  }

  csrfRequest.upsertHeader({
    key: 'x-csrf-token',
    value: 'fetch'
  });

  pm.sendRequest(csrfRequest, function(err, res) {
    console.log('pm.sendRequest start');
    if (err) {
      console.log(err);
    } else {
      var csrfToken = res.headers.get('x-csrf-token');
      if (csrfToken) {
        console.log('csrfToken fetched:' + csrfToken);
        pm.request.headers.upsert({
          key: 'x-csrf-token',
          value: csrfToken
        });
      } else {
        console.log('No csrf token fetched');
      }
    }
    console.log('pm.sendRequest end');
  });
}

console.log('Pre-request Script from Request end');​

The logic here is:

  1. We’re getting the original request and checking if we need to obtain CSRF token or not (we don’t need CSRF token if we’re doing GET or if the token already presented explicitly).

  2. If we’re unlucky enough and we need to obtain CSRF token, we’re cloning the original request. I didn’t find any other way to get the authentication part from the original request into a new request properly and dynamically.

  3. Having the cloned request, we’re immediately changing its method to GET.

  4. Then we’re enriching the URL of the cloned request for performance reason if we need to.

  5. And populating x-csrf-token header of the cloned request with the value “fetch” barging for a token.

  6. As a next step, we’re sending this cloned and modified request providing a call back function. This function will be executed once the request is completed.

  7. In this call back function, we’re checking for any errors, then looking for x-csrf-token header returned to us and if it’s fetched, we’re upserting it (updating if exists, creating if it doesn’t) into the original request.

To use this script, simply copy the code provided and paste it into the tab called Pre-request Script in your Postman’s request. Then click Send to send your POST/PUT/PATCH/DELETE request to C4C oData API.

You can even go further and put this script either into your Folder or Collection in Postman. And then the script will run for any request you’re doing within those folders or collections. You can find out more on the sequence of scripts in Postman documentation.

Mark it as solved, If you get your answer

Another question in IIQ do I still need to add the x-csrf-token or will I still need to use it for POST, PUT and DELETE?

@jomartolosa -
Each time you need to create, update or delete some data via (SAP) oData API you need to use CSRF token. Since this is a restriction from the SAP side, you must need to include this x-csrf-token when you use POST, PUT or Delete methods from IIQ. Please check with your SAP Team for more details on this.

This stackoverflow article must be helpful.

Hi,

forgot to add here’s the error message after running the aggregation hope this can also help
thank you

Hi @officialamitguptaa, Quick question does csrf employs only on IDN or both IIQ and IDN?

Hi everyone,

I’m having issue on how to generate the CSRF token in before operation rule
it’s getting a null value unlike the access token would like to know if what’s wrong with it thank you.

import org.json.simple.JSONArray;
  import org.json.simple.JSONObject;
  import org.json.simple.parser.JSONParser;
  import sailpoint.connector.webservices.WebServicesClient;
  import sailpoint.connector.webservices.EndPoint;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import sailpoint.object.ProvisioningPlan;
  import sailpoint.object.ProvisioningPlan.AccountRequest;
  import sailpoint.object.ProvisioningPlan.AttributeRequest;
  import sailpoint.object.ProvisioningPlan.AccountRequest.Operation;

  String url = application.getAttributeValue("token_url").toString();
  String clientID = application.getAttributeValue("client_id").toString();
  String clientSecret = application.getAttributeValue("client_secret").toString();

  String csrfUrl = "https://proquire-q.us10.sac.cloud/api/v1/csrf";
  String grantType = "client_credentials";

  Map payload = new HashMap();
  payload.put("client_secret", clientSecret);
  payload.put("client_id", clientID);
  payload.put("resource", resource);
  payload.put("grant_type", grantType);

  List codes = new ArrayList();
  codes.add("2**");
  codes.add("4**");

  Map headers = new HashMap();
  headers.put("Content-Type", "application/x-www-form-urlencoded");
  headers.put("x-csrf-token","fetch");

 
  Map arg = new HashMap();
  arg.put(restClient.ARG_URL, url);
  restClient.configure(arg);

  Map argCSRF = new HashMap();
  argCSRF.put(restClient.ARG_URL, resource);
  restClient.configure(argCSRF);

  String csrfResponse = restClient.executeGet(csrfUrl, headers, codes);

  String csrfToken = headers.get("X-CSRF-Token");
  String csrfResponse = restClient.configure(csrfToken);
  JSONParser csrfjsonParser = new JSONParser();
  JSONObject csrfjsonObject = (JSONObject) csrfjsonParser.parse(csrfresponse);
  headers.put("X-CSRF-Token", csrfToken);
  
  String response  = restClient.executePost(url, payload, headers, codes);
  JSONParser jsonParser = new JSONParser();
  JSONObject jsonObject = (JSONObject) jsonParser.parse(response);
  String accessTokenGeneratedInBeforeRuleScript = (String) jsonObject.get("access_token");
 

  Map updatedInfoMap = new HashMap();
  Map headerMap = new HashMap();
  headerMap.put("Authorization","Bearer "+accessTokenGeneratedInBeforeRuleScript);
  requestEndPoint.setHeader(headerMap);
  Map connectorStateMap = new HashMap();
  connectorStateMap.put("accesstoken","Bearer "+accessTokenGeneratedInBeforeRuleScript);
  updatedInfoMap.put("updatedEndPoint",requestEndPoint);
  updatedInfoMap.put("connectorStateMap",connectorStateMap);

  // log error
  log.error("CSRF token value: " + csrfToken);
  log.error("Authorization","Bearer "+accessTokenGeneratedInBeforeRuleScript);
 
  return updatedInfoMap;

I see you execute headers.put with x-csrf-token attribute but once you do that in lowercase and once in uppercase - is it how you wanted to do it? Also i would use custom authentication to do that i stead of before operation rule to avoid amount of rest calls to webservice.

Hi @kjakubiak,
with regards generating using the executeget command I was getting null value also I with regards we only use OAuth2 or API token it depends on the requirement of the application team and I don’t have any idea on how to use the custom authentication also.