Consuming DynamicsNAV WebService with Node.JS node-soap - node.js

I want to consume the WebService from Microsoft Dynamics NAV 2009 with a small node.js application. The Service itself works fine, I am using it with a c# application, now I want to get data into my nodejs/expressjs application but, I always get Invalid WSDL URL as an error message.
Here is the WSDL as my Browser sees it.
Now I tried to connect with node-soap, following the documentation, by normal and by basic auth, but everytime I get an Invalid WSDL URL error.
Here are the methods I tried for a test connection:
var url = "http://navsrv:7047/DynamicsNAV2/WS/Produktiv/Page/WDCETA";
var auth = "Basic " + new Buffer("*********" + ":" + ****************").toString("base64");
soap.createClient(url, function(err, client) {
console.log('Without basic out:');
if (err)
{
console.log('[ERROR] -> ');
console.log(err);
}
console.log(client);
});
soap.createClient(url, {wsdl_headers: {Authorization: auth} }, function(err, client) {
console.log('With basic out:');
if (err)
{
console.log('[ERROR] -> ');
console.log(err);
}
console.log(client);
});
And this is the response I get:
Without basic out:
[ERROR] ->
[Error: Invalid WSDL URL: http://navsrv:7047/DynamicsNAV2/WS/Produktiv/Page/WDDCETA
Code: 401
Response Body: ]
undefined
With basic out:
[ERROR] ->
[Error: Invalid WSDL URL: http://navsrv:7047/DynamicsNAV2/WS/Produktiv/Page/WDDCETA
Code: 401
Response Body: ]
undefined

As it turned out, the build in HTTP-Server from DyanmicsNAV requires SPNEGO or NTLM as authentication. After some tries creating a proper SPNEGO request with nodejs/node-soap I turned off SPNEGO and enabled NTLM.
With the help of soap-ntlm and httpntlm I could retrieve the wsdl.
This is some testing code how I could manage to retrieve the WSDL file. For now I am happy, but I guess when it comes to invoke function there will be some other issues :)
var soap = require('soap-ntlm');
var fs = require('fs');
var httpntlm = require('httpntlm');
var url = 'http://navsrv:7047/DynamicsNAV2/WS/Produktiv/Page/WDCETA';
var username = '*******';
var password = '***********';
httpntlm.get({
url: url,
password: password,
username: username
}, function(err, wsdl) {
if (err)
{
console.log('ERR: -> ');
console.log(err);
return;
}
fs.writeFile('wsdl_cache/WDCETA.wsdl', wsdl.body, function() {
soap.createClient(__dirname + '/wsdl_cache/WDCETA.wsdl', function(err, client) {
if (err) {
console.log('SOAP ERR: ->');
console.log(err);
return;
}
client.setSecurity(new soap.NtlmSecurity(username, password));
console.log(client);
});
})
});

Related

AWS Cognito Node.JS User Authentication returns unknown problem

