Ruby/Rails user authentication with Microsoft Active Directory


Firstly you need to know that Active Directory is actually an implementation of LDAP in Windows 2000/2003 Server, and it follows most of the convention in any LDAP server. After that, it is relatively simple to use Active Directory as your authentication server. I’m going to show you how to use Active Directory to authenticate your users, but not to do stuff that changes the Active Directory entries.

What you will need to try this:

  • An Active Directory Server which you have an account (this might be your Domain Controller, in Windows parlance)
  • The Active Directory Server needs to be accessible through port 389, the standard LDAP port. If you are using a corporate Active Directory, your system administrator would have probably sealed off all ports except those necessary (which normally doesn’t include port 389)
  • Ruby/LDAP installed. There are other ways of access LDAP servers including the easier ActiveLDAP but Ruby/LDAP shows a lower level of access. In any case ActiveLDAP actually wraps around Ruby/LDAP so you’ll need that anyway. I’ll include a short writeup on getting Ruby/LDAP on your Windows machine

That’s it! Let’s start.

Getting Ruby/LDAP on your Windows machine

For this I’m assuming you are running Windows XP. For Linux or other variants you can try to build it yourself. The Ruby/LDAP project is an open source project that provides a library for Ruby applications to access LDAP servers. To install it on Windows, the best tutorial I’ve found is at Olivier’s Toolbox. This gem (pun intended) even provided a RubyGem that you can download and install!

> gem install ldap-0.9.5-mswin32.gem

Alternatively you can try following Olivier’s instructions to build the gem or even try it from the source itself if you’re not into Windows.

Authenticating with Ruby/LDAP

This little piece of code below (open sourced under MIT License) is a concise and direct method of authenticating a user with Ruby/LDAP and getting the groups that the user is assigned to.

require 'ldap'

class Auth
connection = nil
host = "localhost"
port = 389
dn = nil

def initialize(dn,host="localhost",port=389)
@host = host
@port = port
@dn = dn
end

def login(login, pass)
begin
conn = LDAP::Conn.new(@host, @port)
conn.set_option( LDAP::LDAP_OPT_PROTOCOL_VERSION, 3 )
conn.bind( login, pass )
@connection = conn
return true
rescue => e
false
end
end

def get_member_of(username)
result = @connection.search2( @dn, LDAP::LDAP_SCOPE_SUBTREE, "cn=#{username}", ["memberOf"])
members_of = Array.new
result.first["memberOf"].each { |str|
members_of < < str.split(',')[0].split('=')[1]
}
members_of
end

def close
@connection.unbind unless @connection == nil
@connection = nil
end

end

# testing the code
auth = Auth.new("CN=Users,DC=myserver,DC=com",ad.myserver.com, 389)
auth.login("sausheong@myserver.com", "password")
groups = auth.get_member_of("Sau Sheong Chang")
auth.close

Note that your username needs to be in the form of username@domainname. This is apparently a peculiarity with Active Directory, which is different from more standard LDAP servers. The rest of the code is quite self-explanatory. If you want something that allows you to authenticate quickly, this is the shortcut:

require 'ldap'

class Auth
connection = nil
host = "localhost"
port = 389
dn = nil

def initialize(host="localhost",port=389)
@host = host
@port = port
end

def authenticate(login, pass)
begin
conn = LDAP::Conn.new(@host, @port)
conn.set_option( LDAP::LDAP_OPT_PROTOCOL_VERSION, 3 )
conn.bind( login, pass )
return true
rescue => e
false
ensure
conn.unbind unless conn == nil
conn = nil
end
end

end

# test the code
auth = Auth.new("ad.myserver.com", 389)
if auth.authenticate("sausheong@myserver.com", "password")
puts "You're authenticated!"
end

Drop me a mail if you have any questions on the code.

