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.
I’ve also written an ActiveDirectory module for Ruby that’s available as a gem and is hosted at http://rubyforge.org/projects/activedirectory.
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
For Rails you should probably plug this into your login filter somewhere.
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?
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.
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… :(
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.
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.
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.
[...] The next step would be getting the Bridge to use the same logins, so that’s a matter of some Rails LDAP magic (I was going to think about Rails-Apache magic, but at some point I may move to an apache-mongrel-proxy type stack and I think that plays havoc when you use apache to authenticate). [...]
[...] Ruby/Rails user authentication with Microsoft Active Directory The comments include some talk about invisible logons which is something I would like to tackle in the near future to see if I could get it to work. This would be the ideal situation for internal network users of the applications so they don’t have to sign in all the time, and it would force authentication from the outside user who isn’t already authenticated on the domain. [...]
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
i wat where to write code what to write controller,model,view plz provide
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!!!
I was searching for an active directory authentication. Thanks it worked….
What the imbalance creates. ,