I'm using AWS Cognito with Node.JS.
I'm successfully registering and verifying users, but the Authentication is returning "unknown error, the response body from fetch is undefined."
I'm using node-fetch module along with amazon-cognito-identity-js (set as var AWSCognito on code below). User is not in a state of requiring password change and verified.
Have others experienced this and how did you resolve the issue?
Appreciate any guidance in advance....
Here's my code, my complete module is on npm as iditawsutils :
exports.authCognitoUser = function(theUserPoolID, theClientID, userName, userPassword) {
var authenticationData = {
Username : userName,
Password : userPassword
};
var authenticationDetails = new AWSCognito.AuthenticationDetails(authenticationData);
var poolData = { UserPoolId : theUserPoolID,
ClientId : theClientID
};
var userPool = new AWSCognito.CognitoUserPool(poolData);
var userData = {
Username : userName,
Pool : userPool
};
console.log('authentication details: ',authenticationDetails);
var cognitoUser = new AWSCognito.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('access token + ' + result.getAccessToken().getJwtToken());
console.log('id token + ' + result.getIdToken().getJwtToken());
console.log('refresh token + ' + result.getRefreshToken().getToken());
return result;
},
onFailure: function(err) {
console.log(err.message || JSON.stringify(err));
return err;
},
});
}
//from the console log:
authentication details: AuthenticationDetails {
validationData: {},
authParameters: {},
username: 'thesmarterstuff',
password: 'passW0rd!’ }
Unknown error, the response body from fetch is: undefined
Use the following in your onFailure block to find more details about the error.
onFailure: function(err) {
console.log(new Error().stack);
console.log(err.message || JSON.stringify(err));
},
If you find the error occurring in your fetch line in Client.js, then this could be because currently NodeJS SDK, and most other SDKs do not support the default USER_SRP_AUTH.
You could check by adding a console.log in the Client.js
console.log(this.endpoint);
console.log(options);
Login to your AWS account, and make sure you have checked the option - Enable username-password (non-SRP) flow for app-based authentication (USER_PASSWORD_AUTH)
Then, in your code update it with the following setting.
cognitoUser.setAuthenticationFlowType('USER_PASSWORD_AUTH');

Callback doesn't seem to work in Lambda Node.js function - maybe due to firewall?

So I'm trying to password-protect a static S3 website using Node.js in a Lambda#Edge function and a CloudFront trigger. I use the www-authenticate header to get the user's credentials, decode the resulting authorization header to get the username/password, then call adminInitiateAuth (from the aws-sdk) to authenticate with a Cognito user pool.
Here's the actual code:
'use strict';
const AWS = require('aws-sdk');
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
const body = 'Unauthorized';
const response = {
status: '401',
statusDescription: 'Unauthorized',
body: body,
headers: {
'www-authenticate': [{key: 'WWW-Authenticate', value:'Basic'}]
},
};
if(typeof headers.authorization != 'undefined'){
var decoded = new Buffer(headers.authorization[0].value.split(" ")[1], "base64").toString();
console.log("decoded from auth header without basic: " + decoded);
var arr = decoded.split(":");
var user = arr[0];
var pass = arr[1];
console.log("user: " + user);
console.log("pass: " + pass);
const cognito = new AWS.CognitoIdentityServiceProvider( 'us-east-1');
var params = {
AuthFlow: 'ADMIN_NO_SRP_AUTH',
ClientId: [myclientid],
UserPoolId: [myuserpoolid],
AuthParameters: {
USERNAME: user,
PASSWORD: pass,
},
};
cognito.adminInitiateAuth(params, function(err, data) {
if(err){
console.log("failed to authenticate");
console.log(err, err.stack);
callback(null, response);
} else{
console.log("successfully authenticated");
console.log(data);
callback(null, request);
}
});
}
else{
callback(null, response);
}
};
It doesn't seem like there's a problem with the code itself, because it works fine most of the time. But when I'm on my company's network, I keep getting prompted to enter my username and password, even if I input it correctly. I know my company's network has firewall, but I'm not sure of its settings, and how that would interfere with the authentication in the code.
After testing the code a bit more, I've discovered a few things:
Even with the correct credentials entered, adminInitiateAuth seems to give an error (so the callback to continue the request is never called). The authorization header is in the request as far as I can tell, so I'm not sure why this is happening (and only on my company's network).
CloudWatch logs only seem to appear when I make requests outside of my company's network.

npm soap with auth header

I am attempting to us the npm soap package to create a series of endpoints to a remote server that I can interface with through angular 4. I have read the documentation, but I am still unclear with regards to its usage. Below is the WSDL. How do I create a client that I can use to interface with the endpoint below? Here is the WSDL.
http://208.180.122.191:8081/niku/wsdl/Query/ts_pending_approvals?tenantId=clarity
My expectation is that I should get a response with the following:
var soap = require('soap');
var url = 'http://208.180.122.191:8081/niku/wsdl/Query/ts_pending_approvals?tenantId=clarity';
var args = {Username: "jdoe", Password: "*******"};
soap.createClient(url, function(err, client) {
client.Login(args, function(err, result) {
console.log(result);
});
});
When i call console.log(client.describe()), I get the following:
{ ts_pending_approvalsQueryService:
{ ts_pending_approvalsQueryService:
{ Query: [Object],
Login: [Object],
WrappedLogin: [Object],
Logout: [Object] } } }
However, when I call login and pass the username and password, i get undefined. using SoapUI, I was able to successfully complete the request, using the following. My question is how do I simulate this in node.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:quer="http://www.niku.com/xog/Query">
<soapenv:Header/>
<soapenv:Body>
<quer:Login>
<quer:Username>jdoe</quer:Username>
<quer:Password>******</quer:Password>
</quer:Login>
</soapenv:Body>
</soapenv:Envelope>
I was able to resolve this on my own by setting the endpoint, which gave me the expected response token: 6312078__98C024DA-25CF-441E-A47B-A84DDE2FF140
var soap = require('soap');
var url = 'http://208.180.122.191:8081/niku/wsdl/Query/ts_pending_approvals';
var args = {Username: "jdoe", Password: "*****"};
soap.createClient(url, function(err, client) {
client.setEndpoint("http://208.180.122.191:8081/niku/xog")
client.Login(args,(error,result)=>{
if (error) throw error;
console.log(result)
})
});
It is also worth noting that when you're utilizing the package and you have send additional parameters, you may also have to send headers, which map to a namespace specified in the WSDL, in addition to complex structures that require multiple parameters. I was able to figure this out after some trial and error. See working example below:
/////////////////////////////////////////////////////////////////////////////////////////////////////
// 1TS Open Timesheet
ppmRouter.get("/open_time_sheet",(req,res,next) => {
var resourceid = req.query.param_resourceid
var soap = require('soap');
var url = config.wsdlQueryPath + 'open_time_sheet';
var sheader = { Auth: {Username: config.xog_user, Password: config.password}}
var args = {
Query: {Code: "open_time_sheet"},
Filter: {
param_resourceid: resourceid
}
};
soap.createClient(url, function(err, client) {
client.addSoapHeader(sheader,"","tns","Auth");
client.setEndpoint(config.xog_url)
client.Query(args,(error,result)=>{
if (error) throw error;
console.log(result)
res.send(result)
})
});
})

