6

Is there a way to check whether Windows Authentication (Negotiate, Kerberos, NTLM...) is available in the browser (with JavaScript or other methods) without browser prompting for username and password if it is not?

Background

We are developing Angular2 SPA corporate application and want to have SSO with Windows Authentication if it is available (user is accessing the application from Domain joined computer) and gracefully fail over to form based authentication if it is not available (accessing from the Internet or non domain joined computer).

Setup

Have a resource on server side (namely a REST method) which is protected by Windows Authentication. When you call this REST method with JavaScript, either of two things will happen:

  1. If Windows Authentication is available, method will be invoked and you will get a 200 OK response
  2. If Windows Authentication is not available, 401 Unathorized will be returned with WWW-Authenticate header set to Negotiate which will result in browser (at least IE, not tested in others but need to be able to detect this regardless of the browser used) displaying Login prompt

Problem

Displaying of Login prompt is undesired, we want to gracefully fall back to form based login.

Some solutions suggest to remove WWW-Authenticate header from response but this will prevent Windows Authentication to work since this step is part of broser-server negotiating an authentication protocol. If removed, browser will never send NTLM or Kerberos token.

We control both the front end and the backend so can modify either one to make it work. Also, there is no CORS, everything is server from a single domain.

Any method that will detect whether Windows Authentication is available or not from the client is good enough for us. What I have found so far is this but it is only for IE and requires ActiveX to be enabled. This question is somewhat related but it too doesn't have a solution and is quite old.

Aaron Mason
  • 310
  • 3
  • 11
Marko Vodopija
  • 578
  • 6
  • 12
  • 1
    I have this same need. I've stood up IdentityServer, which can be set up to allow users to login with either forms or NTLM. NTLM is treated like an external auth provider and the user can click a button to use that, or use the login form (or Google, etc. if you add that). However, I'd like to make it so that the "Windows" button says "Login as DOMAIN\user" if the user is domain-joined and has automatic login enabled, without prompting for login when they don't. – Daniel May 24 '17 at 17:40
  • @Daniel I still didn't find solution for this problem. We will most likely implement it exactly the way you described. I currently see no other way sadly. If you find something useful be sure to let me know. – Marko Vodopija May 24 '17 at 21:11
  • Good to know I'm not the only one ;) Do post a solution if you find one. – Daniel May 26 '17 at 14:07
  • Have you found the solution to this problem yet? Would love to know if this is possible. Thanks! – Sunil Jamkatel Mar 04 '20 at 21:18
  • @SunilJamkatel No, we did not. We will most likely go into direction of creating login.domain.com type of SSO where from Internal network (LAN) it will point to a site where there is Windows Authentication and from Internet it will point to a site with WA disabled. This will prevent showing login prompt for external users which is good enough. – Marko Vodopija Mar 05 '20 at 08:56

1 Answers1

1

We are using an XmlHttpRequest within an Web Worker. This way it is possible to perform Kerberos authentication without triggering the login window if it fails.

Web Workers are supported by IE11, so you should be good to use it.

This involves proving an single API end point where you can try to authenticate via Kerberos, e.g. 'yourhost.tld/krb5LoginUrl'

Your Webworker (provide as e.g. task.js):

self.addEventListener('message', function(e) {

  var oReq = new XMLHttpRequest();
  oReq.addEventListener("load", reqListener);
  oReq.open("GET",  e.data);
  oReq.send(null);

  function reqListener () {
    self.postMessage(this.status);
  }, false);

Register the worker in your main application code: const worker = new Worker('task.js');

worker.postMessage(/krb5LoginUrl); // Sends the url to our worker and triggers xmlHttpRequest.

worker.addEventListener('message', function (e) {
  if (e.data == 200) {
    console.log("Krb5 login successful");
  } else {
    console.log("Krb5 login failed");
  }
 }, false);
}