Using Devise and the Cequel ORM to authenticate your users using a Cassandra database
Devise is a “flexible authentication solution for Rails”, while Cequel is a Ruby ORM for Cassandra that uses CQL3 (Cassandra Query Language). We’ll set these up together to achieve our goal: authenticating users of a Rails app using a Cassandra database to store their credentials.
This is rather a “high-level” tutorial: I assume you already know Ruby on Rails, that you can find your way around on Github to install tools and find information about a gem, and so on.
I do go into some details concerning Devise, but beginners might get lost in the way!
Use ccm to create and start a local cluster of Cassandra nodes:
ccm create cequel-auth -v 2.0.9 -n 3 ccm start
Note: you could go with a single node (instead of the
-n 3 argument) on a dev environment.
Create a new Rails 4 app, skipping ActiveRecord files:
rails new cequel-auth --skip-active-record
Add the following gems to your Gemfile:
gem 'devise' gem 'cequel'
Install them, and we’ll be set!
Generate the default configuration file:
rails g cequel:configuration
config/cequel.yaml and tune the settings if needed. For this tutorial, the defaults are good enough (we’ll use the dev environment only):
Once you’re happy with your settings, run the rake command to create the app’s keyspace:
We’ll generate a User model with the following attributes:
Cequel provides a generator to create new models, so we’ll just use that for starters!
rails g cequel User email:varchar username:varchar encrypted_password:varchar
We have to modify the generated
app/models/user.rb file though, because the generator’s template uses a UUID column as the partition key and it’s not configurable.
User model file resemble this one:
id key column is removed, and we’ve made the
timestamps directive has been added too, so that we get
updated_at for free just as with Active Record.
Run the following Rake task to synchronize the database schema with our new model:
This will look into
app/models and make sure our database is synced with everything that is defined here.
We can now start worrying about getting Devise in the game. Our
User model has the required attributes to comply to the
Authenticable module of Devise.
You’ll need to add other attributes in order to support the other modules; see this wiki page for reference.
First, run the devise generator:
rails g devise:install
When it’s done, it will output a number of configuration advice. Take a look at them, I’ll let you decide whether they are relevant to your app.
Because we used the
--skip-active-record option, Devise generated a buggy configuration file.
config/initializers/devise.rb and locate the following line at the beginning of the file:
This line loads the ORM adapter to allow Devise to communicate with our database. Luckily, there’s an adapter for Cequel. Unfortunately, it currently has a bug that prevents it from working well with Devise.
Luckily, I’ve fixed it ☺
Change the above line to the following:
To make this work, add the following adapter gems to your
Gemfile and run
At the very beginning of the
config/initializers/devise.rb initializer file, uncomment the
Making them work together
This is the very simple part of it all. There’s a single line of code to add to our
We’ll now add a view and routes to actually try things out.
Our website will simply display a single page asking the visitor to log in/sign up if he’s not authenticated, and welcoming him otherwise.
Let’s create a HomeController for this purpose:
rails g controller home index
Edit the routes files to make the
home#index action the root route:
We’re also asking Devise to generate routes for
Time to edit the
home#index view. It’ll be really slim, we just want to verify that everything is working right. We’ll also add flash messages to our application layout:
This is nothing but regular Devise stuff:
user_signed_in? allows us to render different content if the user is logged in.
When he is, we display his username and a link to log him out (note the
method: :delete part!). When he’s not, two links allow the visitor to either log in or sign up.
Spawn a new server and go to http://localhost:3000:
This is what you should see:
Click sign up and fill up the form:
Upon validation, you should be brought back to the welcome page and it should render differently:
Well done! But, wait… where’s my user name?!
As you may have guessed, the above forms are provided by Devise. By default, Devise will register a user with as little information as the user’s email and password. The rest is up to us.
Still, you can try to click “Log out” and then “log in” to see that we’ve achieved the goal of this tutorial!
As a bonus for those not really acquainted with Devise, here is how to ask a new member for his username.
Customizing Devise registration form
The first step to customize any Devise view, is to make them available:
rails g devise:views
All the views Devise uses to interact with your users are now available in
What we want to change is the registration form, so let’s have a look at
This is a pretty regular form, just like you should be used to!
We’re going to add a field here, to ask the user for his username. We’ll name it after the attribute name we used in the
This is not enough though, because of the strong parameters feature of Rails: the username parameter is not permitted and thus, will not make it past the controller.
There is documentation about that. What we need to do, is tell Devise to permit
username when signing up. This is done in the
Lines 6 to 11 have been added to the original file. We ask Rails to execute
configure_permitted_params before each action of the
DeviseController. In this method, we just add
:username to the list of permitted params for the sign up action, using a “parameter sanitizer” Devise provides us.
Try to go to the sign up form again, and you should be able to register with a username (which can be any text really):
Once posted, the form brings me back to the home page where I am properly greeted!
This is it!
You now have a base application that accepts user registration and authentication with a Cassandra backend.
Bringing the pieces together was not as difficult as I thought it would be; we’re lucky to have a very active Ruby community that provides all the foundations needed to work with the best tools powering today’s Web 🙂