Introduction
In one “enterprise” Ruby on Rails project we had an idea to integrate Windows domain user authentication with Rails application — as majority of users were using Windows and Internet Explorer and always were logged in Windows domain then it would be very good if they could log in automatically to the new Rails application without entering their username and password.
Windows is using NTLM protocol to provide such functionality — basically it uses additional HTTP headers to negotiate authentication information between web server and browser. It is tightly integrated into Microsoft Internet Information Server and if you live in pure Windows world then implementation of NTLM authentication is just a checkbox in IIS.
But if you are using Ruby on Rails with Apache web server in front of it and running everything on Linux or other Unix then this is not so simple. Therefore I wanted to share my solution how I solved this problem.
mod_ntlm Apache module installation
The first step is that we need NTLM protocol support for Apache web server so that it could handle Windows domain user authentication with web browser.
The first thing I found was mod_ntlm, but unfortunately this project is inactive for many years and do not have support for Apache 2.2 that I am using.
The other option I found was mod_auth_ntlm_winbind from Samba project but this solution requires Samba’s winbind daemon on the same server which makes the whole configuration more complex and therefore I was not eager to do that.
Then finally I found that someone has patched mod_ntlm to work with Apache 2.2 and this looked promising. I took this version of mod_ntlm but in addition I needed to make some additional patches to it and as a result I published my final mod_ntlm version in my GitHub repository.
If you would like to install mod_ntlm module on Linux then at first ensure that you have Apache 2.2 installed together with Apache development utilities (check that you have either apxs or apxs2 utility in your path). Then from the source directory of mod_ntlm (that you downloaded from my GitHub repository) do:
apxs -i -a -c mod_ntlm.c
If everything goes well then it should install mod_ntlm.so module in the directory where all other Apache modules is installed. It also tries to add module load directive in Apache configuration file httpd.conf but please check by yourself that you have
LoadModule ntlm_module ...directory.path.../mod_ntlm.so
line in your configuration file and directory path is the same as for other Apache modules. Try to restart Apache server to see if the module will be successfully loaded.
I also managed to install mod_ntlm on my Mac OS X Leopard so that I could later test NTLM authentication locally. Installation on Mac OS X was a little bit more tricky as I needed to compile 64-bit architecture module to be able to load it with preinstalled Apache:
sudo ln -s /usr/include/malloc/malloc.h /usr/include/malloc.h sudo ln -s /usr/include/sys/statvfs.h /usr/include/sys/vfs.h apxs -c -o mod_ntlm.so -Wc,"-shared -arch i386 -arch x86_64" -Wl,"-arch i386 -arch x86_64" mod_ntlm.c sudo apxs -i -a -n 'ntlm' mod_ntlm.so
After this check /etc/apache2/httpd.conf file that it includes:
LoadModule ntlm_module libexec/apache2/mod_ntlm.so
and try to restart Apache with
sudo apachectl -k restart
mod_ntlm Apache module configuration
The next thing is that you need to configure mod_ntlm. Put these configuration directories in the same place where you have your virtual host configuration directives related to your Rails application. Let’s assume that we have domain “domain.com” with domain controllers “dc01.domain.com” and “dc02.domain.com”. And let’s use /winlogin as a URL which will be used for Windows domain authentication.
RewriteEngine On <Location /winlogin> AuthName "My Application" AuthType NTLM NTLMAuth on NTLMAuthoritative on NTLMDomain domain.com NTLMServer dc01.domain.com NTLMBackup dc02.domain.com require valid-user </Location>
mod_ntlm will set REMOTE_USER environment variable with authenticated Windows username. If we are using Mongrel servers cluster behind Apache web server then we need to add the following configuration lines to put REMOTE_USER in HTTP header X-Forwarded-User of forwarded request to Mongrel cluster.
RewriteCond %{LA-U:REMOTE_USER} (.+)
RewriteRule . - [E=RU:%1]
RequestHeader add X-Forwarded-User %{RU}e
Please remember to put all previous configuration lines before any other URL rewriting directives. In my case I have the following configuration lines which will forward all non-static requests to my Mongrel servers cluster (which in my case have HAproxy on port 3000 before them):
# Redirect all non-static requests to haproxy
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^/(.*)$ http://127.0.0.1:3000%{REQUEST_URI} [L,P,QSA]
Rails sessions controller
Now the final part is to handle authenticated Windows users in Rails sessions controller. Here are examples how I am doing this.
routes.rb:
map.winlogin 'winlogin', :controller => 'sessions', :action => 'create_from_windows_login'
sessions_controller.rb:
def create_from_windows_login
if !(login = forwarded_user)
flash[:error] = "Browser did not provide Windows domain user name"
user = nil
elsif user = User.authenticated_by_windows_domain(login)
# user has access rights to system
else
flash[:error] = "User has no access rights to application"
end
self.current_user = user
if logged_in?
# store that next time automatic login should be made
cookies[:windows_domain] = {:value => 'true', :expires => Time.now + 1.month}
# Because of IE NTLM strange behavior need to give 401 response with Javascript redirect
@redirect_to = redirect_back_or_default_url(root_path)
render :status => 401, :layout => 'redirect'
else
render :action => 'new'
end
end
private
def forwarded_user
return nil unless x_forwarded_user = request.headers['X-Forwarded-User']
users = x_forwarded_user.split(',')
users.delete('(null)')
users.first
end
User.authenticated_by_windows_domain is model method that either find existing or creates new user based on authenticated Windows username in parameter and checks that user has access rights. Private method forwarded_user extracts Windows username from HTTP header — in my case it always was formatted as “(null),username” therefore I needed to remove unnecessary “(null)” from it.
In addition I am storing browser cookie that user used Windows domain authentication — it means that next time we can forward this user directly to /winlogin instead of showing login page if user has this cookie. We cannot forward all users to /winlogin as then for all users browser will prompt for Windows username and password (and probably we are also using other authentication methods).
The last thing is that we need to do a little hack as a workaround for strange Internet Explorer behavior. If Internet Explorer has authenticated with some web server using NTLM protocol then IE will think that this web server will require NTLM authentication for all POST requests. And therefore it does “performance optimization” when doing POST requests to this web server — the first POST request from browser will have no POST data in it, just header with NTLM authentication message. In Rails application case we do not need these NTLM authentications for all POST requests as we are maintaining Rails session to identify logged in users. Therefore we are making this trick that after successful authentication we return HTTP 401 code which makes IE think that it is not authenticated anymore with this web server. But together with HTTP 401 code we return HTML page which forces client side redirect to home page either using JavaScript or
create_from_windows_login.html.erb:
<% content_for :head do %>
<script language="javascript">
<!--
location.replace("<%= @redirect_to %>");
//-->
</script>
<noscript>
<meta http-equiv="Refresh" content="0; URL=<%= @redirect_to %>" />
</noscript>
<% end %>
<%= link_to 'Redirecting...', @redirect_to %>
content_for :head is used to specify which additional content should be put in <header> part of layout.
As a result you now have basic Windows domain NTLM authentication working. Please let me know in comments if you have any issues with this solution or if you have other suggestions how to use Windows domain NTLM authentication in Rails applications.
Additional hints
NTLM authentication can be used also in Firefox. Enter “about:config” in location field and then search for “network.automatic-ntlm-auth.trusted-uris”. There you can enter servers for which you would like to use automatic NTLM authentication.

