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)