Calculating an oauth signature - rest

I am trying something a little specific, namely trying to call a REST API. I have been following these instructions.
I have been very careful to ensure that I am creating the "Signature base string" correctly. They define it to be created like this:
(HTTP Method)&(Request URL)&(Normalized Parameters)
You can double check if need be in my code, but I am very sure that it is fine.
The problem that I am having is creating what they call the "oauth signature" and mine isn't matching theirs. They it should be created like this:
Use the HMAC-SHA1 signature algorithm as defined by the [RFC2104] to sign the request where text is the Signature Base String and key is the concatenated values of the Consumer Secret and Access Secret separated by an '&' character (show '&' even if Access Secret is empty as some methods do not require an Access Token).
The calculated digest octet string, first base64-encoded per [RFC2045], then escaped using the [RFC3986] percent-encoding (%xx) mechanism is the oauth_signature.
I express this in my code like so:
var oauthSignature = CryptoJS.HmacSHA1(signatureBaseString, sharedSecret+"&");
var oauthSignature64 = encodeURIComponent(CryptoJS.enc.Base64.stringify(oauthSignature));
console.log("hash in 64: " + oauthSignature64);
I am using Google's CryptoJS library. I take the signature base string as the text, I then take my consumer secret as the key concatenated with "&", I have no Access key and it isn't required but that is OK. I then base 64 encode the result of that hash, after which I URI encode it, please could some guys sanity check my understanding of that and my usage/expressing of it in code using this library, I think this is where my problem is.
Here is my full code:
var fatSecretRestUrl = "http://platform.fatsecret.com/rest/server.api";
var d = new Date();
var sharedSecret = "xxxx";
var consumerKey = "xxxx";
//this is yet another test tyring to make this thing work
var baseUrl = "http://platform.fatsecret.com/rest/server.api?";
var parameters = "method=food.search&oauth_consumer_key="+consumerKey+"&oauth_nonce=123&oauth_signature_method=HMAC-SHA1&oauth_timestamp="+getTimeInSeconds()+"&oauth_version=1.0&search_expression=banana";
var signatureBaseString = "POST&" + encodeURIComponent(baseUrl) + "&" + encodeURIComponent(parameters);
console.log("signature base string: " + signatureBaseString);
var oauthSignature = CryptoJS.HmacSHA1(signatureBaseString, sharedSecret+"&");
var oauthSignature64 = encodeURIComponent(CryptoJS.enc.Base64.stringify(oauthSignature));
console.log("hash in 64: " + oauthSignature64);
var testUrl = baseUrl+"method=food.search&oauth_consumer_key=xxxx&oauth_nonce=123&oauth_signature="+oauthSignature64+"&oauth_signature_method=HMAC-SHA1&oauth_timestamp="+getTimeInSeconds()+"&oauth_version=1.0&search_expression=banana";
console.log("final URL: " + testUrl);
var request = $http({
method :"POST",
url: testUrl
});
I have taken care to ensure that the parameters that I am posting are in lexicographical order and I am very sure that it is correct.
The response that I am getting back is:
Invalid signature: oauth_signature 'RWeFME4w2Obzn2x50xsXujAs1yI='
So clearly either
I haven't understood the instructions provided in the API
Or I have understood them but I haven't expressed them in that way in my code
Or both of the above
Or I have made some subtle mistake somewhere that I can't see
I would really appreciate a sanity check, this has taken a while.

