Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C# code works, but not its Node equivalent: 401 Unauthorized #419

Closed
bfredo123 opened this issue Jan 2, 2023 · 16 comments
Closed

C# code works, but not its Node equivalent: 401 Unauthorized #419

bfredo123 opened this issue Jan 2, 2023 · 16 comments

Comments

@bfredo123
Copy link

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 } }
@bfredo123
Copy link
Author

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!

@bfredo123
Copy link
Author

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!

@cimchd
Copy link

cimchd commented Jan 20, 2023

Same problem here, but no solution yet...

@bfredo123
Copy link
Author

Thanks Cimchd, I feel less alone.

@gautamsi
Copy link
Owner

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

@bfredo123
Copy link
Author

Thank you Gautamsi, the C# code above works. Could it work if NTLM is required?

@gautamsi
Copy link
Owner

gautamsi commented Feb 2, 2023

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.

@bfredo123
Copy link
Author

Thank you so much, will give a try (I have zero skills on ntlm, hope I won't be stuck due to this)

@bfredo123
Copy link
Author

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

@bfredo123
Copy link
Author

Softly asking again in case my last question was skipped :) Many thanks again for the help provided already!

@gautamsi
Copy link
Owner

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

@bfredo123
Copy link
Author

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 :)

@gautamsi
Copy link
Owner

I no longer have access to NTLM exchange server to test this :(

@joeauyeung
Copy link

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

@gautamsi
Copy link
Owner

gautamsi commented Feb 6, 2024

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.

@gautamsi
Copy link
Owner

gautamsi commented Feb 25, 2024

thanks @joeauyeung for the server access,

I have updated version to 0.14 (ccd9d30) which fixes this. The fix is sourced from SamDecrock/node-http-ntlm#107

alternative solution was to use NODE_OPTIONS=--openssl-legacy-provider or the option in the node command line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants