3
votes

I'm currently working on a rails project in which I am in charge of user authentication. We've decided to use third party authentication and I tried following an example setup. The example is done by Kevin Thompson and is called example.

According to the LDAP sever's documentation, the steps I need to do are:

  1. Connect to the LDAP server.
  2. Bind anonymously (no DN and password).
  3. Search for the LDAP entry using the username
  4. Retrieve the DN for the username if found.
  5. Rebind with the user's DN and password that they supplied.
  6. If this rebind succeeds, the user is authenticated.

I've followed Thompson's example, except that I'm not using nifty; using devise for user management and omniauth-ldap for authentication. However, it's not quite working, and I'm wondering if it has to do with a discrepancy between what the server documentation tells me to do and what omniauth-ldap is actually doing...

Specifically, my problem is that I always get an "Invalid credentials" error. Is this because of a mismatch between what I need to do and what omniauth-ldap is doing?

Advice or suggestions are greatly appreciated!

A little more information about how I've set up (to maintain anonymity, I replaced some things) I can post more of my code upon request.

config/initializers/devise.rb:

  config.omniauth :ldap,
    :host => 'ldap1.its.domain.ext',
    :base => 'ou=People, dc=domain, dc=ext',
    :port => 389,
    :attrs => 'uid',
    :method => :plain,
    :uid => 'uid'

app/controllers/users/omniauth_callbacks_controller.rb:

class Users::OmniauthCallbacksController <     Devise::OmniauthCallbacksController
  skip_before_filter :verify_authenticity_token
  def ldap
    ldap_return = request.env["omniauth.auth"]["extra"]["raw_info"]
    username = ldap_return.uid[0].to_s

    if @user = User.find_by_username(username)
      sign_in_and_redirect @user
    else
      @user = User.create(:username => username,)
      sign_in_and_redirect @user
    end
  end
end
2
how's it going? did you get this working?JoeChin

2 Answers

5
votes

I just solved a similar issue.

First, you'll need to determine if your domain allows anonymous binding. This was not allowed in my case. There is an great pull request for using the current user to bind, dorren/omniauth-ldap. Otherwise, you'll need a system account. Just to get moving initially, I used my username(i.e., userPrincipalName)/password for :bind_dn and :password.

Secondly, for LDAP authentication, the two uid values that are used to authenticate are sAMAccountName(username) or userPrincipalName([email protected]). I found that my system uses userPrincipalName. To prevent the user from entering that in, I just concatenated the domain prior to submitting the form.

Try this config.

config.omniauth :ldap,
  :host => 'ldap.domain.ext',
  :base => 'dc=ldap, dc=domain, dc=ext',
  :port => 389,
  :method => :plain,
  :uid => 'userPrincipalName',
  :bind_dn => 'bind_dn',
  :password => 'password' 

I believe :bind_dn can be of the form:

'CN=LastName\, FirstName, OU=People, DC=ldap, DC=domain, DC=ext'

OR

'[email protected]'

I also found writing a ruby script using Net::LDAP, to bind and search really helped me learn about Active Directory as well, since I had no knowledge of this subject prior to this task.

0
votes

I can't add this as a comment, as I don't have 50 reputation yet, however I found this which may be of use to some people here.

http://blackfistsecurity.blogspot.com.au/2011/12/rails-authentication-using-devise-and.html

I was originally trying to use Omniauth and Omniauth-LDAP without anything else, but the lack of documentation on omniauth-ldap's part makes things difficult.

edit:

Instead of using Omniauth-LDAP, I ended up opting for a vanilla devise install, and wrote my own LDAP functionality. Please note: I use mongoid, and as such the code below is directed towards MongoDB. It can be easily modified for ActiveRecord, however.

In order to do this, I edited the new action in the sessions controller, similarly to as follows:

ldap = Net::LDAP.new
ldap.host = 'domainOrIP'
ldap.port = 389

ldap.auth 'user', 'password'

if ldap.bind
    # success, so let's check if the user exists
     @existing_user = User.where({username: params[:user][:username] }).first

     if @existing_user == nil
       #create the user
         @user = User.new( {username: params[:user][:username], password: ''})
         # I didn't personally store the user's password, as I use LDAP for authentication. (If you save this, please hash and salt it first!!)
         @user.save
         flash[:notice] = "Success!"
         redirect_to '/'
     else # already existed
       @user = User.find({ username: params[:user][:username] })
         flash[:notice]  = "Success!"
         redirect_to '/'
     end
else
    flash[:danger] = "An error occurred whilst authenticating with your LDAP server. Please check the configuration and try again."
    redirect_to '/'
end

I then left Devise to handle everything else. Worked brilliantly for me -- the code above is from memory, however, so may not be 100% accurate. :)

edit 2: More information on how to use the Net::LDAP class can be found here: http://www.rubydoc.info/gems/ruby-net-ldap/Net/LDAP