Damn. I needed this two months earlier for an intranet project :).
Great job for figuring this out, I’ll try to implement it later this week.
Comment by Roy van der Meij — December 2, 2008 @ 11:08 am |
Excellent work Raimonds. One question for you in the process of setting-up mod_ntlm in your environment. Have you run across the following error from Apache 2.2?
3229 – SMB_Logon_Server: SMB_SessSetupAndX failed; errorclass = 1, Error Code = 5\n
I’m seeing users intermittently get this, where they are re-prompted for authentication (which doesn’t work). It seems very haphazard and I haven’t yet found a rhyme-or-reason to it.
Comment by Michael Proto — April 9, 2009 @ 12:20 am |
Michael,
If your users are using vista, then they are most surely using NTLM 2.0 on IE. Look into forcing vista to use NTLM instead of NTLM 2.0 and you should be fine. I’ve recently set up mod_auth_ntlm_winbind on a new box at work so we can start using rails with single sign on. I had the same problem in vista, once i changed it to NTLM it went away. HTH
Comment by Jacques Fuentes — April 9, 2009 @ 3:56 pm |
Thanks for this great post!
Is there a minor chance that you could provide build instructions for a windows-only environment (or a windows binary of tje module?
Comment by Rene A. — May 15, 2009 @ 12:54 pm |
As I do not have need for Apache on Windows I am not going to try this mission impossible of building it on Windows :)
There is another module mod-auth-sspi which is for Apache on Windows – see
http://mod-auth-sspi.sourceforge.net/docu/mod_auth_sspi
It is quite old but probably still works.
If you need NTLM on Windows then I think that probably IIS is the best option as it has NTLM integrated into it.
Comment by Raimonds Simanovskis — May 15, 2009 @ 4:06 pm |
Hello.
Are there any problem if the web browser is a Firefox?
If the client operating systems is linux / mac, exist any posibility to get the user authentification from Rails application?
Comment by Rafa — June 22, 2009 @ 11:30 am |
If you are using Firefox on Windows then you can configure automatic log in using NTLM authentication.
Enter about:config in Firefox location field and then find preference
network.automatic-ntlm-auth.trusted-uris
and enter comma separated URLs for which you want automatic NTLM authentication enabled.
If client operating system is Linux or Mac then Firefox will show log in dialog where you can enter your Windows domain user and password to log in.
Comment by Raimonds Simanovskis — June 25, 2009 @ 10:24 am |
Riamonds,
Nice article. Is there any way of knowing what NTLM version (v1 or v2) IE is using? Also, in an earlier post you suggested to force vista to use v1 if it is using v2. How do we do that?
Thanks for your time.
Aravind.
Comment by Aravind — July 14, 2009 @ 12:52 am |
Hey There,
Great work.. seems to the be solution I am looking for!
Unfortunately the download from http://github.com/rsim/mod_ntlm is not working. Are you please able to email me the source?
Thanks
Mel
Comment by Mel — October 13, 2009 @ 3:12 am |
Just try again download from github – it should be working. And even better I would suggest to install git version control tool and then you can clone the source with git.
Comment by Raimonds Simanovskis — October 13, 2009 @ 6:17 pm |
[... - blog.rayapps.com is another nice authority of advice. Car insurance claims [... -
Comment by Online Car Insurance >> http://onlinecarinsuranceclaims.com/ — November 23, 2009 @ 10:32 pm |
HI
Following vulnerabilities are reported against modntlm module.
1.Mod_NTLM_Authorization format string vulnerability
2.Mod_NTLM_Authorization heap overflow vulnerability
Are these vulnerabilities fixed with this version ?
Thanks
hiju
Comment by shiju — April 29, 2010 @ 7:08 am |
HI
Following vulnerabilities are reported against modntlm module.
1.Mod_NTLM_Authorization format string vulnerability
2.Mod_NTLM_Authorization heap overflow vulnerability
Are these vulnerabilities fixed with this version ?
Thanks
Shiju
Comment by shiju — April 29, 2010 @ 7:09 am |
I am not aware of these issues and haven’t fix anything.
Can you give link where it is described and has anyone made any fix for this?
Raimonds
Comment by Raimonds Simanovskis — April 29, 2010 @ 9:58 am |
Any hints on configuring mod_ntlm in a Passenger setup? I saw your workaround http://groups.google.com/group/phusion-passenger/browse_thread/thread/3f01f22ba8e59d53?fwc=2
but wondered if there is any simpler way. The config there is a bit over my head…
Comment by Eric — July 19, 2010 @ 12:33 am |
Latest versions of passenger is working well together with mod_ntlm and mod_rewrite and there is no need anymore to do this workaround. So you do your normal passenger setup and just add … to Apache configuration to ensure that NTLM authentication will be done for /winlogin request.
Comment by Raimonds Simanovskis — July 21, 2010 @ 10:17 pm |