loading...

HTTP [Basic and Token] authentication

victormagarlamov profile image Victor Magarlamov ・2 min read

In this post I show you how to implement HTTP-authentication between the client (JavaScript) and the server (RubyOnRails).

Bit of theory

  1. When an unauthenticated client sends request to the protected resource, the server responses with an 401 Unauthorized HTTP status and adds a WWW-Authenticate header, which contains the authentication scheme and parameters.
  2. When a client sends a request with an Authorization header, the server checks the credentials in this header and response with an 200 OK or with an 403 Forbidden HTTP status.

Bit of practice

First, we need two models: User и AuthToken.

class User < ApplicationRecord
  has_secure_password
  has_many :auth_tokens
end

class AuthToken < ApplicationRecord
  belongs_to :user

  before_create :set_value

  def set_value
    self.value = SecureRandom.hex(32)
  end
end

Authenticate with HTTP Basic

There are some useful modules for HTTP Authentication in RubyOnRails. Lets add one of them to our controller.

class ApplicationController < ActionController::API
  include ActionController::HttpAuthentication::Basic::ControllerMethods
end

Then we create a controller for user authentication. After receiving the POST-request, it checks user credentials. If successful, it sends the token to the user. A little later we do the Token authentication using the one.

class AuthenticationsController < AplicationController
  def create
    authenticate_with_http_basic do |login, password|
      user = User.find_by_login(login)

      if user&.authenticate(password)
        token = user.auth_tokens.create!
        render json: { token: token.value } and return
      end
    end

    render status: :unauthorized
  end
end

Lets create a JavaScript method which helps us get this token.

function authenticate(login, password) {
  const credentials = window.btoa(`${login}:${password}`);
  const headers = {};

  headers[‘Authorization’] = `Basic ${credentials}`;

  return fetch(‘/authentications’,  {method: ‘post’, headers} ).then(...)
}

Please note how we send the credentials. There is a rule for string formatting:

scheme + space + login + ‘:’ + password

And all is encoded by base64, except the name of the scheme.

Authenticate with HTTP Token

Well we successfully passed Basic authentication and got the token. Lets add Token authentication.

class ApplicationController < ActionController::API
  include ActionController::HttpAuthentication::Basic::ControllerMethods
  include ActionController::HttpAuthentication::Token::ControllerMethods

  attr_reader :current_user

  private

  def authenticate_by_token
    authenticate_or_request_with_http_token do |http_token|
      token = AuthToken.find_by_value(http_token)
      @current_user = token.user if token
  end
end

And apply it to a controller.

class UsersController < AplicationController
   before_action :authenticate_by_token

  def create
    ...
  end
end

Now to get access to the protected resource, just add the Authorization header.

headers[‘Authorization’] = `Token ${token}`;

Links
https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication
https://api.rubyonrails.org/files/actionpack/lib/action_controller/metal/http_authentication_rb.html

Discussion

markdown guide