Sometimes we have to pull tweets from Twitter or post status to Twitter from Salesforce through Apex callouts. To interact with Twitter API on behalf of a user, an authentication has to be implemented. Twitter provides different types of authentication mechanisms. Here we can use 3-legged authorization. The implementation includes the following steps.
- Register a Twitter Application
- Obtain the request token from Twitter
- Authenticate with User
- Obtain the Access token.
- Register a Twitter Application
Log on to https://apps.twitter.com/ and go for Create new App and fill the necessary details. We can provide Website & Callback URL as our Visualforce page URL.
After successful registration, we will get a consumer key & a consumer secret.
2. Obtain the Request Token from Twitter
To get the request token, we have to make a POST request to the Twitter API. For every API request, Twitter needs an Authorization header (API documentation). The header includes the following parameters
- oauth_consumer_key - The consumer key from Twitter app
- oauth_nonce - An unique string to identify the request
- oauth_signature - A value which is generated by all of the request parameters and two secret values through a signing algorithm.
- oauth_signature_method - The signature method to generate oauth_signature. eg: HMAC-SHA1
- oauth_timestamp - The number of seconds since the Unix epoch at the point the request is generated
- oauth_version - Always be 1.0 for any request.
- oauth_token - if available from Twitter (optional).
The API request as per the API documentation:
Request Method: POST
End point: https://api.twitter.com/oauth/request_token
Parameters: oauth_callback = "the Visulaforce page URL"
Header: Authorization header generated with the above values.
Salesforce settings
Before going to callouts, we have to add the Twitter API URL (https://api.twitter.com) in remote site settings. And we have to declare some constants in apex class.
Salesforce settings
Before going to callouts, we have to add the Twitter API URL (https://api.twitter.com) in remote site settings. And we have to declare some constants in apex class.
1 2 3 4 5 6 7 8 9 10 11 12 13 | final String oauthVersion = '1.0'; final String oauthConsumerKey = '<YOUR KEY>'; final String oauthConsumerSecret = '<YOUR SECRET>'; final String baseUrl = 'https://api.twitter.com'; final String oauthSignatureMethod = 'HMAC-SHA1'; final String oauth_callback = '<YOUR VF PAGE URL>'; String oauthTimestamp; String oauthNonce; String oauthToken; String oauthTokenSecret; String accessToken; String accessTokenSecret; |
2.1 Generating oauth_timestamp in apex code
1 2 3 4 | private void getTimeStamp(){ DateTime dateTimeNow = dateTime.now(); this.oauthTimestamp = ''+(dateTimeNow.getTime()/1000); } |
2.2 Generating oauth_nonce in apex code
1 2 3 4 5 6 7 8 9 10 | private void generateNounce() { final String chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz'; String randStr = ''; while (randStr.length() < 32) { Integer idx = Math.mod(Math.abs(Crypto.getRandomInteger()), chars.length()); randStr += chars.substring(idx, idx+1); } this.oauthNonce = EncodingUtil.base64Encode(Blob.valueOf(randStr)).remove('='); } |
2.3 Generating oauth_signature in apex code
As per the API documentation, we have to collect all the parameters used in the request along with the oauth parameters and encrypt them by a signing algorithm. Also we have to create a signing key from consumer secret. We can use the Apex Crypto class for HMAC-SHA1 encoding.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | private String generateSignature(String httpMethod, String baseUrl, Map<String, String> params){ String encodedString = ''; Map<String, String> encodeParams = new Map<String, String>(); encodeParams.putAll(params); encodeParams.put('oauth_nonce', this.oauthNonce); encodeParams.put('oauth_signature_method', this.oauthSignatureMethod); encodeParams.put('oauth_timestamp', this.oauthTimestamp); encodeParams.put('oauth_consumer_key', this.oauthConsumerKey); encodeParams.put('oauth_version', this.oauthVersion); List<String> keyList = New List<String>(); keyList.addAll(encodeParams.keySet()); keyList.sort(); for(String key: keyList){ encodedString += EncodingUtil.urlEncode(key,'UTF-8') + '=' + EncodingUtil.urlEncode(encodeParams.get(key),'UTF-8') + '&'; } encodedString = encodedString.removeEnd('&'); String baseString = httpMethod.toUpperCase() + '&' + EncodingUtil.urlEncode(baseUrl,'UTF-8') + '&' + EncodingUtil.urlEncode(encodedString,'UTF-8'); String signingKey = EncodingUtil.urlEncode(this.oauthConsumerSecret,'UTF-8') + '&'; if(params.containsKey('oauth_token') && String.isNotBlank(this.oauthTokenSecret)){ signingKey += EncodingUtil.urlEncode(this.oauthTokenSecret,'UTF-8'); } Blob data = Crypto.generateMac('hmacSHA1', Blob.valueOf(baseString), Blob.valueOf(signingKey)); String signature = EncodingUtil.base64Encode(data); return signature; } |
2.4 Generating the authorization header in apex code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | private String generateAuthHeader(Map<String, String> params){ Map<String, String> authParams = new Map<String, String>(); authParams.putAll(params); authParams.put('oauth_consumer_key', this.oauthConsumerKey); authParams.put('oauth_signature_method', this.oauthSignatureMethod); authParams.put('oauth_timestamp', this.oauthTimestamp); authParams.put('oauth_nonce', this.oauthNonce); authParams .put('oauth_version', this.oauthVersion); List<String> keyList = New List<String>(); keyList.addAll(authParams .keySet()); keyList.sort(); String OathString = ''; for(String key: keyList){ OathString += EncodingUtil.urlEncode(key,'UTF-8') + '=' + '"' + EncodingUtil.urlEncode(authParams.get(key),'UTF-8') + '"' + ', '; } OathString = 'OAuth ' + OathString.removeEnd(', '); return OathString ; } |
2.4 Apex callout to get the Request Token
For this request we don't have the oauth_token & oauth_token_secret. We can use rest of the parameters for generating signature and the header.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | public void getAuthToken(){ String requestUrl = this.baseUrl + '/oauth/request_token'; String requestMethod = 'POST'; this.getTimeStamp(); this.generateNounce(); Map<String, String> params = new Map<String, String>(); params.put('oauth_callback', this.oauth_callback); String authSignature = this.generateSignature(requestMethod, requestUrl, params); params = new Map<String, String>(); params .put('oauth_callback',this.oauth_callback); params .put('oauth_signature', authSignature); HttpRequest request = new HttpRequest(); request.setHeader('Authorization', this.generateAuthHeader(params)); request.setMethod(requestMethod); request.setEndpoint(requestUrl); HttpResponse response = new HttpResponse(); this.oauthToken = ''; Http http = new Http(); try{ response = http.send(request); String responseBody = response.getBody(); this.oauthToken = responseBody.substringBefore('&').substringAfter('='); this.oauthTokenSecret = detail.substringAfter('&').substringBetween('=','&'); ApexPages.currentPage().setCookies(new Cookie[]{new Cookie('TSecret', oauthTokenSecret, null, -1, false)}); }catch(Exception e){ system.debug(e.getMessage()); } return null; } |
We will get a responds with oauth_token & oauth_token_secret as below:
After getting oauth_token & oauth_token_secret we have to go for user authentication. After user authentication we need this oauth_token_secret for next request. So we need to store the oauth_token_secret temporally. Here we can save this in a cookie.
3. Authenticate with User
As per the documentation, we have to call the authentication URL in browser with the oauth_ token obtained from previous step as a parameter.
1 2 3 4 5 6 | public PageReference authenticate(){ String requestUrl = baseUrl + '/oauth/authenticate?oauth_token=' + this.oauthtoken + '&force_login=true'; return new PageReference(requestUrl).setRedirect(true); } |
This will redirect to the Twitter login page. After successful user login, Twitter redirects to our Visualforce page with oauth_token & oauth_verifier as URL parameters.
4. Obtain the Access Token
After getting the oauth_varifier, we have to obtain the access_token by another POST request.
The API request as per the API documentation:
Request Method: POST
End point: https://api.twitter.com/oauth/access_token
Parameters: oauth_verifier = The value we collected on previous step.
Header: Authorization header
To generate the header, follow the same steps for Obtain the request token described above. So we have to generate new time_stamp, new nonce, new signature with new parameters and new signing key as a combination of consumer_secret and oauth_token_secret (already saved in cookies). And we have to include the oauth_token for both signature and header.
The corresponding apex code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | public void getAccesToken(){ this.oauthtoken = ApexPages.currentPage().getparameters().get('oauth_token'); if(String.isNotBlank(this.oauthtoken)){ String twitterId; Cookie counter = ApexPages.currentPage().getCookies().get('TSecret'); if(counter != null) { this.oauthTokenSecret = counter.getValue(); ApexPages.currentPage().setCookies(new Cookie[]{new Cookie('TSecret', '', null, -1, false)}); } String requestUrl = this.baseUrl + '/oauth/access_token'; String httpMethod = 'POST'; String oauthVerifier = ApexPages.currentPage().getparameters().get('oauth_verifier'); this.getTimeStamb(); this.generateNounce(); Map<String, String> params = new Map<String, String>(); params.put('oauth_token', this.oauthToken); params.put('oauth_verifier', oauthVerifier); String authSignature = this.generateSignature(httpMethod, requestUrl, params); params = new Map<String, String>(); params.put('oauth_token',this.oauthtoken); params.put('oauth_signature',authSignature); HttpRequest request = new HttpRequest(); HttpResponse response = new HttpResponse(); Http http = new Http(); request.setEndPoint(requestUrl); request.setMethod(httpMethod); request.setHeader('Authorization', this.generateAuthHeader(params)); request.setBody('oauth_verifier='+EncodingUtil.urlEncode(oauthVerifier, 'UTF-8')); try{ response = http.send(request); String responseBody = response.getBody(); this.oauthToken = responseBody.substringBetween('oauth_token=', '&'); this.oauthTokenSecret = responseBody.substringBetween('oauth_token_secret=', '&'); twitterId = responseBody.substringBetween('user_id=', '&'); detail = twitterId; }catch(Exception e){ system.debug(e.getMessage()); } } } |
This will return the access_token, access_token_secret and the user details.
oauth_token=6253282-eWudHldSbIaelX7swmsiHImEL4KinwaGloHANdrY&oauth_token_secret=2EEfA6BG3ly3sR3RjE0IBSnlQu4ZrUzPiYKmrkVU&user_id=6253282&screen_name=twitterapi
Using this token we can interact with the Twitter API to post or get tweets.