Single SignOn: Authenticate Users with their Windows ActiveDirectory credentials
Summary
Use a OmniAuth Strategy to authenticate against a LDAP ActiveDirectory EndpointLesson
Using an existing "store" of a user registry/directory like ActiveDirectory (AD) in a Windows Domain relieves the Rails application developer from implementing the auth logic, storing passwords, managing rights and can be considered a huge ergonomic boon for the User because he needs to store one less password in his password manager.
We're going to build on top of the system laid out in this article: Rails 8 auth with OAuth and authenticate using LDAP against the Windows AD.
Unfortunately, the "endorsed" LDAP gem, listed in the OmniAuth List of Strategies is a bit dated and not kept up to date with latest dependency versions.
Luckily, Gitlab seems to be picking up the torch and bringing in patches and updates in the Gem gitlab_omniauth-ldap
. However, after I ran into some issues with Rack 3, there is even some more bleeding edge required. Thes repo below fixes a breakage after a deprecation, so we need to install a Gem from Gitlab by adding the following line into our Gemfile
gem "gitlab_omniauth-ldap", "~> 2.2", git: "https://gitlab.com/vicvega/omniauth-ldap.git"
With this, it is possible to add a LDAP OmniAuth provider in the corresponding initializer
# config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :developer if Rails.env.development? || Rails.env.test?
provider :ldap,
host: "<IP of ActiveDirectory Server>",
port: 636, # use 389 for LDAP, 636 for LDAPS
method: :ssl, # :ssl or :tls for secure connections
# disable_verify_certificates: true, # turn off SSL certificate verification
base: "DC=<DOMAIN>,DC=<MORE-DOMAIN-NESTING>",
uid: "userPrincipalName", # typically 'uid' or 'sAMAccountName' for Active Directory
bind_dn: Rails.application.credentials.dig(:ldap, :bind_dn),
password: Rails.application.credentials.dig(:ldap, :password), # the password for the bind user
filter: "(&(userPrincipalName=%{username})(memberOf=CN=<GROUP>,DC=<DOMAIN>,DC=<MORE-DOMAIN-NESTING>))",
name_proc: ->(name) { name.force_encoding("utf-8") } # handle encoding
end
The specifics need to be given by your System Administrator of choice, the full AD identifier (potentially with OU,DC, etc... elements) and notably (ideally) service account credentials that you put in your credentials file, for the example above:
ldap:
bind_dn: <identifier of service account>
password: <its password>
Next, the content of the filter line also depend on the configuration by the SysAdmin. Same for the uid that will be used to identify a given User in the AD. If the AD uses self-signed certificates, uncomment the line with disable_verify_certificates: true
.
Overall, I'd love to see the maintainership a bit streamlined, while I can understand that time is limited, it's a pity if the Gems that were once the "standard" fall behind and start to get forked.