I invoke a RESTful webserice via a method GET url with paramters from google apps script successfully using the following:
UrlFetchApp.fetch(url);
But when I switch the method to use POST, the result returned tells that no parameter was sent:
UrlFetchApp.fetch(url, options);
I also tried to use Google Chrome's (Advanced) Rest Client, and the payload parameters are not sent there as well.
What am I missing?
Below is the Google Chrome's (Advanced) Rest Client interface.
You mention in your comments that you are converting the payload you send to JSON.
First, are you certain the remote service is expecting a JSON payload, rather than form encoded data?
If the service expects form encoded data, don't JSON.stringify your payload:
var data = {"x1": "1", "x2": "2"};
var options = {method : "post", payload : data};
If the service does expect a JSON payload, you need to set the content type:
var data = {"x1": "1", "x2": "2"};
data = JSON.stringify(data);
var options = {method : "post", payload : data, contentType:"application/json"};
Otherwise the default content type of 'application/x-www-form-urlencoded' is used.
See the description on "contentType" under "Advanced Parameters": https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app
Related
I know this is a particular issue that I'm having but I'm struggling with it for ages...If anyone could give me a hint on how to proceed It would be great.
I'm trying to integrate my flutter app to Spotify API and according to Spotify docs, after the user accepted the conditions at their site, it would be redirected back with a code - In my application I'm already retrieving this code. After that, I need to make a request for another endpoint to "trade" my code for an auth.
The docs demand a POST with 3 parameters(grant_type,redirect_uri and code). But even though I send all of this informations I get a (400 -Bad Request) with the description:
{"error":"invalid_request","error_description":"code_verifier
required"}
But note that code_verifier is only required for a PKCE request, as far as I understand.
My post method is the following:
Future getUserAccessToken(String authorizationCode) async {
// Get the Access Token in exchange for the Authorization Code
var url = 'https://accounts.spotify.com/api/token';
var body = {
'grant_type': 'authorization_code',
'code': authorizationCode,
'redirect_uri': redirectUrl,
};
var response = await post(Uri.parse(url), body: body, headers: {
'Content-Type': "application/x-www-form-urlencoded",
'Authorization':
'Basic ${base64Encode(utf8.encode('$clientID:$clientSecret'))}',
});
}
They also demand this at the authorization header:
Base 64 encoded string that contains the client ID and client secret key. The field must have the format: Authorization: Basic
I would recommend going forward with PKCE. It will protect your app against CSRF and authorization code injection attacks. It's also not too bad to implement.
All you should have to do is:
Generate a random string using the characters A-Z, a-z, 0-9, and the punctuation characters -._~ (hyphen, period, underscore, and tilde), between 43 and 128 characters long. This is your code_verifier
Hash that string with SHA256. This is your code_challenge.
Send those values in the requests as the documentation indicates, which means base64 url encoding them, as well. The Spotify doc says "The body of this POST request must contain the following parameters encoded in application/x-www-form-urlencoded".
Also, in case you haven't seen it, there's a OAuth flow example implemented in JS here: https://github.com/spotify/web-api-auth-examples/blob/master/authorization_code/app.js Their copy of this portion looks like:
var authOptions = {
url: 'https://accounts.spotify.com/api/token',
form: {
code: code,
redirect_uri: redirect_uri,
grant_type: 'authorization_code'
},
headers: {
'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64'))
},
json: true
};
request.post(authOptions, function(error, response, body) {...})
The example has "request": "~2.83.0" in the package.json.
Notice how they use form which I would assume is base 64 url encoding the body, as the docs indicate. Not sure how the post method you're using with dart/flutter works with respect to base 64 url encoding, so that's something to investigate, as well.
Credit to https://www.oauth.com/oauth2-servers/pkce/authorization-request/ for providing some of the text in this response
In one of my Ionic 2 projects I need to send a POST request to a server with a JSON body that looks like this:
var body = { "prop" : 1,
"prop2" : "Test",
"prop3": [{ "id" : "1", "qty": 1, "details": "Test" }]
}
I am using the following code to call the server using the native HTTP plugin (1.2.0) in Android:
http.post(url, body, {}).then(function() { ... })
But my server is receiving the following:
{ "prop" : 1,
"prop2" : "Test",
"prop3": "[{ \"id\" : \"1\", \"qty\": 1, \"details\": \"Test\" }]"
}
As you can see, the array property "prop3" is being turned into a string, so my server is failing to parse it because it's expecting an array, not a string.
One of the things I could do is to change the server side code to parse this string back into an array (but that would be far from ideal). The other thing I could do is to parse the JSON object manually with JSON.stringify.
So, is this just a bug in the plugin or am I missing something here?
Native HTTP plugin
Try set http.setDataSerializer("json");
And send data as usual: http.post(url, body, {})
Then http plugin will send data with application/json content type and support deep structure of json, as stated in the documentation:
https://github.com/silkimen/cordova-plugin-advanced-http#setdataserializer
So, after taking a look at the plugin's source code (the Java one, I'm testing my application in Android) it seems that I won't be able to use the plugin as is (I would need to modify it). What I found was this:
In CordovaHttpPost.java, the body of the request is sent as Form data (simple key-values).
request.form(this.getParams()); //Map<?, ?>
That's why my array property is converted into a string (and any other complex object for that matter)
TL;DR this plugin is only useful to send simple JSON key-value objects (no nesting, no complex objects, no arrays, etc.).
I am having difficulties trying to figure out a way to send uploaded file and send it to a Gateway server. I am using the basic FileUploader control.
***<u:FileUploader
id="fileUploader"
name="myFileUpload"
uploadUrl="upload/"
width="400px"
tooltip="Upload your file to the local server"
uploadComplete="handleUploadComplete"/>
<Button
text="Upload File"
press="handleUploadPress"/>***
And in the controller I have the following event handler
***handleUploadPress: function(oEvent) {
var oFileUploader = this.getView().byId("fileUploader");
oFileUploader.upload();
}***
What code should I add after oFileUploader.upload() to have an xstring that I can pass to my attachment property of my OData srvice
Thank you
The first thing to do is to make sure that you have a gateway service that is able to handle media types and streams. To set this up you need to set the entity that is handling the file content as a media type and get the logic in place that deals with streams (CREATE_STREAM). You can find more information on how to do this in this SCN blog.
In your UI5 application, you will have to set the URL of the upload control to e.g. /sap/opu/odata/sap/AWESOME_SERVICE/Customers('0000123456')/$value so that the file is handled by the CREATE_STREAM method that you just implemented.
When the upload is eventually taking place, you need to deal with two header parameters; the slug and the CSRF token. The slug header should be set to e.g. the filename, while the CSRF token needs to be retrieved using a pre-flight request. To set the headers, you could use something like this:
oFileUploader.addHeaderParameter(new FileUploaderParameter({
name: "x-csrf-token",
value: _csrfToken
}));
The slug header parameter could be set in a similar way and should contain something that identifies the file, e.g. filename or id.
To determine the CSRF token, you could do something like this:
var _csrfToken = "";
jQuery.ajax({
url: "/sap/opu/odata/sap/AWESOME_SERVICE",
headers: {
"X-CSRF-Token": "Fetch",
"X-Requested-With": "XMLHttpRequest",
"DataServiceVersion": "2.0"
},
type: "GET",
contentType: "application/json",
dataType: 'json',
success: function(data, textStatus, jqXHR) {
_csrfToken = jqXHR.getResponseHeader('x-csrf-token');
}
});
With the right header parameters in place, sending the file to a properly configured gateway entity should do the trick to get your file uploaded.
I am developing a new REST-full webservice for our application,I wanted to send the reqest data in requestHeader instead of sending as query param, as my request data is large.
I have My jquery code like below to add json to the request header and call the REST service GET method.
$.ajax({
beforeSend: function(req) {
req.setRequestHeader("test", "{name:mouli, id:918}");},
type : "GET",
data :'',
dataType : "jsonp",
url : 'http://localhost:29801/RestFulJSONExample/rest/jobdesc?callback=?',
success : function(data) {
alert("invoked");
}
});
});
And my GET method in my REST service is like
#GET
#Produces("application/javascript")
public JSONWithPadding getJobDescription(#Context HttpHeaders headers) {
List<String> requestHeader = headers.getRequestHeader("test");
//some logic here.
}
i could able to get the JSON object from the request header which i have added in the jquery request.
My question is ..
Can i follow this approach? is it secure and safe?
If not please tell me the other way?
What appears at the right side of the ":" in a header is mostly free. You have to take into account character set restriction in HTTP, and possible carriage returns in the JSON value (you know, headers of more than one line have a specific syntax). If your JSON examples are relatively simple, then I see no problem in that. It is just another way of organizing the actual value of a header line.
How to add a parameters to the Google OAuth 2.0 redirect_uri?
Just like this:
redirect_uri=http://www.example.com/redirect.html?a=b
The b of a=b is random.
Anyone can help ?
You cannot add anything to the redirect uri, redirect uri is constant as set
in the app settings of Oauth.
eg :http://www.example.com/redirect.html
To pass several parameters to your redirect uri, have them stored in state
parameter before calling Oauth url, the url after authorization will send the same parameters to your redirect uri as
state=THE_STATE_PARAMETERS
So for your case,do this:
/1. create a json string of your parameters ->
{ "a" : "b" , "c" : 1 }
/2. do a base64UrlEncode , to make it URL safe ->
stateString = base64UrlEncode('{ "a" : "b" , "c" : 1 }');
This is a PHP example of base64UrlEncoding & decoding (http://en.wikipedia.org/wiki/Base64#URL_applications) :
function base64UrlEncode($inputStr)
{
return strtr(base64_encode($inputStr), '+/=', '-_,');
}
function base64UrlDecode($inputStr)
{
return base64_decode(strtr($inputStr, '-_,', '+/='));
}
So now state would be something like: stateString -> asawerwerwfgsg,
Pass this state in OAuth authorization URL:
https://accounts.google.com/o/oauth2/auth?
client_id=21302922996.apps.googleusercontent.com&
redirect_uri=https://www.example.com/back&
scope=https://www.google.com/m8/feeds/&
response_type=token&
state=asdafwswdwefwsdg,
For server side flow it will come along with token :
http://www.example.com/redirect.html?token=sdfwerwqerqwer&state=asdafwswdwefwsdg,
For client side flow it will come in the hash along with access token:
http://www.example.com/redirect.html#access_token=portyefghsdfgdfgsdgd&state=asdafwswdwefwsdg,
Retrieve the state, base64UrlDecode it, json_decode it, and you have your data.
See more about google OAuth 2 here:
http://code.google.com/apis/accounts/docs/OAuth2.html
Since the accepted answer does expose the actual data and misuses the state parameter instead of sticking to a nonce to protect against CSRF, I'll try to show a proper method. Rather than passing (read exposing) data it should be kept local. Hydrate it before the request and re-hydrate it after a validated request. "Validated" here means that the state-nonce of request and response match.
You need some kind of temporary client side storage. E.g. for SPA or general websites keep it in state or use the browser's localStorage, a session (or a signed cookie). For mobile apps they should use memory or any other local storage.
Before sending the request generate a nonce (see below) that will be used as state parameter for the request. Store the nonce together with the custom state (e.g. a json) in local storage.
For example, the nonce could be ih4f984hf and the custom state {"role": "customer"}. Then you could store data for re-hydration for that request like this:
"ih4f984hf": {
"role": "customer"
}
Then use only the nonce as value for the state parameter of the request. (If you absolutely want to combine the nonce and data into the state value be sure to encrypt it and be aware that the length of the value is limited!)
When receiving a response you get the value of the state parameter back. Look it up and if it matches the value in the local storage you may process the data using the stored state. If the nonces do not match the request is potentially from an attacker and should not be processed.
Generating the nonce
Remember that the nature of a nonce is that it is used once only and must be unpredictable! Unpredictable here means ideally random, but practically pseudo-random is ok if the entropry is high enough - in web apps you might want to check Web API Crypto which is supported pretty well.
For further readings this might be helpful:
http://www.thread-safe.com/2014/05/the-correct-use-of-state-parameter-in.html
https://datatracker.ietf.org/doc/html/draft-bradley-oauth-jwt-encoded-state-00
https://auth0.com/docs/protocols/state-parameters#set-and-compare-state-parameter-values
If you are in .NET you could save the parameters in the Session
HttpContext.Current.Session[{varname}]
and redirect to the authorization page without parameters
Response.Redirect(your_uri_approved_with_no_querystring_parameters);
In Javascript (Node), you could set the state property to an object of key value pairs.
const oAuth2Client = await new google.auth.OAuth2(
clientId: <clientId>,
clientSecret: <clientSecret>,
redirectUrl: <redirectUrl>,
);
return await oAuth2Client.generateAuthUrl({
access_type: "offline",
scope: <scopes>,
state: JSON.stringify({ a: "y", b: "z" }),
});
On google authorization complete, it returns of the state, code etc from ulr,
const params = JSON.parse(state); // { a: "y", b: "z" }
You can redirect parameter with url as below,
When you get response from google than you can pass parameter with url,
See below php code for same,
if (isset($_GET['code'])) {
$client->authenticate();
$_SESSION['token'] = $client->getAccessToken();
$redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL) . '?r=page/view');
}
In above example r=page/view is parameter on which i want the response with parameter