How to make an httpRequest from jobs in parse cloud code? - javascript

Parse.Cloud.job("syncMetadataWithContentPortal", function(request, status) {
var apikey ="49eiivmz";
var uid = "t1g4Y2jC6S";
Parse.Cloud.httpRequest({
url: 'https://api.parse.com/1/functions/getContentMetaData',
method: 'GET',
headers : {
'Content-Type': 'application/json',
'X-Parse-Application-Id':'appkey',
'X-Parse-REST-API-Key':'restapikey',
},
body: {
apiKey : apikey
}
}).then(function(httpResponse) {
Parse.Cloud.useMasterKey();
status.message(httpResponse.text);
console.log(httpResponse.text);
var contents = JSON.parse(httpResponse.text);
var contentIdCollection = [];
for (var i = 0; i < contents.length; i++) {
contentIdCollection.push(contents[i].id);
}
status.success('Content Synced');
}, function(httpResponse) {
// console.error('Request failed with response code ' + httpResponse.status);
status.error('Request failed with response code ' + httpResponse.status)
});
});
So I have a job making httpRequest to call a function getContentMetaData which requires API key as a parameter.
How do I send parameters using GET method?
I got status as :Request failed with response code 405
Please help me how to solve this. Thanks in advance.

Don't use Parse.Cloud.httpRequest to call other cloud functions, instead you should be using Parse.Cloud.run
Your problem could also be related to your headers as you appear to be using string literals instead of variable references.

Related

Google Classroom API 401 Error

I am trying to create a Google Classroom course using the Google Classroom API and a service account. I am currently experimenting using JavaScript and I have everything set up and working to get a list of course. I set up a JWT and request an authentication token which I receive.
{"access_token":"----ACCESS TOKEN HERE----------","token_type":"Bearer","expires_in":3600}
When I use this to retrieve a user's course list (via GET) there is no problem. I receive back a proper response with a list of courses which I then display in a table.
When I try to use the same process to try to create a course (via POST), I get a 401 error:
{
"error": {
"code": 401,
"message": "The request does not have valid authentication credentials.",
"status": "UNAUTHENTICATED"
}
}
This is the code I use to authenticate:
function authenticate(callback) {
function b64EncodeUnicode(str) {
str = JSON.stringify(str);
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
return String.fromCharCode('0x' + p1);
}));
}
// constuct the JWT
var jwtHeader = {
"alg":"RS256",
"typ":"JWT"
}
jwtHeader = JSON.stringify(jwtHeader);
//construct the Claim
var jwtClaim = {
"iss":"psclassroomsync#psclassroomsync.iam.gserviceaccount.com",
"scope":"https://www.googleapis.com/auth/classroom.courses https://www.googleapis.com/auth/classroom.rosters",
"sub":"myemail#address.com", //this is an admin account I shouldn't really need this but tried with and without it
"aud":"https://www.googleapis.com/oauth2/v4/token",
"exp":(Math.round(new Date().getTime()/1000) + 60 * 10),
"iat":Math.round(new Date().getTime()/1000)
}
jwtClaim = JSON.stringify(jwtClaim);
//construct the signature
var key="-----BEGIN PRIVATE KEY-----Removed-----END PRIVATE KEY-----\n";
var jwtSign = b64EncodeUnicode(jwtSign);
var sJWT = KJUR.jws.JWS.sign("RS256", jwtHeader, jwtClaim, key);
var jwt = jwtHeader + "." + jwtClaim + "." + sJWT;
//request Token
var grantType = "urn:ietf:params:oauth:grant-type:jwt-bearer";
var tokenRequest = "grant_type=" + grantType + "&assertion=" + sJWT;
var postURL = "https://www.googleapis.com/oauth2/v4/token"
request = $j.ajax({
url: postURL,
type: "post",
data: tokenRequest,
success: callback
});
}
This is the code I use to GET the course list. (this works)
$j("#getClasses").click(function(event){
function getClasses(callback){
authenticate(function(data){
console.log(JSON.stringify(data));
var access_token = data["access_token"];
var apiUrl = 'https://classroom.googleapis.com/v1/courses'
var myData = 'teacherId=~(teacheremail)&access_token='+access_token;
var files = $j.ajax({
url: apiUrl,
type: "get",
data: myData,
success: function (data) {
var retreivedClasses = JSON.stringify(data);
for(var i = 0; i < data['courses'].length; i++){
nextObject = data['courses'];
$j('#classListTable').append('<tr><td>' + nextObject[i]['name'] + '</td><td>' + nextObject[i]['courseState'] + '</td><td>' + nextObject[i]['enrollmentCode'] + '</td></tr>');
}
//$j('#classList').text(retreivedClasses);
}
});
});
}
getClasses();
});
This is the code that I use to create a course via POST. I've hard coded a few of the variables for testing but still gives the 401 error.
$j("#createClass").click(function(event){
function createClass(callback){
authenticate(function(data){
console.log(JSON.stringify(data));
var access_token = data["access_token"];
var tokenInfo = $j.ajax({
url: 'https://www.googleapis.com/oauth2/v3/tokeninfo',
type: 'get',
data: "access_token="+access_token
});
var apiUrl = 'https://classroom.googleapis.com/v1/courses'
var myData = 'access_token='+access_token + '&ownerId=myemail#address.com&name=myClass'
console.log(myData);
var newGoogleClassroom = $j.ajax({
url: apiUrl,
type: "post",
data: myData,
success: function (data) {
var apiResponse = JSON.stringify(data);
$j('#classCreated').text(apiResponse);
}
});
});
};
createClass();
});
Finally, this is what I get when I get the token info. It looks fine to me i.e. proper scopes: (but I am new at this)
{
"azp": "removed",
"aud": "removed",
"scope": "https://www.googleapis.com/auth/classroom.courses https://www.googleapis.com/auth/classroom
.rosters",
"exp": "1474512198",
"expires_in": "3600",
"access_type": "offline"
}
I'd be grateful for any help.
Doug
P.S. I get the security implications of this code. It is in a secure environment for experimentation only. It won't see the light of day.
Based from this forum which is also receiving a 401 error, try to revoke the old oauth. As stated in this related thread, the 401 Unauthorized error you experienced may be related to OAuth 2.0 Authorization using the OAuth 2.0 client ID.
Suggested action: Refresh the access token using the long-lived refresh token. If this fails, direct through the OAuth flow.