Nodejs NTLM/Basic auth in SOAP

I try to connect nodeJs with soap service, using SOAP package.
In server I have a basic authentification.
How I can put basic authentification in soap library? I have this code:
soap.createClient(url, function(err, client) {
console.log(err)
});
in the log err, I receive the 401 page (for not auth). In web page of SOAP package I see this method:
client.setSecurity(new soap.BasicAuthSecurity('user', 'pass'));
Where I need to put this code? the reicevied client inside createClient are undefined.
EXTRA: The ideal was I can auth using NTLM, it's posible? I have a node app inside the same machine than soap server.
EDIT
I try to set NTLM credentials with soap-ntlm-2 library, using this code:
var url = "http://server/instance/ReportService2010.asmx";
var options = {
wsdl_options: {
ntlm: true,
username: "RSUser",
password: "Reporting2012",
workstation: "",
domain: ""
}
};
soap.createClient(url, options, function (err, client, body) {
if (err) {
console.log(err);
}
// normal use
//client.setSecurity(new soap.NtlmSecurity(options.wsdl_options.userName, options.wsdl_options.password, options.wsdl_options.domain, options.wsdl_options.workstation));
// or object can be passed
client.setSecurity(new soap.NtlmSecurity(options.wsdl_options));
console.log(client.describe());
report = client.ReportingService2010.ReportingService2010Soap;
});
But I have the same error massage: client is undefined.

Node soap, consume password protected WSDL

I'm trying to build a SOAP client with Node, I'm using "soap" package (https://www.npmjs.org/package/soap) trying to consume a user/password protected WSDL.
I can't find how to pass those credentials before creating the client by "soap.createClient", and of course, I can't retrieve the WSDL if I don't provide the right credentials.
I've tried doing:
soap.security.WSSecurity('user', 'pass');
and then calling "createClient" but to no avail.
Also, I've tried to do it with the node-soap-client, with this client I (apparently) can connect to the WSDL, but after that, I've no idea where to go (how to invoke methods).
What am I doing wrong?
Thanks for all your help!
Username and password credentials can be passed like this:
var soap = require('soap');
var url = 'your WSDL url';
var auth = "Basic " + new Buffer("your username" + ":" + "your password").toString("base64");
soap.createClient(url, { wsdl_headers: {Authorization: auth} }, function(err, client) {
});
(derived from https://github.com/vpulim/node-soap/issues/56, thank you Gabriel Lucena https://github.com/glucena)
If its password protected you also need to check the correct security mechanism. I spend a day trying to figure out that the service used NTLM security(it was a clients project and I only got username and password to access the wsdl). In that case, you would need to pass the correct wsdl_options object
var wsdl_options = {
ntlm: true,
username: "your username",
password: "your password",
domain: "domain",
workstation: "workstation"
}
soap.createClient(data.credentials[data.type], {
wsdl_options
},
function(err, client) {
console.log(client.describe());
});
Also, you would need to setSecurity on the client before using any service.
the link to complete explanation: https://codecalls.com/2020/05/17/using-soap-with-node-js/
when I added the auth to the headers I still had a problem. After reading the code and a number of articles I found this to work.
// or use local wsdl if security required
let url = 'http://service.asmx?wsdl'
let wsdl = 'wsdl.wsdl';
let soap = require('soap');
let util = require('util')
soap.createClient(wsdl, function(err, client) {
//don't forget to double slash the string or else the base64 will be incorrect
client.setSecurity(new soap.BasicAuthSecurity('admin\\userName', 'password'));
client.MethodFromWSDL(args, function (err, result) {
console.log(util.inspect(result,{depth: null}))
});
});
This worked for me, the API required the auth parameters as
<UserDetails xmlns="http://url/">';
<userName>{$Username}</userName>
<password>{$Password}</password>
<program>{$program}</program>
</UserDetails>
After lots of trial and error - this ended working
const soapHeader = {
UserDetails: {
userName: process.env.userName,
password: process.env.password,
program: process.env.program
}
}
...
soap.createClient(path, function (err, client) {
if (err) {
console.log('Error creating SOAP client: ' + err);
}
client.addSoapHeader(soapHeader, "", "tns", process.env.URN);
client[process.env.FUNCTION](sargs, function (err, result, rawResponse, soapHeader, rawRequest) {
if (err) {
console.log('Error call SOAP function ' + process.env.FUNCTION + ': ', err);
}
else {
console.log(result);
}
...

Resources