Skip to Content

Some Web applications are securing their applications with the x-csrf-token. This requires you to call the service to get a token before you do the modification of the objects. This is at the moment not support the REST adapter in SAP PI/PO. But it could probably be added later.

The scenario looks like the following. In our message mapping, we will perform a lookup call to a Rest adapter to get the x-csrf-token. Then we have an adapter module that we use to call the service with.

 

You can see the video where I’m describing how to setup the system.

Here is the UDF method that calls to get the token.

	@LibraryMethod(title="lookupToken", description="get token from service", category="RestLookup", type=ExecutionType.SINGLE_VALUE) 
	public String lookupToken (
		 Container container)  throws StreamTransformationException{
		AbstractTrace trace = container.getTrace();
	     Map<String, Object> all = container.getInputHeader().getAll();
		
	     Channel restChannel = LookupService.getChannel("B2B","ReceiverRestToken");
			SystemAccessor restAccessor = LookupService.getSystemAccessor(restChannel);
			trace.addInfo("got channel ");
			  XmlPayload payload = LookupService.getXmlPayload( new ByteArrayInputStream("<root/>".getBytes()));
			Payload response = restAccessor.call(payload);
			trace.addInfo("got response");
			String payloadContent = "";
			try {
				payloadContent = readbytes(response.getContent());
				
				trace.addInfo(payloadContent);
			} catch (IOException e1) {
				trace.addWarning("IO Exception " +e1.getMessage());
			}
			
			
			try{
	     
	     DynamicConfiguration config = (DynamicConfiguration)all.get(StreamTransformationConstants.DYNAMIC_CONFIGURATION);
		   
		
		    //Define key to write in the Dynamic Configuration
		    DynamicConfigurationKey key1 = DynamicConfigurationKey.create("http://sap.com/xi/XI/System/REST","token");
		    config.put(key1,payloadContent );
			}catch(Exception e){ 
				trace.addWarning("Unable to set dynamic configuration" );
			}
		    
		return "";
	}

	private String readbytes(InputStream inputStream) throws IOException{
	ByteArrayOutputStream result = new ByteArrayOutputStream();
	byte[] buffer = new byte[1024];
	int length;
	while ((length = inputStream.read(buffer)) != -1) {
	    result.write(buffer, 0, length);
	}
	// StandardCharsets.UTF_8.name() > JDK 7
	return result.toString("UTF-8");
	}

Then we have the adapter module that you need to include a software component and deploy together with your other modules.

public class SetTokenModule implements Module, SessionBean {
	/**
	 * 
	 */
	private static final long serialVersionUID = -4916672969815060014L;
	private static final Location LOC = Location.getLocation(SetTokenModule.class);
	private AuditAccess audit;

	@Override
	public ModuleData process(ModuleContext context, ModuleData inputmoduleData) throws ModuleException {
		try {
		Message msg = (Message) inputmoduleData.getPrincipalData();
		 MessageKey key = msg.getMessageKey();
		
			audit = PublicAPIAccessFactory.getPublicAPIAccess()
					.getAuditAccess();
				audit.addAuditLogEntry(key, AuditLogStatus.SUCCESS,
				"TokenModule: Module called");
				MessagePropertyKey tokenKey = new MessagePropertyKey("x-csrf-token","http://sap.com/xi/XI/System/REST");
				
				String token = msg.getMessageProperty(tokenKey);
				audit.addAuditLogEntry(key, AuditLogStatus.SUCCESS,
						"TokenModule: Tokenvalue "+token);
				TextPayload tokenPayload = msg.createTextPayload();
				tokenPayload.setText(token);
				msg.setMainPayload(tokenPayload);
		}
		catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return inputmoduleData;
	}
}

The post channel configuration should look like the following.

And to get the token to the adapter you need to add the header properties like the following.

To report this post you need to login first.

3 Comments

You must be Logged on to comment or reply to a post.

  1. Piotr Radzki

    Hi Daniel,

    Nice, I currently do the same (manually) from Postman while testing S4 HANA Cloud API directly, without using CPI. Some of them, especially POST (Create CRUD opeations) require x-csrf-token for communication, not only basic authentication. It’s nice that you share the code Daniel. Thanks!

    BR,

    Piotr

    (1) 
  2. Thomas Büttner

    Hi Daniel,

    great work. I was looking for this very long. But you also get the token, while the message mapping, right? There the message mapping is bind to the receiver channel. What if the message goes to many channels and all uses different tokens? Only a thought? But so I came up with the idea to put it all in the adapter module but I had much problems with implementing it. Do you think this is possible, to put it all in a adapter module and so every channel can use its own token an the message mapping is independent from the channel?

     

    Best Regards,

    Thomas

    (0) 
    1. Daniel Graversen
      Post author

      Hi  Thomas,

      Yes I guess you will need to place it in adapter module. I’m not sure if the adapter lookup is available there but you should be able to use some other ways to lookup channels and invoke them form a module context.

      The cool thing would be if you could get the rest adapter to request the token self, instead of having to code different around it.

      An alternative is that you perform the lookup in the message mapping and then save multiple tokens in the dynamic context and then in your channel, you just select the correct token that corresponds to the messag.e

      (0) 

Leave a Reply