I need $http POST Status code 400, but getting 200

I have this code
var body="ok";
var suc=0; var failed=0;
$http({
url: API.toUrl('api/xxxx/xxxx'),
method: 'POST',
data: body
}).then(function(response) {
if(response.status==200){
suc=suc+1;
}
if(response.status==400){
failed=failed+1;
}
});
My problem in this is that I can't obtain the 400 Status Code, I am only getting 200 but not 400. How can I get 400 status code in my response parameter.
I am working in Angular, any idea for obtain the 400?
Thanks
400 status code will come as an error, then accept two functions as parameters first one for OK response and second one for errors so you have to catch 400 on error function.
So if you want to catch it you should do it like this:
var body = "ok";
var suc = 0;
var failed = 0;
$http({
url: API.toUrl('api/xxxx/xxxx'),
method: 'POST',
data: body
}).then(
function(response) {
if (response.status == 200) {
suc = suc + 1;
}
},
function(error) {
//Catch 400 here
}
);
You need to use another function for errors (400):
var body="ok";
var suc=0; var failed=0;
$http({
url: API.toUrl('api/xxxx/xxxx'),
method: 'POST',
data: body
}).then(function(response) {
alert response.status ;
},function error(response) {
alert response.status ;
});
If you are using PHP server to write your api's/services then use below lines to manually send 400 to your $http request
<?php
// Get the current response code and set a new one
var_dump(http_response_code(400));
// Get the new response code
var_dump(http_response_code());
?>
[UPDATE]
You can see more examples here to check how to send response codes:
PHP: How to send HTTP response code?
From the Docs:
A response status code between 200 and 299 is considered a success status and will result in the success callback being called. Any response status code outside of that range is considered an error status and will result in the error callback being called.
-- AngularJS $http Service API Reference
var body="ok";
var suc=0; var failed=0;
$http.post(url, data).then(function onSuccess(response) {
if(response.status==200){
suc=suc+1;
};
//return to chain response
return response;
}).catch(function onReject(errorResponse) {
if(errorResponse.status==400){
failed=failed+1;
}
//throw to chain rejection
throw errorResponse;
});
If chaining from the httpPromise, be sure to use a throw statement in the onReject handler to avoid converting the rejection to a success.

Twitter request from Parse Cloud Code fails with "Could not authenticate you."

