|
Using Rack for Windows Pass Through Authentication with NTLM
By: Bruce Bahlmann - Contributing Author (your
feedback
is important to us!)
In rails development, particularly when operating over an enterprise or
Citrix network where all your end user browsers end up being some standard
version of Microsoft Internet Explorer (IE), a logical way to authenticate
clients is to hijack their previous authentication onto the IT network. In
other circles this hijacking would be referred to as windows pass-through
authentication as what you are doing is bypassing individual application
authentication and instead using the authentication the end user already
completed as a result of logging into the IT network.
If you are a Microsoft Windows shop with a surplus of IIS web servers, you
basically get this authentication for free as these windows-based web
servers return a convenient REMOTE_USER environmental variable which is
essentially the user's current login name. From there, a simple look up in
Active Directory (AD) via the sAMAccountName gives you all the information you need
about that login name - without the user having to log in or provide you anything.
Well, if you are reading this, clearly you DO NOT HAVE such an IIS setup,
but are wanting to obtain the REMOTE_USER variable with some other
web server like Apache, nGenx, etc. In this article, we show you a web
server independent way to generate REMOTE_USER. Start by including one key
resources Net/NTLM to your Gemfile.
[Gemfile]
gem 'rubyntlm'
Next you need to add some Rack library code to allow your Rails application to
handle the NTLM rather than require your web server to do this. The essential library is below, but you could just as easily expand on this to
include AD lookups, etc. so as to provide more details about the user - like
their first/last name, email, etc.[lib/auth_ntlm.rb]
require 'net/ntlm'
require 'kconv'
class AuthNtlm
def initialize(app, config = {})
@app = app
@options = {}
@config = {
## Initialize Defaults
:uri_pattern => /\//
}.merge(config)
end
def call(env)
@status,@headers,@response = @app.call(env)
if env['PATH_INFO'] =~ @config[:uri_pattern] && env['HTTP_AUTHORIZATION'].blank?
return [401, {'WWW-Authenticate' => "NTLM"}, []]
end
if /^(NTLM|Negotiate) (.+)/ =~ env["HTTP_AUTHORIZATION"]
message = Net::NTLM::Message.decode64($2)
if message.type == 1
type2 = Net::NTLM::Message::Type2.new
return [401, {"WWW-Authenticate" => "NTLM " + type2.encode64}, []]
end
end
if message.type == 3 && env['PATH_INFO'] =~ @config[:uri_pattern]
user = Net::NTLM::decode_utf16le(message.user)
@headers['REMOTE_USER'] = user
end
else
#return [200, {"Content-Type" => "text/html"}, ["not sure what to do here..." + env.inspect]]
end
[@status,@headers,self]
end
def each(&block)
@response.each(&block)
end
end
One serious limitation about NTLM is that although it works great for
obtaining the login name of users which clearly could be used to look up
those users on a network, NTLM does not provide access to the user's
login password. While it is true that NTLM does a hash of the user
password, this hash is of the user password converted to all CAPS and
appears to be a one-way hash to boot. So, even if you were able to figure
out how to un-hash/translate the NTLM password from its traces used by NTLM,
you will not have a usable password once that is complete because the
process would be insensitive to the password case.
The final step in activating the Rack is to insert the call into the
application configuration.
[config/application.rb]
class Application < Rails::Application
…
config.middleware.use "AuthNtlm", {
:uri_pattern => /\// # (default = /\//) (any URL)
}
# Custom directories with classes and modules you want to be autoloadable.
#config.autoload_paths += %W(#{config.root}/extras)
config.autoload_paths += %W(#{config.root}/lib)
The call to your Rack library file should be placed below the class
Application < Rails::Application line within module named after your Rails
application. Note, there is one other change, that is to tell Rails to
autoload your Rack library files placed in /lib. To do this, you need to
uncomment and modify or add a new similar line (what we did) to configure
the autoload. If you do not tell Rails to autoload, Rails will not know
where to find your Rack application.
We would like to acknowledge the previous contribution of lukefx who
originally authored rack-ntlm from which our work was based. We could not
make rack-ntlm work as it is presented in GitHub, there is also the serious
limitation about passwords which lukefx conveniently failed to mention,
instead he hard coded the users password into the application configuration.
If you need to support lots of different browsers (particularly FireFox),
this solution will not work. Your best bet in this case would be to use
rack-auth-kerberos. However, if you are an IE shop, the above solution
should work just fine.
Can Birds-Eye.Net help you or your Company?
Receive your Birds-Eye.Net articles and white
papers hot off
the presses by adding our RSS feed to your reader.
|
|