I'm trying to make a SOAP request to a WSDL service with ws-security.
The code is simple:
var fs = require('fs');
var soap = require('soap');
var url = '<WSDL-URL>';
var args = {
"param1": '1',
"param2": '2',
"param3": '3',
"param4": '4',
};
const options = {
envelopeKey: 'soapenv',
forceSoapVersion: '1.1',
};
var privateKey = fs.readFileSync('<PRIVATE-KEY>');
var publicKey = fs.readFileSync('<PUBLIC-KEY>');
var password = ''; // NO PASSWORD
var wsOptions = { hasTimeStamp: true };
soap.WSSecurityCert(privateKey, publicKey, password, wsOptions)
soap.createClient(url, options, function(err, client) {
//console.log(err);
client.setEndpoint('<SERVICE-ENDPOINT>');
client.setSecurity(new soap.ClientSSLSecurity('<SSL-KEY>', '<SSL-CERT>', {}));
client.METHOD(args, function(err, result, envelope, soapHeader) {
console.log(err);
//console.log(client.lastRequest);
//console.log('Response Envelope: \n' + envelope);
// 'result' is the response body
//console.log('Result invoke: \n' + JSON.stringify(result));
});
});
but when i try to run the script, i got this error:
Error: soap:Server: These policy alternatives can not be satisfied:
{http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702}AsymmetricBinding: Received Timestamp does not match the requirements
{http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702}X509Token: The received token does not match the token inclusion requirement
{http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702}InitiatorToken
{http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702}RecipientToken
{http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702}IncludeTimestamp: Received Timestamp does not match the requirements
{http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702}SignedParts: {http://schemas.xmlsoap.org/soap/envelope/}Body not SIGNED
{http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702}EncryptedParts: {http://schemas.xmlsoap.org/soap/envelope/}Body not ENCRYPTED
I think I don't have set correctly the ws-security to the header and i'm still missing how to encrypt my body too.
Can someone help me to figure out how to solve this issue?
Related
I am currently writing to an API to try and get a token. I'm nearly there but fallen at the last hurdle..
const fs = require('fs');
const https = require('https');
const ConfigParams = JSON.parse(fs.readFileSync('Config.json', 'utf8'));
const jwt = require('jsonwebtoken');
const apikey = ConfigParams.client_id;
var privateKey = fs.readFileSync(**MY KEY**);
var tkn;
const jwtOptions = {
algorithm: 'RS512',
header: { kid: 'test-1' }
}
const jwtPayload = {
iss: apikey,
sub: apikey,
aud: **API TOKEN ENDPOINT**,
jti: '1',
exp: 300
}
jwt.sign(jwtPayload,
privateKey,
jwtOptions,
(err, token) => {
console.log(err);
//console.log(token);
tkn = token;
let = tokenPayload = {
grant_type: 'client_credentials',
client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer/',
client_assertion: tkn
}
tokenAuthOptions = {
payload: tokenPayload,
host: **HOST**,
path: **PATH**,
method: 'POST',
}
https.request(
tokenAuthOptions,
resp => {
var body = '';
resp.on('data', function (chunk) {
body += chunk;
});
resp.on('end', function () {
console.log(body);
console.log(resp.statusCode);
});
}
).end();
}
)
the encoded token comes back fine for the first part, the https request though returns a problem.
the response I get back is grant_type is missing, so I know I have a formatting problem due to this x-www-form-urlencoded, but I can't figure out how to fix it.
here is what the website said:
You need to include the following data in the request body in
x-www-form-urlencoded format:
grant_type = client_credentials client_assertion_type =
urn:ietf:params:oauth:client-assertion-type:jwt-bearer
client_assertion = <your signed JWT from step 4> Here's a complete
example, as a CURL command:
curl -X POST -H "content-type:application/x-www-form-urlencoded"
--data \ "grant_type=client_credentials\ &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion="
END POINT
Ideally I want a solution using the https request, but if that's not possible I'm open to other solutions.
Any help is greatly appreciated.
Thanks,
Craig
Edit - I updated my code based on a suggestion to:
const params = new url.URLSearchParams({
grant_type: 'client_credentials',
client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer/',
client_assertion: tkn
});
axios.post("URL", params.toString()).then(resp => {
console.log("response was : " + resp.status);
}).catch(err => {
console.log("there was an error: " + err);
})
But I'm still getting an error code 400, but now with less detail as to why. (error code 400 has multiple message failures)
Postman is the best.
Thank for #Anatoly for your support which helped to point me in the right direction. I had no luck so used postman for the first time, and found it had a code snippet section, with four different ways of achieving this using node.js.
The solution with Axion was:
const axios = require('axios').default;
const qs = require('qs');
var data = qs.stringify({
'grant_type': 'client_credentials',
'client_assertion_type': 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
'client_assertion': tkn
});
var config = {
method: 'post',
url: '',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.status));
})
.catch(function (error) {
console.log(error);
});
I believe the issue was that I was not passing the information into 'data:' in combination with the querystring problem. Using qs.stringify to format the object, then passing this into the data: key solved the problem.
I am using an exchange API to create a bot. The exchange uses SHA1 and a private key to sign HTTP requests. Everything is working except when one of my POST parameters is an array.
I tried using JSON.stringify and querystring.stringify instead of just having the array as is. I even tried sending the parameters as a string, but the API response says it has to be an array.
let api_key = 'api_key';
let secret = fs.readFileSync('./key.pem').toString('ascii');
var params: any = {
amount: '1',
api_key,
begin_time: '',
end_time: '',
market: 'USDT',
market_type: '1',
page: '1',
price: '150',
size: '10',
token: 'LTC',
tokens: 'LTC', //this must be an array, but when I make it an array the signature becomes invalid
type: '2',
};
const hash = crypto.createSign('sha1');
hash.update(JSON.stringify(params));
const signature = encodeURIComponent(hash.sign(secret, 'base64'));
params = {
sign: signature,
...params,
};
console.log(params);
var api_endpoint = '/api_market/getBalance';
var url = 'https://api.bcex.vip' + api_endpoint;
var http_header = {
'Content-Type': 'application/x-www-form-urlencoded',
};
request.post(
url,
{
headers: http_header,
body: querystring.stringify(params),
},
function(error, response, body) {
if (error) {
// res.json(error);
console.error(error);
} else {
// res.json(body);
// console.log(response);
console.log(JSON.parse(body));
}
},
);
}
API Wiki:
https://github.com/BCEX-TECHNOLOGY-LIMITED/API_Docs/wiki/Interface
Signature Instructions
https://github.com/BCEX-TECHNOLOGY-LIMITED/API_Docs/wiki/Sign
The endpoint point getBalance needs tokens to be an array
It turns out that the problem is with querystring library, I changed it to qs and now it works.
I'm building a facebook bot in nodejs with facebook messenger API. I'm trying to send a image from the bot by directly uploading the image file from the heroku server itself (not through URL) and it does not work.
Here is a error log from the console.
Failed calling Send API 400 Bad Request { message: '(#100) Incorrect
number of files uploaded. Must upload exactly one file.',type:
'OAuthException', code: 100,error_subcode: 2018005,fbtrace_id:
'E32ogm/ofxd' }
The official facebook document only contains an example in curl format and I'dont know how to replicate this curl into node format.
I've tested with curl and it worked like a charm.
curl \ -F 'recipient={"id":"recipientId"}' \ -F 'message={"attachment":{"type":"image", "payload":{}}}' \ -F 'filedata=#resource/pdf_img/sample.jpg;type=image/jpeg' \ "https://graph.facebook.com/v2.6/me/messages?access_token=PAGE_ACCESS_TOKEN"
This is my node implementation that seems to be problematic,
//file_loc = __dirname+"/resource/pdf_img/sample.jpg"
function sendImageMessage(recipientId, file_loc){
let fs = require('fs');
var readStream = fs.createReadStream(file_loc);
var messageData = {
recipient : {
id : recipientId
},
message : {
attachment : {
type : "image",
payload :{}
}
},
filedata:readStream
}
callSendAPI(messageData);
}
function callSendAPI(messageData) {
request({
uri: "https://graph.facebook.com/v2.6/me/messages",
qs: {access_token: process.env.PAGE_ACCESS_TOKEN},
method: "POST",
json: messageData
}, function(error, response, body) {
if (!error && response.statusCode == 200) {
var recipientId = body.recipient_id;
var messageId = body.message_id;
if (messageId) {
console.log("Successfully sent message with id %s to recipient %s",
messageId, recipientId);
} else {
console.log("Successfully called Send API for recipient %s",
recipientId);
}
} else {
console.error("Failed calling Send API", response.statusCode, response.statusMessage, body.error);
}
});
}
Please any help with fixing my node implementation or translating that curl in to node would be appreciated.
You can use forms /form-data/ in the request module (which has integrated module 'form-data').
But all requests needs to be stringified.
So, based on your example, the following should do the job >>
function sendImageMessage(recipientId, file_loc){
let fs = require('fs');
var readStream = fs.createReadStream(file_loc);
var messageData = {
recipient : {
id : recipientId
},
message : {
attachment : {
type : "image",
payload :{}
}
},
filedata:readStream
}
callSendAPI(messageData);
}
function callSendAPI(messageData) {
var endpoint = "https://graph.facebook.com/v2.6/me/messages?access_token=" + process.env.PAGE_ACCESS_TOKEN;
var r = request.post(endpoint, function(err, httpResponse, body) {
if (err) {return console.error("upload failed >> \n", err)};
console.log("upload successfull >> \n", body); //facebook always return 'ok' message, so you need to read error in 'body.error' if any
});
var form = r.form();
form.append('recipient', JSON.stringify(messageData.recipient));
form.append('message', JSON.stringify(messageData.message));
form.append('filedata', messageData.filedata); //no need to stringify!
}
Details here https://github.com/request/request#forms
I think sending variable messagedata as formdata would solve the problem.
var FormData = require('form-data');
var fs = require('fs');
var https = require('https');
function sendImageMessage(recipientId, file_loc){
var readStream = fs.createReadStream(file_loc);
var messageData = new FormData();
messageData.append('recipient', '{id:' +recipientId+ '}');
messageData.append('message', '{attachment :{type:"image", payload:{}}}');
messageData.append('filedata', readStream);
callSendAPI(messageData);
}
Secondly you need to change request a bit since now you are using formdata. I have done it using module https and so have changed the callSendAPI() code accordingly. You can find out how to send the formdata using request module.
function callSendAPI(messageData) {
var options = {
method: 'post',
host: 'graph.facebook.com',
path: '/v2.6/me/messages?access_token=' + pagetoken,
headers: messageData.getHeaders()
};
var request = https.request(options);
messageData.pipe(request);
request.on('error', function(error) {
console.log("Unable to send message to recipient %s", recipientId);
return;
});
request.on('response', function(res) {
if (res.statusMessage == "OK") {
console.log("Successfully sent message to recipient %s", recipientId);
} else {
console.log("Unable to send message to recipient %s", recipientId);
}
return;
});
}
I am using using AWS SES to post an email from an s3 bucket to a method on my server. I have an SES rule set that writes said email to the bucket (which works), then there is a lambda function which posts to a function on my server.
Here is the lambda function I am using (minus a few pieces of information).
var AWS = require('aws-sdk');
var https = require('https');
var s3 = new AWS.S3();
var queryString = require('querystring');
var bucketName = '<My-Bucket-Name>';
exports.handler = function (event, context) {
var sendToServer = function(emaildata) {
// Options and headers for the HTTP request
var dataString = queryString.stringify(emaildata);
var options = {
host: '<My-Host-Name>',
port: 443,
path: '<My-PATH>',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(dataString),
}
};
// Setup the HTTP request
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var req = https.request(options, function (res) {
res.setEncoding('utf-8');
// Collect response data as it comes back.
var responseString = '';
res.on('data', function (data) {
responseString += data;
});
// Log the responce received from Twilio.
// Or could use JSON.parse(responseString) here to get at individual properties.
res.on('end', function () {
console.log('Response: ' + responseString);
context.succeed();
});
});
// Handler for HTTP request errors.
req.on('error', function (e) {
console.error('HTTP error: ' + e.message);
context.fail();
});
// Send the HTTP request to the Twilio API.
// Log the message we are sending to Twilio.
console.log('Start API call');
req.write(dataString);
req.end();
}
var sesNotification = event.Records[0].ses;
// Retrieve the email from your bucket
console.log("sesNotification = " + event.Records);
console.log("Key value = " + sesNotification.mail.messageId);
s3.getObject({
Bucket: bucketName,
Key: sesNotification.mail.messageId
}, function (err, data) {
if (err) {
console.log(err, err.stack);
context.fail();
} else {
console.log("Raw email:\n" + data.Body);
sendToServer(data);
}
});
};
The php script I am posting to has the the following code:
$this->layout=false;
$obj = json_encode($this->request->data);
I then email $obj to my personal email.
The message I get is:
{"AcceptRanges":"bytes","LastModified":"Wed, 02 Dec 2015 19:55:09 GMT","ContentLength":"2797","ETag":"\"1ee74034ec791514fa57d1ff13452737\"","ContentType":"application\/octet-stream","Metadata":"","Body":""}
What I am looking for is all the information pertaining to the email so I can do some fancy stuff on my server. What am I doing wrong? Any suggestions will be appreciated. Thanks.
The problem seems to be in your S3 callback function. data.Body isn't actually simple object that can be serialized. You have to stream it to a buffer, file, or turn it into a string (which I think is being done in your log statement).
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#getObject-property
Before your sendToServer call, try:
data.StringBody = data.Body.toString()
I am trying to make REST API calls to Node.js using request module. I have obtained accessToken and accessTokenSecret.
When I make an api call to obtain my profile I get a result, but when I make an api call to do a company search I get the following error
<error>
<status>401</status>
<timestamp>1345187981287</timestamp>
<request-id>HE45IXV7YZ</request-id>
<error-code>0</error-code>
<message>[unauthorized]. OAU:xxxxx|xxxxx|*01|*01:1345188248:kCNFNUu6JePTEy7k5e8Ca9vHAzg=</message>
</error>
But when I make the same API call using JAVA (Scribe jar), using the same accessToken and accessTokenSecret I get results for the company search.
I am posting my node.js code below for reference
//oauth test
var request = require('request');
var key = 'xxxxx';
var secret = 'xxxxx';
var redirect = '';
var restURL = 'http://api.linkedin.com/v1/company-search?keywords=philips&format=json';
var accessToken = 'xxxxx';
var accessTokenSecret = 'xxxxx';
var proxySetting = "http://proxy:port/";
function getRequestToken()
{
var requestOAuth =
{
consumer_key: key
, consumer_secret: secret
//, token: accessToken
//, token_secret: accessTokenSecret
};
var requestTokenURL = 'https://api.linkedin.com/uas/oauth/requestToken';
request.get({'proxy':proxySetting,url:requestTokenURL,oauth:requestOAuth},function(e,r,data)
{
console.log('Error is: ' + e);
console.log('Data is: ' + data);
console.log('Response is: ' + r);
});
}
function getAccessToken()
{
var accessOAuth =
{
consumer_key: key
, consumer_secret: secret
, token: 'xxxxx'
, token_secret: 'xxxx'
,verifier : #####
};
var accessTokenURL = 'https://api.linkedin.com/uas/oauth/accessToken';
request.get({'proxy':proxySetting,url:accessTokenURL,oauth:accessOAuth},function(e,r,data)
{
console.log('Error is: ' + e);
console.log('Data is: ' + data);
console.log('Response is: ' + r);
});
}
/
function comSearch()
{
var apiToken = 'xxxxx';
var apiTokenSecret = 'xxxxx';
var apiOAuth =
{
consumer_key: key
, consumer_secret: secret
, token: apiToken
, token_secret: apiTokenSecret
};
var apiURL = 'http://api.linkedin.com/v1/company-search?keywords=philips';
var peopleProfile = 'http://api.linkedin.com/v1/people/~';
request.get({'proxy':proxySetting,url:apiURL,oauth:apiOAuth},function(e,r,data)
{
console.log('Error is: ' + e);
console.log('Data is: ' + data);
console.log('Response is: ' + r);
});
}
comSearch();
Following is my header
Response is: GET http://api.linkedin.com/v1/company-search?keywords=philips HTTP/1.1
host: api.linkedin.com
Authorization: OAuth keywords="philips",oauth_consumer_key="xxx",oauth_nonce="xxx",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1345189928",oauth_token="xxx",oauth_version="1.0",oauth_signature="xxx"
content-length: 0
Connection: keep-alive
Could there be some error due to the signature issues ?
I have posted this Issue, but posting it here to reach a wider audience
EDIT 1
Reason why I am using the request module is, it helps me do oauth behind a proxy.
I would love to try out Passport.js or node-oauth or linked-in but none seem to have options where I can specify my proxy
I managed to get it work using node-oauth
Apparently there was code fix provided so that we could access node-oauth over the proxy and that works perfectly fine.
The fix for using node-oauth over a http-proxy was mentioned by Jared Hanson in this SO Question. This fix for node-oauth over a http-proxy can be found here
Please, share your generated base signature string. It seems like your query params were not corectly added to the signature string. In some old post on linkedIn forum I've seen that params need to be organized in the string to sign in alphanumeric order.