20 thoughts on “Ruby/Rails user authentication with Microsoft Active Directory

  1. I am new to Rails and starting to trudge through tutorials. How and where is the code above used i.e. in a Rails controller? What would a view look like to see the test data?

    Thanks for the post!

    M.W. Rails

  2. Any experience with automatic / invisible logins?
    This is a requirement of a new RoR app I am designing for internal government purposes.
    There was a page purportedly written by a Microsoft employee that mentioned this, but the page has apparentlt been removed.

    Google’s cached version is here:
    http://72.14.203.104/search?q=cache:Xx6-552ngMIJ:wiki.rubyonrails.org/rails/pages/WindowsDomainAuthentication+ruby+rails+windows+authentication&hl=en&gl=us&ct=clnk&cd=7&client=firefox-a

    Does anyone know anything about this that could post more of an in depth example more than the one line the the linked article?

  3. I don’t have IIS installed to try, but the explanation sounds pretty easy. You have to set IIS authentication to “Integrated Authentication”. This is somewhat like your HTTP authentication except that it will go to your Windows authentication if it is available, or automatically log you in, if you are already logged into the correct domain. The application itself isn’t protected by any of its own access control mechanism, and like HTTP Authentication you can’t control it directly. As the example mentioned however you can use the environment variable “AUTH_USER” to see who is logged in.

    Hope this helps.

  4. That helps a bit, but I cannot use IIS (using Red Hat webservers), and I must have automatic logins via active directory. Maybe this cannot be done in Rails without Microsoft’s help… :(

  5. AFAIK you can’t use integrated authentication if you’re not using Windows servers. In fact integrated authentication is specific to IIS only and IIS only runs on Windows servers. But I could be wrong.

    Perhaps you can do a workaround like using an activex control to capture the login from your IE and use that to log into the Rails app. It’s a bit hackish though but if I remember correctly some SSO solutions use a similar mechanism.

  6. I’m using an Apache 2.0.55 on Windows XP to run a single-sign-on Mediawiki (PHP). The authentication stuff is done by mod_sspi, which adds NTLM/SSPI authentication to Apache.

    Mod_sspi creates the server environment variable REMOTE_USER which contains the name of the authenticated user. I have not tested this setup together with Rails and/or on a non-Windows machine.

    The Apache config looks like this:

    LoadModule sspi_auth_module modules/mod_auth_sspi.so

    AllowOverride None
    Options None
    Order allow,deny
    Allow from all

    AuthName “Some description”
    AuthType SSPI
    SSPIAuth on
    SSPIOfferSSPI on
    SSPIAuthoritative off
    SSPIOfferBasic on
    SSPIDomain NAME-OF-WINDOWS-DOMAIN
    SSPIOmitDomain on
    SSPIUsernameCase lower

    require valid-user

    If SSPIOfferBasic is set to ‘on’, Apache offers basic authentication to the browser, which in turn offers a username/password promt to user where she can enter her Windows username and password.

    I expect that this setup should work with Rails too, maybe also on a non-Windows server.

  7. I’m using an Apache 2.0.55 on Windows XP to run a single-sign-on Mediawiki (PHP). The authentication stuff is done by mod_sspi, which adds NTLM/SSPI authentication to Apache.

    Mod_sspi creates the server environment variable REMOTE_USER which contains the name of the authenticated user. I have not tested this setup together with Rails and/or on a non-Windows machine.

    The Apache config looks like this:

    LoadModule sspi_auth_module modules/mod_auth_sspi.so

    AllowOverride None
    Options None
    Order allow,deny
    Allow from all

    AuthName “Some description”
    AuthType SSPI
    SSPIAuth on
    SSPIOfferSSPI on
    SSPIAuthoritative off
    SSPIOfferBasic on
    SSPIDomain NAME-OF-WINDOWS-DOMAIN
    SSPIOmitDomain on
    SSPIUsernameCase lower

    require valid-user

    If SSPIOfferBasic is set to ‘on’, Apache offers basic authentication to the browser, which in turn offers a username/password promt to user where she can enter her Windows username and password.

    I expect that this setup should work with Rails too, maybe also on a non-Windows server.

  8. Hi,
    I would like to know where I can find all the attributes of a user (like “memberof”) in Active Directory in order to retrieve them: for instance, email, phone etc.
    Moreover, how could I supply the login name (ie. sausheong@myserver.com) and retrieve the complete name (Sau Sheong Chang)?
    Many Thanks.
    Ax

  9. Hi,
    I would really like to get ldap-0.9.5-mswin32.gem… But all download servers seem to be down… Does anybody have a funktional link/download?

    Many Tanks!!!

  10. Hi there, I discovered your blog by way of Google at the same time as
    searching for a comparable topic, your web site came up, it seems great.
    I have bookmarked it in my google bookmarks.

  11. This is the perfect website for anyone who hopes to find out about this topic.

    You understand a whole lot its almost hard to argue with you (not that I personally would want to…HaHa).

    You definitely put a brand new spin on a topic that has been discussed
    for many years. Great stuff, just excellent!

Leave a comment