ruby-on-railsjwtauthorizationrails-apiruby-on-rails-7

decoded jwt token not accessible from Rails API controller method


I have a Rails API using JWT for login. login works with the token generated and stored in local storage. However, having trouble authorizing controller actions based on decoding the token.

authorize_request is used as a before action in various controllers.

here is the authorize_request action as defined in the application_controller:

class ApplicationController < ActionController::API

  def not_found
    render json: { error: 'not_found' }
  end

  def authorize_request
    header = request.headers['Authorization']
    header = header.split(' ').last if header
    puts "header is #{header}"
    begin
      @decoded = JsonWebToken.decode(header)
      puts "decoded header is #{@decoded}"
      @current_user = User.find(@decoded[:user_id])
      puts "current_user is #{@current_user}"
    rescue ActiveRecord::RecordNotFound => e
      render json: { errors: e.message }, status: :unauthorized
    rescue JWT::DecodeError => e
      render json: { errors: e.message }, status: :unauthorized
    end
  end
end

Here is the JsonWebToken class decode method:

class JsonWebToken
  SECRET_KEY = Rails.application.secrets.secret_key_base

  def self.encode(payload, exp = 24.hours.from_now)
    payload[:exp] = exp.to_i
    JWT.encode(payload, SECRET_KEY)
  end

  def self.decode(token)
    puts("decoding token: #{token}")
    decoded = JWT.decode(token, SECRET_KEY)
    puts("decoded is #{decoded}")
    HashWithIndifferentAccess.new decoded
  end
end

based on the puts stmts included, I can see that the token is decoded in the JWT class, but it is still not accessible in the controller:

header is eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNjZiZmQyMDAtZDExMy00NjdlLTkwMmEtMDRjMmI5YjE2ZmUwIiwiZXhwIjoxNjgwNzAyMzExfQ.x95ikz4QeWAdgm2rak_L6eUOqhILsRIepvALGieAwx8
decoding token: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNjZiZmQyMDAtZDExMy00NjdlLTkwMmEtMDRjMmI5YjE2ZmUwIiwiZXhwIjoxNjgwNzAyMzExfQ.x95ikz4QeWAdgm2rak_L6eUOqhILsRIepvALGieAwx8
decoded is [{"user_id"=>"66bfd200-d113-467e-902a-04c2b9b16fe0", "exp"=>1680702311}, {"alg"=>"HS256"}]
decoded header is {}

Solution

  • JWT.decode(token, SECRET_KEY) is returning an array, not a hash.

    Passing an array to HashWithIndifferentAccess.new is going to return an empty hash, which is what you are returning to the controller

    Just find the array element with user_id and return that to the controller.