In this post I show you how to implement HTTP-authentication between the client (JavaScript
) and the server (RubyOnRails
).
Bit of theory
- When an unauthenticated client sends request to the protected resource, the server responses with an
401 Unauthorized
HTTP status and adds aWWW-Authenticate
header, which contains the authentication scheme and parameters. - When a client sends a request with an
Authorization
header, the server checks the credentials in this header and response with an200 OK
or with an403 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)