HTTP [Basic and Token] authentication
Tags: rails, javascript, authentication
Published: 2020-05-17 20:25:32

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.

~~~rb
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.

~~~rb
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.

~~~rb
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.

```rb
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.

```rb
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