LDAP is mostly used by medium-to-large organizations to have one centralized place to store users and groups and to allow others internal systems to authenticate the users. If you want to build Phoenix applications that will work within an enterprise you will likely have to integrate with an existing LDAP server.
In this article I’m going to show you how you can authenticate and synchronize users to your Phoenix application.
Let’s setup a new Phoenix project and a user model that we can use to demonstrate LDAP authentication / synchronization in Phoenix:
To manage the authentication process I’m going to use Guardian which is one of the most popular authentication framework for use with Elixir. This article is not about Guardian so I’m not going to explain in detail what the code below does. If you’re new to Guardian and want to know more you should read the documentation for Guardian.
This is the basic setup for Guardian that we need to get started. To verify that everything works
you can run
mix phoenix.server and point your browser at http://localhost:4000 and you should just see the message ‘Unauthenticated’. That’s because the PageController is now protected and we’re not logged in yet.
To connect to a LDAP server and authenticate users I’m going to use the Exldap library. Exldap is basically a thin wrapper for the eldap module in Erlang. To make everything a little bit easier we’re also going to use a public LDAP server with demo users from Forumsystems so we don’t have to spend time setting up our own LDAP server for testing.
config/config.exs (use config.secret.exs in a real applications):
The code above will give us access to forumsys public LDAP server which have a few users setup:
euclid. All the users have the password
Develop our own LDAP module
Now we have finally come to the part where we will implement a module to communicate with the LDAP server:
authenticate function takes an uid and a password as arguments so we can authenticate the user. uid stands for user id in LDAP and is used as the computer system login name. The function opens a connection to the LDAP server and verifies the credentials.
get_by_uid function is used to search for the object with a specified uid in LDAP. We’ll use this function later to synchronize username, name & email to our local PostgreSQL database. Even though we have the information in LDAP we probably want to have a local table with our users
so we can have real database relationships with other tables in our application.
to_map functions is just a helper function which transforms an ldap_entry to a map with more sane keys names that we use in our local database.
Setup the SessionController and templates
To authenticate users in Phoenix we need to create a very basic GUI and a Session controller to handle sign in and sign out scenarios.
The interesting parts in SessionController happens in the
insert_or_update functions. In the
create function we just authenticate the user with username / password using our own Ldap module. If the user is authenticated in LDAP we continue to the
handle_sign_in function and calls the
insert_or_update_user function. That function just gets the user attributes from LDAP and creates a map that we can use when we create an Ecto changeset. The changeset deals with all the details and determines if we need to insert the user (first time sign in) or just update it. The user will only be updated if the attributes in LDAP differs from the attributes in our
local user table.
The user model need to be updated with a virtual field for the password and a special
login_changeset that we use in the sign in form.
And finally we need to add our SessionController to the router. Now you can try to start your Phoenix application and point your browser at http://localhost:4000/sign_in and try to login with einstein / password. You should now see the default Phoenix page and a message saying that you’re logged in. To sign out again you can just point your browser at http://localhost:4000/sign_out and you should see the Unauthenticated message again.
Implementing LDAP authentication and synchronization with Elixir / Phoenix was much more straight forward than I thought. The library support is already in place thanks to Guardian and Exldap and if you want dig deeper and implement more advanced things you can always fall back to the eldap module in Erlang which seems to have virtually everything you need to work with LDAP.
It also worth mentioning that there are another LDAP library in Elixir which integrates nicely with Ecto called EctoLdap.