DEV Community

Victor Magarlamov
Victor Magarlamov

Posted on

HTTP [Basic and Token] authentication

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

Top comments (0)