How to send Twilio SMS via Web Service in Salesforce Apex - salesforce

I want to send SMS from Twilio noticed they have libraries built for Java, .Net, Node, etc. so that we can use them if we are upto those technologies.
But I want to do the same from Salesforce Apex and trying to figure out how to build the Http parameters to make the authorization.
I tried to map with cURL example given in Twilio documentation and can't find header keys for Auth token.
Below is my current code and looking for how to set the authentication params.
req.setEndpoint(baseURL + '/2010-04-01/Accounts/account_sid/Messages.json');
req.setMethod('POST');
req.setHeader('to', EncodingUtil.urlEncode('+to_number', 'UTF-8'));
req.setHeader('from', EncodingUtil.urlEncode('+from_number', 'UTF-8'));
Http ht = new Http();
HttpResponse res = ht.send(req);
Updated request :
Blob headerValue = Blob.valueOf('my-twilio-account-sid:my-twilio-auth-token');
String authorizationHeader = 'BASIC ' + EncodingUtil.base64Encode(headerValue);
req.setHeader('Authorization', authorizationHeader);
req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
String body = EncodingUtil.urlEncode('From=+from_number&To=+to_number&Body=Sample text from twilio', 'UTF-8');
req.setBody(body);
Http ht = new Http();
HttpResponse res = ht.send(req);
Response saying
Bad Request : A 'From' phone number is required.

The phone numbers don't go in the headers.
For the headers you will need
Content-Type: "application/x-www-form-urlencoded"
then you will need another header for authorization
Authorization: auth-string
where auth-string is a combination of the string Basic followed by a space followed by a base64 encoding of twilio-account-sid:twilio-auth-token (replace with your Twilio credentials joined by the colon) so the header will look something like
Authorization: "Basic ABCiDEdmFGHmIJKjLMN2OPQwR2S3TUVzABliCDE3FGc1HIo2JKL2MjNwOPcxQRSwTUc1Vzc0XmYhZAB3CDElFGH1Jw=="
The body of the POST request should contain key, value pairs of To, From and Body, something like
"From=" + twilio-phone-number + "&To=" + to-number + "&Body=" + message-body (replace with string values for phone numbers and message).
I hope this helps.

Related

dropbox-api get_thumbnail_v2 & get_thumbnail returns question marks(?) inside rhombs ()

