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, once I’ve got a full understading in the domain, I’ve 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 when user needs to enter his / her username and password once in Active Directory when he logs to the operating system and later, when using a Web application, he gets automatically authenticated using his AD credentials.

For this, browsers support the following: when browser gets HTTP code 401 “Not Authorized” and <WWW-Authenticate: Negotiate> header in a response to GET request it triggers a request to KDC (Key Distribution Center, one of AD services) to obtaine a special SPNEGO-token for the web service. If no record was found 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.

Next, the web service gets the token and sends it to KDC using a Kerberos-client. If it’s a valid one, the web service gets the user name as defined in AD which 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 - to send a token for a check
  • authUserKrb5Password - authentication with 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 received token:

//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 to get all groups of a user. For a bigger number of groups, it works much faster than the one in the activedirectory module.

As the result, if you’ve done everything right, you should see a web page that displays username and the list of groups he is a member of.

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.