Adding Basic HTTP Authentication to Rails Routes in Plain Ruby on Rails

In this article, we will look at implementing basic HTTP authentication in Ruby on Rails using constraints in Rails routes. This is useful for admin panels and other similar functionality where you want to restrict access to a specific group of users. Let's dive in.

Understanding Basic HTTP Authentication

Basic HTTP Authentication is a security technique in which a user agent passes a username and password to the server as part of an HTTP request. This information is base64-encoded, but not encrypted, making it suitable for simple authentication cases where traffic is served over HTTPS.

Setting up your private routes

The first step is to define your route for example within a namespaced admin section. This is where we'll set up our protected routes.

# routes.rb
namespace :admin, path: '/panel_admin' do
    root to: 'base#index'
    constraints AdminConstraint do
      mount GrapeSwaggerRails::Engine, at: '/docs'
      mount MissionControl::Jobs::Engine, at: '/jobs'
      get 'protected', to: 'protected#index'
    end
    # redirect to login from any path when unauthorized
    get '*path', to: redirect('/panel_admin')
end

Any unauthorized requests will be redirected back to the /panel_admin front page, because the rest are under AdminConstraint block blocked by our custom logic.

Creating the AdminConstraint

The AdminConstraint class checks that the request matches the correct HTTP Authentication. Here's how you set it up:

# config/initializers/admin_constraint.rb
class AdminConstraint
  def self.matches?(request)
    return request.env["HTTP_AUTHORIZATION"] == "Basic " + Base64.strict_encode64("#{ENV['ADMIN_LOGIN']}:#{ENV['ADMIN_PASSWORD']}")
  end
end

In our method matches?, we are comparing the incoming request's HTTP_AUTHORIZATION header to the Basic Auth format, which is 'Basic base64_encoded_credentials'. Please make sure you have your ENVs filled properly

#.env
# add long and complicated login and password
ADMIN_LOGIN=abcd
ADMIN_PASSWORd=abcd

Setting Up the Admin Controller

In your admin controller, call http_basic_authenticate_with at the beginning of the code block. This triggers the authentication mechanism.

class Admin::BaseController < ApplicationController
http_basic_authenticate_with name: ENV['ADMIN_LOGIN'], password: ENV['ADMIN_PASSWORD']

  def index
    render plain: "Hello!"
  end
end

The http_basic_authenticate_with method takes arguments of the username and password required for access. When we will go to that page and you will type login and password correctly, then your browser will fill the header HTTP_AUTHORIZATION with that login and password. So then you can easily go to our restricted page from constraint block, because our AdminConstraint will return true.

Ensuring security

Finally, remember to use HTTPS as base64 is visible on the network. To protect against brute-force attacks, consider setting up your firewall or rack_attack for security to ban a user after a few failed attempts to call /panel_admin. Of course if possible, please use standard token auth authorization for your restricted controllers.

Happy restricting your routes!