well...I did it, but not the way that I thought I would end up doing it, I spent hours trying it out with angular then JQuery, then finally I tried Node JS and it worked, here are two working examples, one with food.get and another with foods.search
food.get example
var rest = require('restler'),
crypto = require('crypto'),
apiKey = 'xxxx',
fatSecretRestUrl = 'http://platform.fatsecret.com/rest/server.api',
sharedSecret = 'xxxx',
date = new Date;
// keys in lexicographical order
var reqObj = {
food_id: '2395843', // test query
method: 'food.get',
oauth_consumer_key: apiKey,
oauth_nonce: Math.random().toString(36).replace(/[^a-z]/, '').substr(2),
oauth_signature_method: 'HMAC-SHA1',
oauth_timestamp: Math.floor(date.getTime() / 1000),
oauth_version: '1.0'
};
// make the string...got tired of writing that long thing
var paramsStr = '';
for (var i in reqObj) {
paramsStr += "&" + i + "=" + reqObj[i];
}
// had an extra '&' at the front
paramsStr = paramsStr.substr(1);
var sigBaseStr = "GET&"
+ encodeURIComponent(fatSecretRestUrl)
+ "&"
+ encodeURIComponent(paramsStr);
// no access token but we still have to append '&' according to the instructions
sharedSecret += "&";
var hashedBaseStr = crypto.createHmac('sha1', sharedSecret).update(sigBaseStr).digest('base64');
// Add oauth_signature to the request object
reqObj.oauth_signature = hashedBaseStr;
rest.get(fatSecretRestUrl, {
data: reqObj,
}).on('complete', function(data, response) {
console.log(response);
console.log("DATA: " + data + "\n");
});
foods.search example
var rest = require('restler'),
crypto = require('crypto'),
apiKey = 'xxxx',
fatSecretRestUrl = 'http://platform.fatsecret.com/rest/server.api',
sharedSecret = 'xxxx',
date = new Date;
// keys in lexicographical order
var reqObj = {
method: 'foods.search',
oauth_consumer_key: apiKey,
oauth_nonce: Math.random().toString(36).replace(/[^a-z]/, '').substr(2),
oauth_signature_method: 'HMAC-SHA1',
oauth_timestamp: Math.floor(date.getTime() / 1000),
oauth_version: '1.0',
search_expression: 'mcdonalds' // test query
};
// make the string...got tired of writing that long thing
var paramsStr = '';
for (var i in reqObj) {
paramsStr += "&" + i + "=" + reqObj[i];
}
// had an extra '&' at the front
paramsStr = paramsStr.substr(1);
var sigBaseStr = "POST&"
+ encodeURIComponent(fatSecretRestUrl)
+ "&"
+ encodeURIComponent(paramsStr);
// again there is no need for an access token, but we need an '&' according to the instructions
sharedSecret += "&";
var hashedBaseStr = crypto.createHmac('sha1', sharedSecret).update(sigBaseStr).digest('base64');
// Add oauth_signature to the request object
reqObj.oauth_signature = hashedBaseStr;
rest.post(fatSecretRestUrl, {
data: reqObj,
}).on('complete', function(data, response) {
console.log(response);
console.log("DATA: " + data + "\n");
});
really sorry to anyone using Angular or JQuery, if I ever have a spare minute or two I will try it with angular, also anyone using angular if you get CORS related errors just start chrome like this:
chromium-browser --disable-web-security - I do this on terminal
or add that extension to some chrome shortcut on windows, just as a quick work around, hope it helps anybody out there.

Related

How to have an empty string as a parameter value in Flutter/Dart?

Suppose you want to send an empty search query to an API. For example:
https://api.example.com/search?query=''
In Dart, it makes sense to create this URI with:
final uri = Uri.https('api.example.com', 'search', {'query': ''});
But if you try to print this out: print(uri); you will see:
https://api.example.com/search?query
My current workaround is to wrap double-quotes inside single-quotes (or other way around):
final uri = Uri.https('api.example.com', 'search', {'query': '""'});
which produces this URI:
https://api.example.com/search?query=%22%22
Not a big deal. But still this behavior surprised me as a bit illogical. Who would want to send only a parameter name and not its value? Is it a problem of http library or am I missing something here? Is it the intended behavior and actually serves a purpose?
Was looking for the same use case too. Looked around and ended up with this with some inspiration from the plain javascript implementation.
void main() {
String fullURL = 'http://api.example.com/search';
Object queryParameters = {'query': '', 'colour': 'green', 'name': ''};
String queryString = getQueryString(queryParameters);
fullURL += '?' + queryString;
print(fullURL); // http://api.example.com/search?query=&colour=green&name=
}
getQueryString(object) {
var str = [];
object.forEach(
(k, v) => str.add(Uri.encodeComponent(k) + "=" + Uri.encodeComponent(v)),
);
return str.join("&");
}

can't get ADLS Gen2 REST continuation token to work

