A tutorial and sample app explaining user authentication with Sinatra. The authentication and session information is handled by Warden, a middleware for Rack. If that didn’t make sense, don’t worry, I’ll go over everything.
This article is intended for people familiar with Sinatra and DataMapper who want multiple user authentication.
If you’ve never built a website with Sinatra I’d recommend Peepcode’s excellent Meet Sinatra screencast, it is definitely worth the twelve dollars.
Passwords should never be stored in plain text. If someone were to get access to your database they’d have all of the passwords. You’d have everyone’s passwords. We need to encrypt the passwords. DataMapper supports a BCryptHash property type which is great because bcrypt is pretty dang secure.
Let’s get started on a
User model. For the rest of this section we will be building a file named
model.rb in stages. The first step is to install the gems we need:
$ gem install data_mapper $ gem install dm-sqlite-adapter
When installing the
bcrypt-ruby is installed as a dependency.
Note: you may need to run the above gem commands with
sudo if you are not using rvm.
Open up (or create) a file named model.rb and require the gems and set up DataMapper:
Now let’s create a User model. In addition to including
DataMapper::Resource we will include the
BCrypt class (the gem is named ‘bcrypt-ruby’, it is required as ‘bcrypt’ and the class is named
Let’s test this code.
$ irb > require './model' > @user = User.new(:username => "admin", :password => "test") > @user.save > @user.password # => "$2a$10$lKgran7g.1rSYY0M6d0V9.uLInljHgYmrr68LAj86rllmApBSqu0S" > @user.password == 'test' # => true > @user.password # => "$2a$10$lKgran7g.1rSYY0M6d0V9.uLInljHgYmrr68LAj86rllmApBSqu0S" > exit
Excellent. We have a User model that stores passwords in an encrypted way.
If you’d like to see another take on using bcrypt, Github user namelessjon has a more complex example with some discussion here.
Warden, a Library for Authentication and User Sessions
Warden is an excellent gem for authentication with Sinatra. I’ve found that the documentation for Warden is lacking which is why I’m writing this. If you want to know the why of Warden read this.
You may have seen that there is a gem called sinatra_warden. Why am I not using that? The sinatra_warden gem chooses the routes for logging in and logging out for you and that logic is buried in the gem. I like for all of the routes in my Sinatra apps to be visible at a glance and not squirreled away.
But ok, on to Warden.
Warden is middleware for Rack. Sinatra runs on Rack. Rack is an adapter to let Sinatra run on many different web servers. Warden lives between Rack and Sinatra.
bundler with Sinatra, this is the Gemfile for this example app. Before You’ll need to create that Gemfile in your directory and run the following in Terminal:
$ bundle install
rack-flash3 to show alerts on pages, the first chunk of code will load our gems and create a new Sinatra app and register session support and the flash messages:
Now in the Warden setup. Most of the lines need to be explained so I’ll mark up the code with comments. This block tells Warden how to set up, using some code specific to this example, if your user model is named User and has a key of
id this block should be the same for you, otherwise, replace where you see User with your model’s class name.
The last part of setting up Warden is to write the code for the
:password strategy we called above. In the following block, they keys of
params which I am using are based on the login form I made.
Hold on a minute. I called an
authenticate method on
user. We need to create such a method in our User class that accepts an attempted password. Back in model.rb we’ll add the following:
Time to define a few routes to handle logging in, logging out and a protected page.