Rails basic API authentication with Sorcery and JWT
Share on twitter
Share on facebook
Share on linkedin
Share on tumblr

Rails basic API authentication with Sorcery and JWT

APIs have become more and more popular due to increasing demand for development of mobile and single page apps, and so the need of sessionless server-side applications, being those that are token-based the most popular ones now days due to their relative easy implementation. Here you have a great example that uses JWT to secure a microservice-based application.
JWT (or jot) stands for JSON Web Token and as it states it is a web token structured in a JSON format. JWTs are composed of 3 parts: the header, the payload, and the signature. In short, the header contains the type of token and the algorithm for the cryptographic signing; the payload (or claim) contains the information wanted to securely send to the server, and the signature which contains the secret key known only by the server and that is used to decrypt the data contained in the payload. It is not the primary objective of this post to explain in detail what JWT is, so if you want to know more about it you can do it here.
JWTs are used in the next way:

  1. The API server generates the JWT.
  2. The JWT is sent to the client.
  3. The client saves the JWT (i.e. in LocalStorage).
  4. The client sends the JWT back –we’ll send it on a request’s header– to the server.
  5. The server uses the JWT’s payload data for X or Y purpose. In our case to identify the user that is performing the request.

 
Besides JWT, we need an authentication strategy to actually identify users. We’ll use Sorcery for this purpose; I recommend you to go ahead and read the documentation if you haven’t done so. You can implement any other authentication library –or build your own– but for this particular case I personally like Sorcery because it offers all basic authentication features and it is not as overkilling as other libraries.
This is how the general authentication flow looks like:
jwt-diagram

Fig. 1 JWT Flow

Conveniently there is a jwt library that we can use to generate user’s token, so go ahead, read its documentation and install it in your project.
Next, we’ll create a service to provide the JWT, another one to validate it, and a last one to authenticate the user.
app/services/jwt/token_provider.rb

module Jwt::TokenProvider
  extend self
  def call(payload)
    issue_token(payload)
  end
  private
  def issue_token(payload)
    JWT.encode(payload, Rails.application.secrets.secret_key_base)
  end
end

app/services/jwt/token_decriptor.rb

module Jwt::TokenDecryptor
  extend self
  def call(token)
    decrypt(token)
  end
  private
  def decrypt(token)
    begin
      JWT.decode(token, Rails.application.secrets.secret_key_base)
    rescue
       raise InvalidTokenError
    end
  end
end
class InvalidTokenError < StandardError; end;

app/services/jwt/user_authenticator.rb

module Jwt::UserAuthenticator
  extend self
  def call(request_headers)
    @request_headers = request_headers
    begin
      payload, header = Jwt::TokenDecryptor.(token)
      return User.find(payload['user_id'])
    rescue => e
      # log error here
      return nil
    end
  end
  def token
    @request_headers['Authorization'].split(' ').last
  end
end

NOTE: In this case we are assuming that JWT is in ‘Authorization’ header with format ‘Bearer xxxx.yyyy.zzzz’, that’s why we are splitting `@request_headers[‘Authorization’]`.

Implementing our services

We need to generate the JWT after user has successfully logged in:

class SessionController < ApplicationController
  def create
    user = User.authenticate(params[:email], params[:password]) # This method comes from Sorcery
    if user
      token = Jwt::TokenProvider.(user_id: user.id)
      render json: {user: user, token: token} #  As an extra security metric, you might want to make sure you don’t send user’s encrypted password in `user: user` ;)
    else
      render json: {error: 'Error description'}, status: 422
    end
  end
end

Or signed up:

class UsersController < ApplicationController
  def create
    user = User.new(user_params)
    if user.save
      token = Jwt::TokenProvider.(user_id: user.id)
      render json: {user: user, token: token}
    else
      render json: {error: 'Error description'}, status: 422
    end
  end
end

The token returned in the response should be properly saved in the client so it is sent back to the server in subsequent requests’ headers.
Then we need to authenticate user on future requests. Adding an authentication method on ApplicationController will let us use it on ApplicationController’s children controllers to protect our private endpoints:

class ApplicationController < ActionController::Base
  def authenticate
    render json: {errors: 'Unauthorized'}, status: 401 unless current_user
  end
  def current_user
    @current_user ||= Jwt::UserAuthenticator.(request.headers)
  end
end
class AnotherController < ApplicationController
  before_filter :authenticate
end

And that’s it, our API is now secured using a sessionless approach. From this point on you can add more complexity using jwt and Sorcery‘s methods to, for instance, make token expire after 1 hour or reset user’s password.
Let me know in the comments section below what you think about this strategy.

Share on twitter
Share on facebook
Share on linkedin
Share on tumblr