I'm trying to retrieve list of files and folders form ADLS Gen2. I can get the first 5000 items, but when I use continuation to get the rest (about 17,000 items or so), I get Error 403 (Forbidden). According to documentation, I add the continuation token to URI and to Canonicalized Resource in signature string. However, I cannot get it to work.
I've read the documentation on ADLS Gen2 REST calls and whatever I could find on this, and I can't figure out the issue.
var date = System.DateTime.UtcNow.ToString("R");
string toSign = DefaultSignatureString(date);
toSign +=
$"/{storageaccountname}/{filesystemname}" + "\n" +
$"directory:{dir}" +"\n" +
"recursive:true" + "\n" +
"resource:filesystem";
var signedSignature = SignData(accessKey, toSign);
var uri = $"https://{storageaccountname}.dfs.core.windows.net/{filesystemname}?directory={dir}&recursive=true&resource=filesystem";
HttpWebResponse response = GetWebResponse(storageaccountname, date, signedSignature, uri);
var token_continuation = response.Headers["x-ms-continuation"];
//I get the token_continuation and repeat the previous steps, adding the continuation part:
while (token_continuation != null)
{
date = System.DateTime.UtcNow.ToString("R");
toSign = DefaultSignatureString(date);
toSign +=
$"/{storageaccountname}/{filesystemname}" + "\n" +
$"continuation:{token_continuation}" + "\n" +
$"directory:{dir}" + "\n" +
"recursive:true" + "\n" +
"resource:filesystem";
signedSignature = SignData(accessKey, toSign);
uri = $"https://{storageaccountname}.dfs.core.windows.net/{filesystemname}?directory={dir}&recursive=true&resource=filesystem&continuation={token_continuation}";
response = GetWebResponse(storageaccountname, date, signedSignature, uri);
token_continuation = response.Headers["x-ms-continuation"];
}
//this is my GetWebResponse method
private static HttpWebResponse GetWebResponse(string storageaccountname, string date, string signedSignature, string uri, string continuation = null)
{
WebRequest request = WebRequest.Create(uri);
if (continuation != null)
{
request.Headers.Add($"x-ms-continuation:{continuation}");
}
request.Headers.Add($"x-ms-date:{date}");
request.Headers.Add($"x-ms-version:2018-11-09");
request.Headers.Add($"Authorization:SharedKey {storageaccountname}:{signedSignature}");
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
return response;
}
As I said, I get the first response OK; when I get into while loop, I get the error. What am I doing wrong?
In most of the cases (if not all) continuation token returns with == at the end, which messes up the URI.
For URIs, escape them by replacing == with %3D%3D.
For canonicalized resources, leave the string as is.

Is it possible to send page content via email using UI5?

My application is designed to create a table which is later edited by the user. After this I need my application to send the page content via email.
I used URLHelper's trigger email() but through this I am able to trigger the email with to, cc, subject, text body but my ui5 application is not able to insert the table into the email.
Can someone please suggest something? or is it even possible?
I won't mind using plain javascript either, Point is I need to do this without using the backend.
We do something similar on one of our apps. I added a button to the screen which when clicked invokes a 'mailto', and populates the email client with the to, subject and body. The body is created as part of the script. We basically read the table contents into an array, then loop through the entries using a forEach. Keep in mind using mailto or even the URLHelper does not allow you to use HTML formatted text in the 'body' of the email. So, if you're looking for something pretty, you may be out of luck.
onNotifyUserPress: function(oEvent) {
var oItem = oEvent.getSource();
var oBinding = oItem.getBindingContext();
// Set some vars for the email package
var sEmpEmail = oBinding.getProperty("Smtp");
var sEmpName = oBinding.getProperty("STEXT_2");
var sEmailSubject = "Your Subject " + sEmpName;
// Create DateFormat Object
var oDateFormat = DateFormat.getDateTimeInstance({pattern: "dd/MM/yyyy"});
// Retrieve Table Data
var oTable = this.getView().byId("yourTable");
var aTableData = oTable.getBinding("items").getContexts();
// Build the email body
var sBody = sEmpName + " - Some Body Text\n\n";
sBody += "Field 1 | " + "Field 2 | " + "Field 3 | " + "Field 4" + "\n";
// Loop through table data and build the output for the rest of the email body
aTableData.forEach(function(oModel) {
var oModelData = oModel.getObject();
var sEndDate = oDateFormat.format(oModelData.Vendd);
var sStatus = this._formatStatus(oModelData.ZQ_STAT);
sBody += (oModelData.Essential === "X" ? "Yes" : "No") + " | " + oModelData.Ttext + " | " + sEndDate + " | " + sStatus + "\n";
}.bind(this));
// Open email client window and prepopulate with info
window.open("mailto:" + sEmpEmail + "&subject=" + sEmailSubject + "&body=" + encodeURIComponent(sBody), "_self");
},
You'll obviously need to update the code to point to your table data. In this particular instance, we have an object page with a couple of sections. Each section contains a table which loads a list of entities that are associated with the user. As the data is already loaded and exists in the model, this may not work in the same fashion as what you're trying to do (if I understand correctly), as you need to send an email after the data is entered/modified?
Hopefully this can at least get you started!
Cheers!

