I have integrated messaging with Twilio and am using Twilio callback to update status of the message in my system. When twilio is responding back with the status, it's always failing for POST api.
I have followed the steps from twilio documentation https://www.twilio.com/docs/usage/security#validating-requests
Below is what I am doing :
1. Extract request URL and append query parameters
2. Get parameter map, filter out any request param that is part of this map
3. Validate by calling validate from twilio sdk
I have found that my generated signature is never matching the signature coming from twilio for POST method
Here is my code for getting complete request URL :
StringBuilder requestURL = new
StringBuilder(request.getRequestURL().toString());
String queryString = request.getQueryString();
String requestURLString = "";
if (StringUtils.isBlank(queryString))
{
requestURLString = requestURL.toString();
}
else
{
requestURLString = requestURL.append('?').append(queryString).toString();
}
And then I am getting request parameter map like this :
Map<String, String[]> requestParameterMap = request.getParameterMap();
Later on I am filtering any extra request parameter that is part of my requestParameterMap :
if (StringUtils.isNotBlank(request.getQueryString()))
{
Arrays.asList(request.getQueryString().split("&")).stream()
.forEach(p -> params.remove(p.split("=")[0]));
}
And then calling twilo util to calculate the signature :
RequestValidator validator = new RequestValidator(authToken);
isValid = validator.validate(serverURL, params, expectedSignature);
Inspecting my request from Twilio console this is how my request is looking like :
Request : http://7096a2e1.ngrok.io/api/text/textMessage/updateStatus
Parameters :
ApiVersion 2010-04-01
MessageStatus delivered
SmsSid SMa72ee979a8374b218107081b04d7eb9a
SmsStatus delivered
From +17326418522
To +19086160992
MessageSid SMa72ee979a8374b218107081b04d7eb9a
AccountSid AC134c1c93ef7ef868e85ba9264b4024a1
While debugging in my local server I can see request URL and parameter map are all forming as expected.
Can anyone please help here.
Related
I am trying to call a 3rd party service that uses Oauth2 Password Credentials to get an authentication token. Ballerina is returning the following messages.
2020-04-23 15:07:35,414 ERROR [ballerina/oauth2] - Received an invalid response with status-code: 406; and payload: {"fault":{"faultstring":"Raising fault. Fault name : RF.Raise-406-Exception","detail":{"errorcode":"steps.raisefault.RaiseFault"}}}
2020-04-23 15:07:35,418 ERROR [ballerina/oauth2] - Failed to generate OAuth2 token. : error {ballerina/oauth2}Error message=Received an invalid response with status-code: 406; and payload: {"fault":{"faultstring":"Raising fault. Fault name : RF.Raise-406-Exception","detail":{"errorcode":"steps.raisefault.RaiseFault"}}}
error {ballerina/http}AuthenticationFailed message=Failed to prepare request at bearer auth handler. cause=error {ballerina/auth}Error message=Failed to generate OAuth2 token. cause=error {ballerina/oauth2}Error message=Received an invalid response with status-code: 406; and payload: {"fault":{"faultstring":"Raising fault. Fault name : RF.Raise-406-Exception","detail":{"errorcode":"steps.raisefault.RaiseFault"}}}
It's the 406 code that is confusing me as I have set both the content type & accept headers to "application/json" which is what the service requires.
However, the second message says "Failed to generate OAuth2 token" so could it be the call to get the oauth token that is returning the 406? If so how do I set the accept header on the token service call?
Using Ballerina I have called the token endpoint and successfully got a token but if I try to call a service using a PasswordGrantConfig those are the errors I get. I've tried everything I can think of and have successfully got other services using ClientCredentialsGrantConfig to work.
Any help gratefully received.
The relevant code is below. The three sections below are parts of the code in 3 different .bal files.
// configure the Oauth2 Config
import ballerina/config;
import ballerina/http;
import ballerina/oauth2;
public function getOauth2Handler() returns http:BearerAuthHandler {
oauth2:PasswordGrantConfig passwordGrantConfig = {
tokenUrl: config:getAsString("experian.authentication.tokenUrl"),
username: config:getAsString("experian.authentication.username"),
password: config:getAsString("experian.authentication.password"),
clientId: config:getAsString("experian.authentication.clientId"),
clientSecret: config:getAsString("experian.authentication.clientSecret"),
credentialBearer: http:AUTH_HEADER_BEARER
};
oauth2:OutboundOAuth2Provider oauth2Provider = new (passwordGrantConfig);
return new (oauth2Provider);
}
// Configure the API Client
http:ClientConfiguration delphiSelectClientConfig = {
auth: {
authHandler: experian:getOauth2Handler()
}
};
experian:DelphiSelectClientConfig delphiSelectConfig = {
serviceUrl: config:getAsString("experian.services.delphi-select.serviceUrl"),
clientConfig: delphiSelectClientConfig
};
experian:DelphiSelectClient delphiSelectClient = new (delphiSelectConfig);
// Call the endpoint using the Oath2 configuration
import ballerina/http;
import ballerina/io;
public type DelphiSelectClientConfig record {
string serviceUrl;
http:ClientConfiguration clientConfig;
};
//==============================
//============Client============
//==============================
public type DelphiSelectClient client object {
public http:Client clientEp;
public http:ClientConfiguration config;
public function __init(DelphiSelectClientConfig config) {
http:Client httpEp = new (config.serviceUrl, {auth: config.clientConfig.auth});
self.clientEp = httpEp;
self.config = config.clientConfig;
}
public remote function newApplication() returns #untainted json|error {
io:println("In newApplication function");
http:Request request = new;
json requestBody = newApplicationBody; // get test data from json in another file
request.setJsonPayload(requestBody);
var response = check self.clientEp->post("/application", request);
var payload = check response.getJsonPayload();
return payload;
}
};
I have also modified my test code to call the token EP and deliberately set accept to an unacceptable value, for example, "text/csv". In this case I get the same error response. However setting accept to "*/*" does work. Final test; accept of "" (empty) also fails so I suspect that the BearerAuthHandler is not setting any value for accept.
So can I force the BearerAuthHandler to set an accept of "application/json"?
Thanks.
See picture below.
Also, the example in the Oath2 spec you referenced shows a content-type value being set. Even a value of “*/*” would work but I suspect Ballerina leaves it blank.
I have raised the GitHub issue Need to be able to set http header values for OutboundOAuth2Provider
The main objective of http:OutboundAuthHandler objects are to prepare the http:Request with authentication information that needs to be authenticated with external endpoint you are calling to.
The http:BearerAuthHandler is responsible for adding Authorization header with the value of Bearer <token>. "token" is prepared with the provided information. So, there is no option to force http:BearerAuthHandler to set any header for the request.
But in this case, if the API successfully respond if there is Accept header with the value of application/json, you can simply add that header to the http:Request before calling the POST request as follow:
request.addHeader("Accept", "application/json");
I need to send POST request from my UWP app.
I read about it here.
I use one shared HttpClient.
private Windows.Web.Http.HttpClient httpClient;
Initialization:
httpClient = new Windows.Web.Http.HttpClient();
var headers = httpClient.DefaultRequestHeaders;
string header = "Chrome/64.0.3282.140";
if (!headers.UserAgent.TryParseAdd(header))
{
throw new Exception("Invalid header value: " + header);
}
I use this object for all request
But when I use it for POST request, it works like GET request or POST, but without parameters
Uri requestUri = new Uri("http://some_websit.ru");
Dictionary<string, string> pairs = new Dictionary<string, string>();
pairs.Add("par1", "val1");
pairs.Add("par2", "val2");
HttpFormUrlEncodedContent formContent = new HttpFormUrlEncodedContent(pairs)
var result = await httpClient.PostAsync(requestUri, formContent);
string resultContent = await result.Content.ReadAsStringAsync();
It ignore parameters which I give.
I tried to send POST request here http://seriyps.ru/postget/ and it works.
There is nothing wrong with your code, I have tested it locally with the same code, only a different URL, and the POST request is sent properly along with the passed in parameters:
I recommend you to install Telerik Fiddler 4 to see the network traffic and confirm that the parameters are indeed sent. I have used http://example.com just as a sample URL here. I would suspect the problem is rather on the side of the server than your app or that the server expect different parameters than what is being sent.
Using Postman I'm successfully able to query and create tailored audiences using the Twitter API, using Postman's OAuth 1.0 Authorization. However when trying to do the same with RestSharp I get an Unauthorized error.
"UNAUTHORIZED_ACCESS" - "This request is not properly authenticated".
My GET request authenticates fine, but the POST request fails.
_twitterRestClient = new RestClient("https://ads-api.twitter.com/1")
{
Authenticator = OAuth1Authenticator.ForProtectedResource(ConsumerKey, ConsumerSecret, AccessToken, AccessSecret)
};
var restRequest1 = new RestRequest(string.Format("/accounts/{0}/tailored_audiences", TwitterAccountId), Method.GET);
//this works and gives me a list of my tailored audiences
var response1 = _twitterRestClient.Execute(restRequest1);
var restRequest2 = new RestRequest(string.Format("/accounts/{0}/tailored_audiences?name=SampleAudience2&list_type=EMAIL", TwitterAccountId), Method.POST);
// this results in an "Unauthorized" status code , and the message {\"code\":\"UNAUTHORIZED_ACCESS\",\"message\":\"This request is not properly authenticated\"}
var response2 = _twitterRestClient.Execute(restRequest2);
Turns out this is due to a quirk in RestSharp OAuth1 implementation. I think its related to this issue - https://www.bountysource.com/issues/30416961-oauth1-not-specifing-parameter-type . Part of creating an OAuth1 signature involves gathering all the parameters in the request and other details and then hashing it all. It looks like when the HTTP Method is a POST, then RestSharp is not expecting parameters in the querystring (which makes sense), its expecting them in the post body. Anyhow if you add parameters explicitly then they are picked up and the OAuth1 signing works. (Turns out the twitter API works if these params are in the post body, so I didn't need to explicitly add them to the query string). Updated code that now works:
_twitterRestClient = new RestClient("https://ads-api.twitter.com/1")
{
Authenticator = OAuth1Authenticator.ForProtectedResource(ConsumerKey, ConsumerSecret, AccessToken, AccessSecret)
};
var restRequest1 = new RestRequest(string.Format("/accounts/{0}/tailored_audiences", TwitterAccountId), Method.GET);
var response1 = _twitterRestClient.Execute(restRequest1);
var restRequest2 = new RestRequest(string.Format("/accounts/{0}/tailored_audiences", TwitterAccountId), Method.POST);
restRequest2.AddParameter("name", "SampleAudience2");
restRequest2.AddParameter("list_type", "EMAIL");
var response2 = _twitterRestClient.Execute(restRequest2);
As part of our project we need to call Twilio to get E164 format of the Phone Number. In order to do that I followed the below steps to achieve. If the phone number is valid then the below step is working but if the phone number is not valid and we are passing the same to Twillo then it is not returning anything back and halt the process.
I know that the below mention number is not valid but that doesn't mean when I am trying to lookup with invalid number it should not return any response. It should return at least an error but actually it is not which is making me more confused. I am trying with IBM websphere server.
Can some one please help me to fix this issue or is there any way where I can set request time out for twilio lookup so that it will allow other process to continue if any failures in twilio lookup. Also, correct me if I did any mistake in my coding for twilio lookup.
Sample URL "https://lookups.twilio.com/v1/PhoneNumbers/phoneNumber"
Steps Followed:
LookupsClient client = new LookupsClient(ACCOUNT_SID, AUTH_TOKEN);
client.setHttpClient(getProxyClient());
// Twilio Lookup with country code
String twilioURL = createURL(phoneNumber, countryCd);
String twilioE164Number = getTwilioNumber(client, twilioURL);
//Twilio Lookup without country code
StringBuilder appender = new StringBuilder();
appender.append(TWILIO_URL);
appender.append(PLUS);
appender.append(phoneNumber);
String twilioE164Number = getTwilioNumber(client, appender.toString());
//Create lookup URL
private String createURL(String phoneNumber, String countryCode){
StringBuilder appender = new StringBuilder();
appender.append(phoneNumber);
appender.append(QUERY_PARAM);
appender.append(countryCode);
return TWILIO_URL+appender.toString();
}
//Get Twilio E164 Number
private String getTwilioNumber(LookupsClient client,String url){
TwilioRestResponse response;
String twilioResult = "";
try {
response = client.get(url);
String str = response.getResponseText();
//Code for parsing the JSON response and set the twilioResult.
} catch (TwilioRestException e) {
logger.error("TwilioRestException while calling twilio "+e);
}catch(Exception ex){
logger.error("Error in twilio Calling "+ex);
}
return twilioResult;
}
//Get proxy client to set.
private HttpClient getProxyClient() {
HttpHost proxy = new HttpHost(PROXY_HOST, PROXY_PORT);
//new DefaultHttpClient().getParams().setParameter(arg0, arg1)
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT),new UsernamePasswordCredentials(ACCOUNT_SID, AUTH_TOKEN));
CloseableHttpClient httpClient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.setDefaultCredentialsProvider(credentialsProvider)
.build();
return httpClient;
}
v6.3.0 of twilio-java has a LookupsClient has a .getPhoneNumber() method that allows you to pass a phone number instead of requiring that you construct the URL from scratch. The whole point of using LookupsClient instead of a vanilla TwilioClient is that the class does the bulk of the work for you.
Have you tried client.getPhoneNumber(phoneNumber) in this case? This page also demonstrates the structure of the response that you should receive when using that particular endpoint. The E164-formatted phone number should be the phone_number property in the response, although I use twilio-python and I don't know how the Java library exposes response objects for your consumption.
If you aren't getting a response at all, you might want to try making the same request without your proxy configuration to make sure the proxy isn't causing the problem. As for your timeout question, twilio-java's requests are processed synchronously, one way to get around that would be to wrap the request in an asynchronous method call.
Hope some of this helps you diagnose your issue!
twilio javascript client set from number , Also how I can get the call sid after connect?
I tried to set the from Number in the call options like the next lines before connect and still the same issue in the javascript
$(document).ready(function () {
Twilio.Device.setup(token);
var connection = null;
$("#call").click(function () {
var params = { "Phone": $('#Phone').val(), "from":$('#Phone').val() };
connection = Twilio.Device.connect(params);
return false;
});
});
-- and inside the server side code vbnet when I am generating the token I added the next code but this doesn't solve the from number issue
Dim options As New CallOptions()
options.Record = True
options.From = model.FromNumber
Dim cap = New TwilioCapability(_settings.AccountSID, _settings.AuthToken)
cap.AllowClientOutgoing(_settings.ClientCallApplicationSID, options)
dim token = cap.GenerateToken()
Twilio evangelist here.
The params collection that you pass into the connect function is just a dictionary of key/value pairs. Those key/values simply get passed as parameters to the Voice URL that Twilio requests when Client makes its connection to Twilio, and you can use those parameters to dynamically generate some TwiML markup. Twilio does not do anything with them.
For example, if this is a PHP application, in the Voice URL you could do something like:
<Response>
<Dial>$_REQUEST['From']</Dial>
</Response>
One note of caution, Twilio already adds a parameter called from (which in the case of Client will be the client identifier set when you made your capability token) to the parameters sent to the Voice URL, so you might want to choose a different key name for your dictionary entry. I normally use a name like target for the key that holds the number that I want to dial.
Hope that helps.
To get the call sid, you can get it in connect event.
Please note that I am using Twilio JS v1.9.7
device.on('connect', function (conn) {
log('Successfully established call!');
//Get the CallSid for this call
callUUID = conn.outboundConnectionId;
console.log('CallSid: ' + callUUID);
});