I am trying to use DropBox API to get a thumbnail from DropBox and show them on Lightning Web Component in Salesforce, but can not do it because in a response Apex receiving body with black rhombs and question marks inside.
I use standard HTTP method to call
HttpRequest req = new HttpRequest();
req.setHeader('Authorization', 'Bearer sl.validToken');
req.setHeader('Dropbox-API-Arg', '{"resource": {".tag": "path","path": "/folderName/pictureName.jpg"},"format": "jpeg","size": "w64h64","mode": "strict"}');
req.setHeader('Content-Type', 'text/plain; charset=utf-8');
req.setEndpoint('https://content.dropboxapi.com/2/files/get_thumbnail_v2');
req.setMethod('POST');
Http httpreq = new Http();
HttpResponse res = httpreq.send(req);
this is what I receive in body of response in Apex. The same response I have in Postman.
https://i.stack.imgur.com/90yjI.png
This is what I have in DropBox explorer with same values and headers (JSON)
https://i.stack.imgur.com/ytDxv.png
File scope is Read to everyone. SF Remote Site Settings & CSP Trusted Sites are set.
Short update:
I`ve been able to get JSON From header. I did use that piece of code:
List<String> headers = new List<String>(res.getHeaderKeys());
for(String key : headers){
System.debug('key ->>> '+key+' = '+res.getHeader(key));
}
String jsonString = res.getHeader('Dropbox-Api-Result');
System.debug('->>>ddd '+jsonString);
But still do not understand how to use it as a thumbnail in LWC.
Thank you in advance for your help.
The /2/files/get_thumbnail_v2 Dropbox API endpoint is a "content-download" style endpoint, meaning the "response body contains file content, so the result will appear as JSON in the Dropbox-API-Result response header". So, the illegible value you're receiving is the actual bytes of the thumbnail data itself. You're currently attempting to display it as text, but you'll instead need to save and display it as an image to see the thumbnail. Refer to your platform's documentation for information on how to display an image.
For reference, the Dropbox API v2 Explorer is built with knowledge of the different endpoint formats, so in this case it displays the metadata from the Dropbox-API-Result response header, and just offers the file data, in this case the thumbnail data, as a download via a "Download" button.

Finding the ID of a Salesforce Global Value Set

I'm attempting to write an APEX class that will add items to a global valueset, but in order to do so I need to know the ID of the global value set. Is there a way to find the ID of the global valueset (through APEX, not by looking at the URL) that a picklist field is using? Ideally I'd be able to do something similar to:
Case.picklistField__c.getdescribe();
and get a response that includes the ID of the global value set that it uses - that way I can then use my the metadataAPI to update the values.
Alternatively if I could find the global valueset by name I could use that with the metadata api as a work around.
UPDATE: Was able to get this to work using Eyescreams suggestion with the tooling API - full implementation:
String gvsName = 'TestValueSet'; //name of the global valueset you want the Id for goes here
HttpRequest req = new HttpRequest();
req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID());
req.setHeader('Content-Type', 'application/json');
req.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm()+'/services/data/v41.0/tooling/query/?q=select+id+from+globalvalueset+Where+developername='+gvsName);
req.setMethod('GET');
Http httpreq = new Http();
HttpResponse res = httpreq.send(req);
system.debug(res.getBody());
SELECT Id, FullName, Description
FROM GlobalValueSet
But it's not available in straight Apex queries, you'd need Tooling API (meaning a REST callout). You can play with it in Developer Console -> Query Editor, just check the tooling api checkbox on bottom
These days you need to escape the variable like this:
String gvsName = 'TestValueSet'; //name of the global valueset you want the Id for goes here
HttpRequest req = new HttpRequest();
req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID());
req.setHeader('Content-Type', 'application/json');
req.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm()+'/services/data/v48.0/tooling/query/?q=select+id+from+globalvalueset+Where+developername=\''+gvsName+'\'');
req.setMethod('GET');
Http httpreq = new Http();
HttpResponse res = httpreq.send(req);
system.debug(res.getBody());

Google JWT Invalid Signature from Salesforce

I am trying to authenticate via a service account from Salesforce.com to Google's DFP. I had the integration working under a previous user/credential pair, but am required to update to a new user.
I created the project/user/key pair in the Google Developer Console and added the new service account to the network in DFP. I then changed the "iss" value to be the new user's email and the private key to be the new private key from the keypair.
I am now receiving an 'Invalid Signature' error.
In SFDC, I am using Crypto.sign method with RSA-SHA256.
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_restful_crypto.htm#apex_System_Crypto_sign
I have validated the key format to be PKCS#8 with header and new line characters removed per the documentation (I went so far as to decode the ASN.1 format and inspect the nodes for conformity).
Have I missed a step in the connection between the user and the correct credential? Is there a way for me to validate the signature that I am producing locally to see where I am going wrong? The only difference I have seen is that the old private key was shorter than the current private key.
Below is the code I am using to generate the JWT (again, this code functioned properly with a different username and credential key).
JWTHeader head = new JWTHeader();
head.alg = 'RS256';
head.typ = 'JWT';
JWTClaimSet claim = new JWTClaimSet();
claim.iss = '<username>#*.iam.gserviceaccount.com';
claim.scope = 'https://www.googleapis.com/auth/dfp';
claim.aud = 'https://accounts.google.com/o/oauth2/token';
claim.iat = DateTime.now().getTime() / 1000;
claim.exp = claim.iat + 3600;
System.debug(JSON.serialize(head));
System.debug(JSON.serialize(claim));
String key = '<privatekey>’;
String base = EncodingUtil.urlEncode(EncodingUtil.base64Encode(Blob.valueOf(JSON.serialize(head))), 'UTF-8') + '.' + EncodingUtil.urlEncode(EncodingUtil.base64Encode(Blob.valueOf(JSON.serialize(claim))), 'UTF-8');
String sig = EncodingUtil.urlEncode(EncodingUtil.base64Encode(Crypto.sign('RSA-SHA256', Blob.valueOf(base), EncodingUtil.base64Decode(key))), 'UTF-8');
String body = base + '.' + sig;
System.debug(body);
Http http = new Http();
HttpRequest req = new HttpRequest();
req.setEndpoint('https://accounts.google.com/o/oauth2/token');
req.setBody('grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=' + body);
req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
req.setMethod('POST');
HttpResponse resp = http.send(req);
Days later I found another solution that solved the issue. The problem was the base64urlsafe encoding. This encoding is not natively done in SFDC and perscribes the removal of trailing padding characters from the base64 string. Luckily, my original username encoded with no padding characters in the claim set. With the new username, the padding characters are present and must be removed before signing.
It all comes down to just a few characters.

Basic Auth Header appears to be lost

I am writing a Flask RESTful server and an AngularJS client, and am running into an issue where it appears the username and password information are being lost in transmission, so to speak.
In the Javascript console, I can tell that the client is sending the the Authorization header as expected: Authorization: Basic username:password. However, within the #auth.verify_password callback, they are both both empty.
I have a fair bit of unit tests around the server portion of the code, and the auth information appears to be present in all of them, so it is reassuring that I can at least get the username and password from within the header in some instances.
Additionally, I have added the CORS extension to the server code, and allow it server wide. It appears that an OPTIONS(which always returns 200) to the below url is always called immediately before the GET(returns 401, due to username and password issue) to the same url.
Reference code:
Server auth callback:
#app.route('/api/users/token')
#auth.login_required
def get_auth_token():
token = g.user.generate_auth_token()
return jsonify({ 'token': token.decode('ascii') })
#auth.verify_password
def verify_password(email_or_token, password):
print 'email_or_token: ' + email_or_token
print 'password: ' + password
...
Server Unit test code behaving as expected:
def _get_user_token(self, email=TEST_EMAIL, password=TEST_PASSWORD):
headers = {
'Authorization': 'Basic ' + b64encode("{0}:{1}".format(email, password))
}
response = self.app.get('/api/users/token', headers=headers)
return response
AngularJS code which yields appropriate header when inspected in browser but empty username and password in auth callback:
$http.get(silkyAppConstants.BASE_URL + '/api/users/token', {
headers: { "Authorization": "Basic " + username + ":" + password }
})
I suspect your problem is that you are sending an invalid Authorization header. The username + ":" + password portion of the header must be base64 encoded (see Section 2 of RFC 2617. When Flask receives the plain text credentials that you are sending it is trying pass it through a base64 decoder and that fails.

Salesforce Oauth Status Code 302

While trying to setup oauth in the org, getting status code 302.
Below is the code snippet.
String AUTH_URL = 'https://login.salesforce.com/services/oauth2/authorize';
String redirect_uri = 'https://login.salesforce.com/services/oauth2/success';
String response_type = 'token';
String client_id = 'xxxxxxxxxxxxxx.xxxxxxxxx';
authURL = AUTH_URL +
'?response_type=' + response_type +
'&client_id=' + client_id +
'&redirect_uri=' + redirect_uri;
HttpRequest req = new HttpRequest();
HttpResponse res = new HttpResponse();
Http http = new Http();
req.setMethod('GET');
req.setEndpoint(authUrl);
res = http.send(req);
//RESULT:
res.getStatusCode() = 302
res.getStatus() = Found
I am expecting to receive access Token and refresh token in response's body but it is empty.
Please suggest if I am missing out on something.
Thanks in advance.
the authorize endpoint is for a browser based interactive login, not a programatic login. If you want a pure programatic method, then you should checkout the username/password oauth flow that you can use via the /services/oauth2/token endpoint.

Resources