sandesh b nataraj
sandesh b nataraj

Reputation: 5

How to fix the problem, When I try authentication on local system its working perfectly, but when uploaded to heroku it comes back with error 500?

I am new to rails and react, this might be a simple one but i cant seem to figure it out.

I am trying to implement a simple jwt authentication using ruby on rails with react as client. I followed the steps that was suggested in : https://www.pluralsight.com/guides/token-based-authentication-with-ruby-on-rails-5-api

It works as expected on my local system but when i uploaded my app on to heroku it always comes back with error : 500. All the other 'Post' and 'Get' requests work normally. Its only when i try to authenticate and get the auth_token back it runs into 500 error.

this is the request format post: localhost:3001/api/authenticate

and body:

{
    "email": "[email protected]",
    "password": "evin"
}

I verified that this data is available on heroku by using get which works perfectly.

I have been working on resolving this for over 2 days now. There is very little information available online on this authentication. There was plenty of recommendations on using auth0. But i could not find much help with this form of authentication.

This is what i have

#Path: /app/controllers/application_controller.rb

class ApplicationController < ActionController::API
  before_action :authenticate_request
  attr_reader :current_user

  private

  def authenticate_request
    @current_user = AuthorizeApiRequest.call(request.headers).result
    render json: { error: 'Not Authorized' }, status: 401 unless @current_user
  end
end


#Path: app/controllers/api/authentication_controller.rb

class Api::AuthenticationController < ApplicationController
 skip_before_action :authenticate_request

 def authenticate
   command = AuthenticateUser.call(params[:email], params[:password])

   if command.success?
     render json: { auth_token: command.result }
   else
     render json: { error: command.errors }, status: :unauthorized
   end
 end
end



#Path: /app/commands/authenticate_user.rb

class AuthenticateUser
  prepend SimpleCommand

  def initialize(email, password)
    @email = email
    @password = password
  end

  def call
    JsonWebToken.encode(user_id: user.id) if user
  end

  private

  attr_accessor :email, :password

  def user
    user = User.find_by_email(email)
    return user if user && user.authenticate(password)

    errors.add :user_authentication, 'invalid credentials'
    nil
  end
end


#Path:  /app/commands/authorize_api_request.rb

class AuthorizeApiRequest
  prepend SimpleCommand

  def initialize(headers = {})
    @headers = headers
  end

  def call
    user
  end

  private

  attr_reader :headers

  def user
    @user ||= User.find(decoded_auth_token[:user_id]) if decoded_auth_token
    @user || errors.add(:token, 'Invalid token') && nil
  end

  def decoded_auth_token
    @decoded_auth_token ||= JsonWebToken.decode(http_auth_header)
  end

  def http_auth_header
    if headers['Authorization'].present?
      return headers['Authorization'].split(' ').last
    else
      errors.add(:token, 'Missing token')
    end
    nil
  end
end




#Path: /lib/json_web_token.rb

class JsonWebToken
 class << self
   def encode(payload, exp = 24.hours.from_now)
     payload[:exp] = exp.to_i
     JWT.encode(payload, Rails.application.secrets.secret_key_base)
   end

   def decode(token)
     body = JWT.decode(token, Rails.application.secrets.secret_key_base)[0]
     HashWithIndifferentAccess.new body
   rescue
     nil
   end
 end
end





#path: /config/application.rb

require_relative 'boot'

require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "active_storage/engine"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "action_cable/engine"
# require "sprockets/railtie"
require "rails/test_unit/railtie"

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module Deveycon
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 5.2

    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration can go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded after loading
    # the framework and any gems in your application.

    #Autoload lib for encrypt and decrypt
    config.autoload_paths << Rails.root.join('lib')

    # Only loads a smaller set of middleware suitable for API only apps.
    # Middleware like session, flash, cookies can be added back manually.
    # Skip views, helpers and assets when generating a new resource.
    config.api_only = true
  end
end

Upvotes: 0

Views: 1866

Answers (3)

Deepak Kumar
Deepak Kumar

Reputation: 21

In #lib/JsonWebToken: Just increase the exp time of token and replace .secrets.secret_key_base with .credentials.read

class JsonWebToken
  class << self
    def encode(payload, exp = 1200.hours.from_now)
      payload[:exp] = exp.to_i
      JWT.encode(payload, Rails.application.credentials.read)
    end

    def decode(token)
      body = JWT.decode(token, Rails.application.credentials.read)[0]
      HashWithIndifferentAccess.new body
    rescue
    nil
    end
  end
end

Upvotes: 2

Acushla
Acushla

Reputation: 89

I had similar issues, the API works perfectly on localhost after uploading to Heroku, I still got unauthorized on secure pages even with the token on the headers. I added

production:
   secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

to config/secrets.yml

Upvotes: 5

Piyush Awasthi
Piyush Awasthi

Reputation: 378

Please check the more details log of your heroku application by using Heroku CLI.

heroku logs -t

If the problem with AuthenticateUser::JsonWebToken use auto loaded in your

config/application.rb

class Application < Rails::Application
    #.....
    config.autoload_paths << Rails.root.join('lib')
    #.....
end

I hope that helpful to resolve your issue.

Upvotes: 2

Related Questions