C# code works, but not its Node equivalent: 401 Unauthorized
Hi, I am trying to use this ews-javascript-api package, but I am facing an issue:
- I can use autodiscovery and send an email from a Windows/.Net program, from the example given by Microsoft
- The Node equivalent seems to do the autodiscovery fine, but fails with a 401 error when sending the email.
The server is a hosted Exchange one, and as it works from the Windows client, I assume the issue is not caused by the server rejecting basic auth.
Could you please help me? Below are the 2 codes (working C#, and failing Node).
BTW: the autodiscovery fails on Node if I specify the 2007_SP1 exchange version, whereas it works on C#. When I switch to 2010 or above, then it works. Do you know why?
BTW2: the traces do not output anything in Node (but it seams to be implemented, so maybe I miss something to make it work?)
Many thanks!
C# working code:
using Microsoft.Exchange.WebServices.Data;
internal class Program
{
private static void Main(string[] args)
{
string addr = config.email;
string pwd = config.pwd;
Console.WriteLine("Hello, World!");
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.TraceEnabled = true;
service.TraceFlags = TraceFlags.All;
service.Credentials = new WebCredentials(addr, pwd);
service.UseDefaultCredentials = true;
service.AutodiscoverUrl(addr, RedirectionUrlValidationCallback);
EmailMessage email = new EmailMessage(service);
email.ToRecipients.Add(config.to);
email.Subject = "hello";
email.Body = new MessageBody("this is the first email");
email.Send();
static bool RedirectionUrlValidationCallback(string redirectionUrl)
{
bool result = false;
Uri redirectionUri = new Uri(redirectionUrl);
if (redirectionUri.Scheme == "https")
{
result = true;
}
return result;
}
}
}
Node failing code:
const ews = require("ews-javascript-api")
var exch = new ews.ExchangeService(ews.ExchangeVersion.Exchange2010);
exch.TraceEnabled = true;
exch.TraceFlags = ews.TraceFlags.All;
exch.Credentials = new ews.WebCredentials(config.email, config.pwd);
exch.AutodiscoverUrl(user.email, (redirectionUrl) => {
return true
}).then(x => {
console.log("autodisc url then", x)
let email = new ews.EmailMessage(exch)
email.ToRecipients.Add(config.to);
email.Subject = "hello";
email.Body = new ews.MessageBody("this is the first email");
email.Send().then(x => {
console.log("email.send then", x)
}).catch(err => {
console.error("email.send error", err)
})
})
Here is the exception I get:
email.send error SoapFaultDetails {
message: '401 Unauthorized',
InnerException: null,
faultCode: null,
faultString: null,
faultActor: null,
responseCode: 127,
errorCode: 0,
exceptionType: null,
lineNumber: 0,
positionWithinLine: 0,
errorDetails:
DictionaryWithStringKey { keys: [], keysToObjs: {}, objects: {}, keyPicker: [Function] },
HttpStatusCode: 401,
Exception:
ServiceRequestUnauthorizedException { message: '401 Unauthorized', InnerException: null } }
Investigating further on the 'CreateItem' request sent, I could find some differences in the headers:
- The C# working version has no "Authoriszation: ' Basic ..." header (and the Node one has one), and no 'Accept' nor 'Accept-Encoding' headers
- The C# working version also has much more headers:
HTTP/1.1 200 OK
Cache-Control: private
Transfer-Encoding: chunked
Vary: Accept-Encoding
Server: Microsoft-IIS/10.0
request-id: 783e106b-af4a-4b4b-807e-d25aa6dca657
X-CalculatedBETarget: dag3ex1.indiv4.local
Strict-Transport-Security: max-age=31536000; includeSubDomains, max-age=31536000; includeSubDomains
X-DiagInfo: DAG3EX1
X-BEServer: DAG3EX1
X-AspNet-Version: 4.0.30319
Set-Cookie: exchangecookie=42xxx; expires=Tue, 02-Jan-2024 10:29:08 GMT; path=/; HttpOnly, X-BackEndCookie=S-1-5-21-1840533827-1334025008-xxx-90715=xxx/N0s/Oq87Pxc3Gxc/HgZaRm5aJy9GTkJyek4HP; expires=Wed, 01-Feb-2023 10:29:08 GMT; path=/ews; secure; HttpOnly
Persistent-Auth: true
X-Powered-By: ASP.NET
X-FEServer: CAS7
Date: Mon, 02 Jan 2023 10:29:08 GMT
Content-Encoding: gzip
Any clue to make my Node app work? Many thanks!
Am I the only one have this issue? If anyone has any clue, even if it's not a solution, I would greatly appreciate any feedback: it might help me to find what to look after, to fix it. Big thank you in advance!
Same problem here, but no solution yet...
Thanks Cimchd, I feel less alone.
Have you tried to find out if you need NTLM auth or basic auth? Check with your exchange admin. This library does not have built in Windows NTLM auth you have to use it separately
Thank you Gautamsi, the C# code above works. Could it work if NTLM is required?
yes, c# runs on windows and it can choose between NTLM or not, but you have to know beforehand with this lib and use https://github.com/ewsjs/xhr to setup NTLM.
Thank you so much, will give a try (I have zero skills on ntlm, hope I won't be stuck due to this)
Great news, thanks to the xhr package I could make it work, thank you again!
Now, I would like to use the autodiscover, but it does not work:
const ews = require("ews-javascript-api")
var ewsAuth = require("ews-javascript-api-auth");
ews.ConfigurationApi.ConfigureXHR(new ewsAuth.ntlmAuthXhrApi(user.email, user.password));
var autod = new ews.AutodiscoverService(new ews.Uri("https://autodiscovers.outlook.com/autodiscover/autodiscover.xml"), ews.ExchangeVersion.Exchange2010_SP1);
autod.Credentials = new ews.WebCredentials(user.email, user.password);
var settings = [
ews.UserSettingName.InternalEwsUrl,
ews.UserSettingName.ExternalEwsUrl,
ews.UserSettingName.UserDisplayName,
ews.UserSettingName.UserDN,
ews.UserSettingName.EwsPartnerUrl,
ews.UserSettingName.DocumentSharingLocations,
ews.UserSettingName.MailboxDN,
ews.UserSettingName.ActiveDirectoryServer,
ews.UserSettingName.CasVersion,
ews.UserSettingName.ExternalWebClientUrls,
ews.UserSettingName.ExternalImap4Connections,
ews.UserSettingName.AlternateMailboxes
];
autod.GetUserSettings([user.email], settings)
.then(function (response) {
//do what you want with user settings
}, function (e) {
console.error("autod error", e)
});
}
When I run this, I get: autod error undefined
I have tried some variants, but no luck. Do you know what is wrong here? Many thanks
Softly asking again in case my last question was skipped :) Many thanks again for the help provided already!
Autodiscover may not work with NTLM, in case you are using NTLM you can provide direct URL to ews.
in this example, you have already provided url so it is not going to autodiscover anything
Thank you very much Gautamsi! Do you refer to the code example I gave on February 3rd? The only URL it provides is one for autodiscovery (autodiscovers.outlook.com/...), so I don't understand why you say it is not going to autodiscover anything.
Regarding NTLM, autodiscovery works with the C# code I have provided on January 2nd, so I suspect the reason of failure is not NTLM, do you agree? Or do you mean that the autodiscover implementation of the ews-javascript-api node package does not work with NTLM?
One more hint: I have tried with a 3rd party tool (Calendly) that by giving only the email address and the password, it was able to connect to that specific account, so the autodiscovery has worked in that case too.
Many thanks in advance for any further help! I would love so much to offer autodiscovery in my app :)
I no longer have access to NTLM exchange server to test this :(
Hi @gautamsi, I'm experiencing the same issue as @bfredo123.
I no longer have access to NTLM exchange server to test this :(
I've setup a test Exchange server following these instructions. Is there anything that the instructions are missing to enable NTLM? https://learn.microsoft.com/en-us/exchange/plan-and-deploy/deploy-new-installations/create-azure-test-environments?view=exchserver-2019
That should do it, let me know how can I access this. you can connect with me on linkedin (link in profile) to send message with credential.
thanks @joeauyeung for the server access,
I have updated version to 0.14 (ccd9d300fc6f5e27f81f5f42c9d8c901e82da0b5) which fixes this. The fix is sourced from https://github.com/SamDecrock/node-http-ntlm/pull/107
alternative solution was to use NODE_OPTIONS=--openssl-legacy-provider or the option in the node command line.