Really Simple Authentication in Sinatra

One of the biggest requests from people is a post about authentication. There are loads of gems that help to do this, but I like to roll my own. Here’s a really simple way of creating an admin section of a website.

First of all we start by creating some settings:

set :username,'daz'
set :token,'maketh1$longandh@rdtoremember'
set :password,'topsecret'

Next are the handlers for the different routes. There will be a login page, found at ‘/admin’.

get('/admin'){ haml :admin }

This is a simple one-liner, because all it needs to do is display the page (using haml in this case). Here is the code for the view:

%form(action="/login" method="post")
  %label(for="username")Username:
  %input#username(type="text" name="username")
  %label(for="password")Password:
  %input#password(type="password" name="password")
  %input(type="submit" value="Login") or <a href="/">Cancel</a>

This is a straightforward form that asks for a username and password and has a submit button. The form gets submitted using a POST request to the url ‘/login’, so we need to create a handler for that. This is where all the hard work is done in authenticating the user.

post '/login' do
  if params['username']==settings.username&&params['password']==settings.password
      response.set_cookie(settings.username,settings.token) 
      redirect '/'
    else
      "Username or Password incorrect"
    end
end

Basically, the code just checks to see if the username and password that were submitted in the form (held in the params hash) are the same as the settings. If they are then a cookie is set, using the username as the key and the token as the value. You could just use true as the value, but using a token adds an extra layer of security (can anybody elaborate on exactly how?) If the wrong combination of username and password is entered then a basic message is returned (this could be improved upon).

All that is needed now is a way of logging out. This is done by going to the url ‘/logout’.

get('/logout'){ response.set_cookie(settings.username, false) ; redirect '/' }

This sets the cookie with the username key to a value of false.

This takes care of authenticating a user. So how do we actually authorise pages? We simply need to add a helper function to our code, like so:

helpers do
  def admin? ; request.cookies[settings.username] == settings.token ; end
  def protected! ; halt [ 401, 'Not Authorized' ] unless admin? ; end
end

This provides 2 helper methods. The first one is called admin? and is used to check if the user is logged in or not. This can be used in handler logic or in views. So you could display a different message if somebody is logged in. It does this by checking if the cookie value matches the token setting.

The second helper method is protected!. This is used at the beginning of a route handler to say that a user must be logged in to see this route. It uses the aforementioned admin? helper to check if they are logged in and if not halts the code and displays a ‘Not Authorized’ message.

Here is a single file app that demonstrates the code above. Feel free to test it out and experiment with it:

require 'rubygems'
require 'sinatra'

set :username,'Bond'
set :token,'shakenN0tstirr3d'
set :password,'007'

helpers do
  def admin? ; request.cookies[settings.username] == settings.token ; end
  def protected! ; halt [ 401, 'Not Authorized' ] unless admin? ; end
end

get '/' do
  haml :index
end

get('/admin'){ haml :admin }

post '/login' do
  if params['username']==settings.username&&params['password']==settings.password
    response.set_cookie(settings.username,settings.token) 
    redirect '/'
  else
    "Username or Password incorrect"
  end
end

get('/logout'){ response.set_cookie(settings.username, false) ; redirect '/' }

get '/public' do
  'Anyone can see this'
end

get '/private' do
  protected!
  'For Your Eyes Only!'
end

__END__
@@layout
!!! 5
%html
  %head
    %meta(charset="utf-8")
    %title Really Simple Authentication
  %body
    %a(href='/admin')Login
    %a(href='/logout')Logout
    %a(href='/public')Public
    %a(href='/private')Private
    = yield
@@admin
%form(action="/login" method="post")
  %label(for="username")Username:
  %input#username(type="text" name="username")
  %label(for="password")Password:
  %input#password(type="password" name="password")
  %input(type="submit" value="Login") or <a href="/">Cancel</a>
@@index
-if admin?
  %h1 Welcome 007!
-else
  %h1 Welcome!

This is a very simple example of authentication and authorisation … but it’s useful for playing around with. If you can find any ways to build on and improve this then please add your ideas in the comments below.

blog comments powered by Disqus