{"errors":[{"code":32,"message":"Could not authenticate you."}]}
This is what I get when trying to perform a GET users/show request to Twitter. Some background:
User is authenthicated in my Android app through ParseTwitterUtils;
From Android, I call a parse.com Cloud Code function passing in the user token and token secret (looks like bad practice, but for now I'd just like to see this work);
From Cloud Code, I format the auth header using this github library. This is needed as explained here.
You can see some of my code below. Android launch code:
HashMap<String, Object> params = new HashMap<>();
params.put("twitterId", ParseTwitterUtils.getTwitter().getUserId());
params.put("authToken", ParseTwitterUtils.getTwitter().getAuthToken());
params.put("authTokenSecret", ParseTwitterUtils.getTwitter().getAuthTokenSecret());
ParseCloud.callFunctionInBackground("fetchPictureFromTwitter", params, ... );
Cloud code main function:
Parse.Cloud.define("fetchPictureFromTwitter", function(request, response) {
var twitterId = request.params.twitterId;
var authToken = request.params.authToken;
var authTokenSecret = request.params.authTokenSecret;
var url = "https://api.twitter.com/1.1/users/show.json";
Parse.Cloud.httpRequest({
url: url,
followRedirects: true,
headers: {
"Authorization": getOAuthSignature(url,authToken,authTokenSecret)
},
params: {
user_id: twitterId
}
}).then(...)
And lastly here's getOAuthSignature, the function used to sign the request (I took this from the example page in the github link):
var getOAuthSignature = function(url, authToken, authTokenSecret) {
var nonce = OAuth.nonce(32);
var ts = Math.floor(new Date().getTime() / 1000);
var timestamp = ts.toString();
var consumerKey = <MY-APP-CONSUMER-KEY>
var consumerSecret = <MY-APP-CONSUMER-SECRET>
var accessor = {
"consumerSecret": consumerSecret,
"tokenSecret": authTokenSecret
};
var params = {
"oauth_version": "1.0",
"oauth_consumer_key": consumerKey,
"oauth_token": authToken,
"oauth_timestamp": timestamp,
"oauth_nonce": nonce,
"oauth_signature_method": "HMAC-SHA1"
};
var message = {
"method": "GET",
"action": url,
"parameters": params
};
OAuth.SignatureMethod.sign(message, accessor);
var normPar = OAuth.SignatureMethod.normalizeParameters(message.parameters);
var baseString = OAuth.SignatureMethod.getBaseString(message);
var sig = OAuth.getParameter(message.parameters, "oauth_signature") + "=";
var encodedSig = OAuth.percentEncode(sig);
return 'OAuth oauth_consumer_key="'+consumerKey+'", oauth_nonce=' + nonce + ', oauth_signature=' + encodedSig + ', oauth_signature_method="HMAC-SHA1", oauth_timestamp=' + timestamp + ',oauth_token="'+authToken+'", oauth_version="1.0"'
};
What could be wrong? I've spent two days on the matter now and I don't know what to do anymore.
The issue here was that user_id="<user-id> has to be encoded in the request header as well as all the other oauth_* parameters. So I had to change this section:
var params = {
"user_id": twitterId, // <- add here
"oauth_version": "1.0",
"oauth_consumer_key": consumerKey,
"oauth_token": authToken,
"oauth_timestamp": timestamp,
"oauth_nonce": nonce,
"oauth_signature_method": "HMAC-SHA1"
};
And I'm passing the userId from the outer function, like getOAuthSignature(url,twitterId,authToken,authTokenSecret).
As for passing auth token data from device to cloud, this is probably not needed because you can find all the authentication info in the authData field of any ParseUser (as long as it is linked with twitter or Facebook).

Fitbit OAuth API request, invalid signature

I am trying to make an API request to fitbit, using the oauth debugger from fitbit (https://dev.fitbit.com/apps/oauthtutorialpage) i am trying to figure out what i am doing wrong. I have added comments to my code below to help you understand what i am trying to achieve. What i am quite sure of is that i am either signing my request wrong, or using the wrong data to sign it. This is echoed by the API response.
I know there are more fitbit api questions here on stackoverflow, however did not find my answer there.
Is there anyone with more experience in Oauth signatures that knows what i could be doing wrong? Or could help me find a different approach to this?
var request = require('request');
var crypto = require('crypto');
var params = {
'oauth_consumer_key' : 'key12345',
'oauth_nonce' : Math.random().toString(36).substring(3), //random string
'oauth_signature_method' : 'HMAC-SHA1',
'oauth_timestamp' : Date.now().toString().substring(0,10), //timestamp with the same length as in the tutorial
'oauth_version' : '1.0'
}
var oauth_consumer_secret = 'secret123';
var post_string = 'POST&https://api.fitbit.com/oauth/request_token';
for(var key in params){
post_string += '&' + key + '=' + params[key];
}
/*At this point we have made a post string that we have to hash with hmac-sha1
the post string looks like this:
POST&https://api.fitbit.com/oauth/request_token&oauth_consumer_key=key12345&oauth_nonce=az6r8cqlzyqfr&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1439147378&oauth_version=1.0
The post_string from the tutorial looks like this:
POST&%2Foauth%2Frequest_token&oauth_consumer_key%3D%26oauth_nonce%3D%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1439145944%26oauth_version%3D1.0
*/
var hmac = crypto.createHmac('sha1', oauth_consumer_secret + "&");
// The tutorial page shows me the signature was 'signed with secret&'. I have tried with and without the & at the end, but without luck.
hmac.setEncoding('base64'); //i'm not sure if this is correct
hmac.write(post_string);
hmac.end();
var hash = hmac.read();
//and finally adding the hash to the parameters.
params.oauth_signature = hash;
//now, making the request with an authorization header.
var header='';
for (var key in params){
if(header.length === 0){
header = ' OAuth ' + key + '="' + params[key] + '"';
}
else{
header += ', ' + key + '="' + params[key] + '"';
}
}
/*
At this point the header parameter looks like this
OAuth oauth_consumer_key="key12345", oauth_nonce="jnr97ppvjs2lnmi", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1439148049", oauth_version="1.0", oauth_signature="random_signature"
The tutorial tells me to use the headers:
OAuth oauth_consumer_key="key12345", oauth_nonce="jnr97ppvjs2lnmi", oauth_signature="different_signature", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1439145944", oauth_version="1.0"
*/
var headers ={
'Authorization' : header
}
var url="https://api.fitbit.com/oauth/request_token";
var requestTimeout = 5000;
var opts = {
url: url,
timeout: requestTimeout,
headers : headers
}
request(opts, function (err, res, body) {
if (err) {
console.dir(err);
return;
}
var statusCode = res.statusCode;
if(res.statusCode === 200){
console.log(body);
}
else{
console.log("http-error-code: " + res.statusCode);
console.log(body);
}
})
/*
The response:
http-error-code: 401
{"errors":[{"errorType":"oauth","fieldName":"oauth_signature","message":"Invalid signature: 9fXI85C7GvZqMyW1AK1EkOSWZCY="}],"success":false}
*/
To get access token and secret use Grant (you can test FitBit in the playground).
Once you have access token and secret use Purest to make subsequent request to the FitBit API.
Here is an example on how to get the user's profile:
var Purest = require('purest')
var fitbit = new Purest({provider:'fitbit',
key:'[CONSUMER_KEY]', secret:'[CONSUMER_SECRET]'})
fitbit.get('user/-/profile', {
oauth:{token:'[ACCESS_TOKEN]', secret:'[ACCESS_SECRET]'}
}, function (err, res, body) {})
Alternatively you can use request for that:
var request = require('request')
request.get('https://api.fitbit.com/1/user/-/profile.json', {
oauth:{
consumer_key:'..',
consumer_secret:'..',
token:'..',
token_secret:'..'
}
}, function (err, res, body) {})
In short - don't try to implement the web server OAuth flow by yourself - use Grant, then use either Purest or request, just keep in mind that you don't have to pass all of the OAuth parameters by yourself, just pass the credentials.

Google API gapi.cliet.request not working

I am using Freebase JS api to fetch topic details. This is a simple function for that:
function simpleTopicDetail(topicIds){
path = 'freebase/v1/topic' + topicIds;
var opts = {
'method': 'GET',
'path': path,
'params': {'filter':'/common/topic/article'}
};
var request = gapi.client.request(opts);
request.execute(onFinalSuccess);
var onFinalSuccess = function(data){
console.log(data);
//do something with data
//parsing JSON resp to get a node value.
}
}
On debugging I see, it goes to onFinalSuccess and then nothing! Skips to the end. What's wrong here?
NOTE I'm using it in conjuction with YT API. It's a sepearte function though. Can it a problem?
You are refering to the call back function before it's assigned.
Try:
function simpleTopicDetail(topicIds){
path = 'freebase/v1/topic' + topicIds;
var opts = {
'method': 'GET',
'path': path,
'params': {'filter':'/common/topic/article'}
};
var request = gapi.client.request(opts);
request.execute(function(data){
console.log(data);
//do something with data
//parsing JSON resp to get a node value.
});
}

Categories