By sjoekle March 5, 2017 4:00 PM
NodeJS: SSO with Kerberos

All ingenious is simple. But to achieve the simplicity one has to read thousands of manuals. That is why, after I have figured out the topic for myself, I have decided to write a quick-start tutorial about implementing Single Sign-On in a web application connected to Active Directory (AD). Also I would like to share my test project and I am looking for your feedback.

Let’s start with some theory. SSO is an idea, according to which a user enters his username/password once in Active Directory when he starts his PC session and later, when using a Web application, he gets automatically authenticated using his AD credentials.

For this, web browsers support the following: when a browser gets HTTP code 401 “Not Authorized” in a response to a GET request together with a header <WWW-Authenticate: Negotiate>, the browser makes a request to KDC (Key Distribution Center, one of AD services) for obtaining a special SPNEGO-token for the web service. If there is no record for such a web service in the AD, the browser provides a standard response for NTLM authorization.

If the web service is registered with AD, the browser sends the GET request again with the Authorization header containing YIIJvwYGKw… When the token starts with YII, it means that it is a Kerberos-encoded token which contains data for authentication.

Kerberos is just a type of encryption but since it is normally used for SSO, these concepts are tightly connected.

After the web service gets the token, it sends the token to KDC using a Kerberos-client. If the token is valid, the web service gets the user name as defined in AD which it can be used to get more information about the user.

The best visualization of this process is found in the official documentation by Microsoft:

https://msdn.microsoft.com/en-us/library/ms995329.http-sso-1-fig03(l=en-us).gif So, apart from the web application development, the following steps are required:

What needs to be done on the Active Directory side:

  • set the SPN-name for the web service in AD (following HTTP/webservice.example.com@EXAMPLE.COM pattern). This will allow browsers to request a token for the given service and transfer it in the HTTP header to the backend.

  • configure a krb5.keytab key for the web service, which will be used by the Kerberos client to verify user tokens.

What needs to be done on the web service side:

  • a Kerberos client needs to be installed (Windows has it by default, on Linux - for example, in RedHat: yum install krb5-workstation krb5-libs krb5-auth-dialog krb5-devel)
  • configure the client by modifying krb5.conf to allow accessing KDC
  • install the key file in /etc/krb5.keytab.

In the web application:

  • install a kerberos module for communication with the kerberos client
  • install an active directory module for making LDAP requests.

Caveat: on Windows the Kerberos module provides a special API which didn’t work for me. If anyone knows how to make it work, please let me know.

On Linux the kerberos module offers two main methods, which we need:

  • authGSSServerStep - for sending a token for a check
  • authUserKrb5Password - authentication by login/password, in case if SSO failed.

There is no documentation how to use these methods but there are helpful comments in the file lib/kerberos.js.

The following code snippet checks the token that the web service receives:

//cut phrase "Negotiate "
var ticket = req.headers.authorization.substring(10);

//init context
kerberos.authGSSServerInit("HTTP", function(err, context) {
  //check ticket
  kerberos.authGSSServerStep(context, ticket, function(err) {
    //in success context contains username
    res.set( 'WWW-Authenticate', 'Negotiate ' + context.response);
    res.send(context.username);
  });
});

the test project on GitHub.

In the test project you can also see how to create a filter for getting all groups of a user that works much faster than the one in the activedirectory module given a big number of groups.

As the result, if you have done everything right, you should see a web page which displays the user name and the list of groups the user is in.

Limitations

  • the service URL cannot be in the form ip:port. It has to be a DNS-name.
  • this kind of authentication works with certain settings in IE (currently, they are enabled by default, “Automatic logon only in Intranet zone” and “Allow Windows Integrated authentication”). Chrome by default uses IE settings. Other browsers may require additional configuration.