CRC is a mechanism by which the event publisher verifies that the registered subscriber owns the URL that is registered (via webhook) for receiving events. For e.g. Twitter's webhook-based APIs provide the challenge-response checks to confirm the ownership of the web app receiving webhook events.

In this article we will see how can we create one such web app - which is nothing but a Apex REST service, and register it to twitter webhook. This is part of the configuration step required for Twitter Salesforce integration article here.

How CRC works

For a CRC of your Apex REST service, twitter will actually make a GET request to your service with a crc_token parameter. When that request is received, our service needs to build an encrypted response_token based on the crc_token parameter and our twitter app's Consumer Secret. The response_token must be encoded in JSON and returned within three seconds. If successful, a webhook id will be returned.

Recap of CRC Response requirements

  • A base64 encoded HMAC SHA-256 hash created from the crc_token and your app Consumer Secret
  • Valid response_token and JSON format.
  • Latency less than 3 seconds.
  • 200 HTTP response code.

In order to implement this we need a simple Apex REST service made public via Guest profile. The class must have two HTTP request methods

  • GET - triggered when a CRC request is sent by twitter during webhook registration. This will read the crc_token passed as a URL parameter and generate a response as shown below adhering to the response requirements mentioned above. The URL of the service would be of the form https://<SalesforceDomainName>/services/apexrest/{Prefix of your Apex REST Service}?crctoken={random characters set by twitter}.
{
 "responsetoken": "sha256=x0mYd8hz2goCTfcNAaMqENy2BFgJJfJOb4PdvTffpwg=" 
}
  • POST - triggered after a successful webhook registration when any webhook event occurs at twitter end.

Sample Apex REST Service

@RestResource(urlMapping='/{prefix}')
global class TwitterSF {
    public class myres{
        String response_token;
    }

    @HttpGet
    global static void getResponse()
    {
        RestRequest restReq = RestContext.request; 
        String crc_token = restReq.params.get('crc_token'); 
        RestContext.response.addHeader('Content-Type','application/json'); 

        system.debug('crc_token'+crc_token);
        String secretKey = String.valueOf('{Obtained API secret key}');
        String key = 'key';
        String crctoken;

        Blob data = crypto.generateMac('HmacSHA256',
                                       Blob.valueOf(crc_token),Blob.valueOf(secretKey));
        String response = '{'+
            'response_token: sha256=' +EncodingUtil.base64Encode(data)+
            '}';
        myres r = new myres();
        r.response_token = 'sha256='+EncodingUtil.base64Encode(data);
        RestContext.response.responseBody = Blob.valueOf(JSON.serialize(r));
    }
    
    @HttpGet
    global static void processResponse()
    {
    	//Logic for processing the event. e.g. create case from the tweet
    }
}
You've successfully subscribed to inteygrate
Welcome back! You've successfully signed in.
Great! You've successfully signed up.
Success! Your account is fully activated, you now have access to all content.