Generate and download file from Word dialog

Is there a way to generate a file with some JSON content and prompt the user with a saveAs dialog.
This is from an open dialog in word.
The object could be like (will be quite a lot bigger in practice)
var obj = {a: 1, b: 2, c: 'qwerty'}
I tried to uri encode and using window.open without any luck.
content = JSON.stringify(obj);
uriContent = "data:application/octet-stream," + encodeURIComponent(content);
newWindow = window.open(uriContent, 'filename');
I did this for XML, but it requires some back-end and front-end work. On the backend, you need some ASPX like this:
string filename = "export.xml";
byte[] data = Convert.FromBase64String(Request.QueryString[0].Replace("\"", ""));
string decodedString = System.Text.Encoding.UTF8.GetString(data);
// set the http content type to "APPLICATION/OCTET-STREAM
Response.ContentType = "APPLICATION/OCTET-STREAM";
System.String disHeader = "Attachment; Filename=\"" + filename + "\"";
Response.AppendHeader("Content-Disposition", disHeader);
Response.Flush();
Response.Write(decodedString);
I called mine "download.aspx." Then on the front-end, you have to use AJAX. This created a form region on the page and the forces a submit of the form to start the download:
// Helper function to load a form and then send the post results to a
// new window so that we can get the download button
function ajax_download(url, data, input_name) {
try {
$('#form-div').append("<form method='GET' action='" +
url + "' target='_blank'>" +
"<input type=hidden name='" + input_name + "' value='" +
JSON.stringify(data) + "'/></form>");
$('#form-div').find('form').submit();
} catch (err) {
showNotification("error", err.description);
}
}
To call it, you simply make this this call from your JavaScript code where you want it:
xml = "<xml><data>12345</data></xml>";
ajax_download('./Download.aspx', btoa(xml), 'xml');
In this case, mine is targeting XML and always creates a file called "export.xml", but you can adjust it as needed.

Unable to add picture with url parameters on Facebook feed dialog (Direct URL)

I was wondering how could I add parameters to my picture URl on this example:
https://www.facebook.com/dialog/feed?app_id=...&link=...&picture=www.blablabladotcom?parameter1=1&parameter2=2&name=...&caption=...&description=...&redirect_uri=...
I tried with %26 encoding and picture doesn't show up.
What is strange is that when I try %26 on the redirect_uri parameter, it works fine.
Any tips about this one??
You need to encode all the parts properly (example Java Script code below):
var _FBAPPID = "xxxxxxxxxx", // your app ID (mandatory)
_name = "name",
_text = "text",
_link = "link",
_picture = "http://cdn2.insidermonkey.com/blog/wp-content/uploads/2012/10/facebook4-e1349213205149.jpg", // some google image - replace it
_caption = "caption",
_redirect_uri = "http://google.com" // this URL must be from within the domain you specified in your app's settings
var _params = "&name=" + encodeURIComponent(_name)
+ "&description=" + encodeURIComponent(_text)
+ "&link=" + encodeURIComponent(_link)
+ "&picture=" + encodeURIComponent(_picture)
+ "&caption=" + encodeURIComponent(_caption)
+ "&redirect_uri=" + encodeURIComponent(_redirect_uri);
var _href = "http://www.facebook.com/dialog/feed?app_id=" + _FBAPPID + _params + "&display=touch";
I have also added display=touch because I'm using direct URL only for mobile devices.
Source is here.