Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Rack authentication module for single sign on via NTLM

branch: master

Fetching latest commit…

Octocat-spinner-32-eaf2f5

Cannot retrieve the latest commit at this time

Octocat-spinner-32 lib
Octocat-spinner-32 .gitignore
Octocat-spinner-32 Gemfile
Octocat-spinner-32 LICENSE
Octocat-spinner-32 README.md
Octocat-spinner-32 Rakefile
Octocat-spinner-32 ntlm-sso.gemspec
README.md

NTLM Single Sign On authentication module for Rack

What does this do?

This is an authentication module that presents a connecting client with an NTLM challenge. A client that can speak NTLM will then respond to the challenge according to protocol. All the challenge/response stuff is handled by the module. At the end of the exchange the server will have received the Windows user account name from the client. The module saves the user name to the environment variable REMOTE_USER.

If the exchange failed, the REMOTE_USER variable will be either left empty or assigned to a default value, dependent on the property default_remote_user.

NOTE: The code only causes the server to respond with an NTLM exchange. It doesn't actually verify any of the stuff that the browser sends. I've been using this only as a way to get the Windows account name (which is configured once during IE setup and then locked here) through NTLM. In a situation where the remote user can input a user name and password herself (i.e. not in a controlled environment), the result is not to be trusted. To verify the LM hash in the response you might be able to pass it to Samba's winbind.

How do I use it?

In a rails application you should write an initialiser first. Create a file config/initializers/ntlm-sso.rb with the following contents:

require 'rack'
require 'rack/auth/ntlm-sso'
require 'continuation'

class NTLMAuthentication
  def initialize(app)
    @app = app
  end

  def call(env)
    req = Rack::Request.new(env)

    env = callcc do |cont|
      auth = Rack::Auth::NTLMSSO.new(cont)

      # optional configuration
      #auth.domain = "MYDOMAIN"
      #auth.default_remote_user = "John Lennon"

      return auth.call(env)
    end

    @app.call(env)
  end
end

You will also need to enable this snippet as middleware to be used in your application by adding the line config.middleware.use "NTLMAuthentication" to config/application.rb.

Of course you also have to add gem 'ntlm-sso', '=0.0.1' to your application's Gemfile after building the gem from source.

After restarting your application, all requests should automatically pass through this authentication code.

Notes

Note that due to the nature of NTLM, authentication cannot be made optional. NTLM always responds with error 401 plus a message in the header. A client that does not understand the NTLM protocol will interpret this as plain "Access denied".

If you want to bypass this module when the client is not a browser, you can modify the call function:

  def call(env)
    req = Rack::Request.new(env)

    # Try NTLM auth only if the client is a browser.
    # Their user-agent strings all start with "Mozilla" for historical reasons.
    if req.user_agent[/^Mozilla/]
      env = callcc do |cont|
        auth = Rack::Auth::NTLMSSO.new(cont)

        # optional configuration
        #auth.domain = "MYDOMAIN"
        #auth.default_remote_user = "John Lennon"

        return auth.call(env)
      end
    end

    @app.call(env)
  end
Something went wrong with that request. Please try again.