NTLM and Negotiate single-sign-on for Node.js
When using web applications through popular browsers on Windows OS, the user can be logged in automatically to trusted sites that support Windows Authentication. This module allows Node.js applications to deliver a similar single-sign-on user experience, by utilizing native Win32 API calls.
Only Windows OSs are supported. The module can be installed on any Node.js platform, but the native module won't be built.
npm install win-sso
Usage of this module requires some knowledge of the NTLM or Negotiate handshake process. There are multiple sources for this on the web, a starting point for NTLM could be:
- Client: Send http request to server (with FQDN
app.server.com
) - Server: Respond with 401 where the
www-authenticate
header includes NTLM - Client: Create WinSso instance for NTLM:
let winSso = new WinSso('NTLM', 'app.server.com', undefined, false);
- Client: Get authentication request token:
let authRequestToken = winSso.createAuthRequestHeader();
- Client: Resend the http request to the server, setting the
authorization
header to the value of the token - Server: Respond with 401 where the
www-authenticate
header contains the server response token - Client: Get authentication response token:
let authResponseToken = winSso.createAuthResponseHeader(serverResponseToken);
- Client: Resend the http request to the server, setting the
authorization
header to the value of the token - Authentication complete, the server responds to the actual request.
The NTLM handshake always follows this pattern.
- Client: Send http request to server (with FQDN
app.server.com
) - Server: Respond with 401 where the
www-authenticate
header includesNegotiate
- Client: Create WinSso instance for Negotiate:
let winSso = new WinSso('Negotiate', 'app.server.com', undefined, false);
- Client: Get authentication request token:
let authRequestToken = winSso.createAuthRequestHeader();
- Client: Resend the http request to the server, setting the
authorization
header to the value of the token - Server: Respond with 401 where the
www-authenticate
header contains the server response token - Client: Validate the server response token:
let authResponseToken = winSso.createAuthResponseHeader(serverResponseToken);
- If the token returned from step 7 has length 0, the authentication is complete and the client may parse the response content. Otherwise, continue with step 9.
- Client: Resend the http request to the server, setting the
authorization
header to the value of the token - Server: Respond to the actual request where the
www-authenticate
header contains the server response token - Got to step 7
The number of round trips for a Negotiate (Kerberos) handshake varies, so the number of cycles of step 8-11 could be 0, 1, 2 or more. Therefore step 7 must always be done even when the server does not responds with status code 401. This validates that authentication is indeed complete.
To clarify, Negotiate actually means NTLM or Kerberos. In the past NTLM may have been more common in Negotiate scenarios, but currently Negotiate nearly always means Kerberos. The example above is for Kerberos.
Utility method to simplify the case where an application supports multiple platforms, but single-sign-on is only supported on Windows OSs. Returns true if the platform is win32. If this returns false, all the methods below will throw.
This class provides an interface to create and use an authentication context. Due to protocol details, one instance of this class should be created for each connection (socket) communicating with an endpoint where single-sign-on should be used. The class will free the resources it has allocated when it is destroyed, but it is still recommended to call the freeAuthContext() method when the instance will no longer be used.
WinSso(securityPackage: string, targetHost: string | undefined, peerCert: PeerCertificate | undefined, flags: number | undefined)
Class constructor.
- securityPackage: The name of the authentication method to use. Valid values are 'NTLM' and 'Negotiate'.
- targetHost (optional): The FQDN of the target host. This is used to build a SPN string in the authentication message for enhanced security.
- peerCert (optional): If the connection is http, pass undefined. If the connection is https, pass the peer certificate to add Channel Binding to the authentication message for enhanced security.
- flags: Flags to set in the authentication context. If not set, NTML defaults to no flags, while Negotiate defaults to ISC_REQ_MUTUAL_AUTH | ISC_REQ_SEQUENCE_DETECT
Static method that returns the domain and username of the user executing the Node.js process, in the standard format DOMAIN\username
. The credentials of this user will be used for single-sign-on.
Returns the Authentication Request token as a buffer. For NTLM this is the NTLM type 1 message, for Negotiate this is the NegTokenInit message.
Returns the Authentication Request header as a string. This is the token returned by WinSso.createAuthRequest() encoded as Base64, prefixed with the security package name. This is the expected format of the Authorization
header in a http/https request to the target server to initiate the NTLM/Negotiate handshake.
Returns the Authentication Response token as a buffer. For NTLM this is the NTLM type 3 message, for Negotiate this is the NegTokenResp message. May be an empty Buffer if Negotiate handshake is complete.
- inTokenHeader: The content of the 'www-authenticate' header in the response to the Authentication Request.
This method throws if the response could not be created. One such scenario is if the server requires NTLMv1, and the client settings does not permit NTLMv1.
Returns the Authentication Response header as a string. This is the token returned by WinSso.createAuthResponse() encoded as Base64, prefixed with the security package name. This is the expected format of the Authorization
header in a http/https request to the target server to finalize the NTLM/Negotiate handshake. May be an empty string if Negotiate handshake is complete.
- inTokenHeader: The content of the 'www-authenticate' header in the response to the Authentication Request.
This method throws if the response could not be created. One such scenario is if the server requires NTLMv1, and the client settings does not permit NTLMv1.
Releases all allocated resources for the authorization context. Resources are also released when the instance is destroyed, but it is still recommended to call this method when the context is no longer required, such as when an error ocurred or when the socket was closed.
The idea of implementation is based on the work of @snowytoxa, who presented the concept of retrieving NTLM tokens for the current user without any admin privileges in 2014. The source for his project selfhash shortened the development time significantly.