Post

Windows Authentication Tips for HttpPlatformHandler

This post talks about the Windows authentication tips for all HttpPlatformHandler.

Windows Authentication Tips for HttpPlatformHandler

When hosting web apps on IIS/Windows, some administrators prefer to use Windows authentication. So when the web stack is not from Microsoft, such as Node.js, Go, or Python, you might wonder how to properly enable Windows authentication for them so that your web apps know who are accessing them.

Enable Windows Authentication on IIS

The internet is full of articles on how to enable Windows authentication on IIS, so I won’t repeat them here.

Windows Authentication for HttpPlatformHandler

When you use HttpPlatformHandler to host your web apps, you might find that Windows authentication does not work as expected. This is because HttpPlatformHandler does not pass the Windows authentication information to the backend web apps by default.

To enable Windows authentication for HttpPlatformHandler, you need to add the following configuration to your web.config:

1
2
3
4
5
6
7
8
<configuration>
  <system.webServer>
    <handlers>
      <add name="myHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified" requireAccess="Script" />
    </handlers>
    <httpPlatform processPath="xxxxx" arguments="xxxxx" stdoutLogEnabled="false" startupTimeLimit="3600" forwardWindowsAuthToken="true" />
  </system.webServer>
</configuration>

The forwardWindowsAuthToken attribute is the key to enabling Windows authentication for HttpPlatformHandler. When set to true, HttpPlatformHandler will pass the Windows authentication information to the backend web apps via the X-IIS-WindowsAuthToken header.

Access Windows Authentication Information in Your Web App

After enabling Windows authentication for HttpPlatformHandler, you can access the Windows authentication information in your web app by reading the X-IIS-WindowsAuthToken header. The header contains the Windows authentication information in the format of a Windows token handle. Therefore, you need to call the relevant Windows API to query the user information from the token handle.

Python Example

Here is an example of how to access the Windows authentication information in a Python web app:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import win32api
import win32security
from flask import request
import logging

# Note if using aspNetCore instead of original httpPlatformHandler then the header would be 'Ms-Aspnetcore-Winauthtoken' instead of 'X-IIS-WindowsAuthToken'
# Also note that the header lookup is case-insensitive (for Flask at least, as in this example)
token_handle_str = request.headers.get('X-IIS-WindowsAuthToken', None)
if token_handle_str:
    token_handle = int(token_handle_str, 16) # need to convert from Hex / base 16
    sid = win32security.GetTokenInformation(token_handle, 1)[0] # TOKEN_INFORMATION_CLASS enum value 1 = TokenUser
    user, domain, account_type = win32security.LookupAccountSid(None, sid)
    win32api.CloseHandle(token_handle) # don't leak resources, need to close the handle!
    logging.warning(f'Request initiated by user {user}')

Ruby Example

Here is an example of how to access the Windows authentication information in a Ruby web app:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
module WIN32
  extend Fiddle::Importer
  dlload 'kernel32.dll'
  include Fiddle::Win32Types
  extern 'BOOL CloseHandle(HANDLE)'
end

module SelfServicesHelper
  def authorize_by_authtoken
    if request.headers.key? "HTTP_X_IIS_WINDOWSAUTHTOKEN"
      handle = request.headers["HTTP_X_IIS_WINDOWSAUTHTOKEN"].hex

      buflen = 64
      tokenInfo, len = "\0" * buflen, [0].pack("L!")
      if WIN32Security::GetTokenInformation(handle, WIN32Security::TokenUser, tokenInfo, buflen, len) != 0
        namelen, dnamelen, use = *[32,32].map{|e| [e].pack("L!")}, [0].pack("I")
        namebuf, dnamebuf = [namelen, dnamelen].map{|e| "\0".encode("utf-16le") * e.unpack1("L!")}
        # ... PSID is at the top of tokenInfo
        if WIN32Security::LookupAccountSidW(nil, tokenInfo.unpack1("Q!"), namebuf, namelen, dnamebuf, dnamelen, use) != 0
          namelen, dnamelen = [namelen, dnamelen].map{|e| e.unpack1("L!")}
          WIN32::CloseHandle(handle)
          logger.debug {"namebuf: %s, dnamebuf: %s" % [namebuf[0, namelen].encode("utf-8"), dnamebuf[0, dnamelen].encode("utf-8")}
        else
          logger.error "LookupAccountSidW returned false, last error: %d" % Fiddle.win32_last_error
        end
      else
        logger.error "GetTokenInformation returned false, last error: %d" % Fiddle.win32_last_error
      end
    else
      logger.debug "no HTTP_X_IIS_WINDOWSAUTHTOKEN"
    end
  end
end

Node.js Example

To invoke Win32 API in Node.js apps, the packages need to be installed via npm:

1
npm install ffi-napi ref-napi ref-struct-napi

However, they seem to be not working well, so I don’t currently have a working sample to show here.

Go Example

There is a dedicated package for this purpose in Go.

Conclusion

This post talks about how to enable Windows authentication for HttpPlatformHandler and how to access the Windows authentication information in your web app. By following the steps in this post, you can enable Windows authentication for your web apps hosted on IIS/Windows and access the Windows authentication information in your web app.

© Lex Li. All rights reserved. The code included is licensed under CC BY 4.0 unless otherwise noted.
Advertisement