Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return JWT token when user signs in rails

Having an issue with getting JWT token using devise gem and devise-jwt gem. This is how my confirmation looks like.

devise.rb

  Devise.setup do |config|

    config.jwt do |jwt|
      jwt.secret =  SECRETS.devise_jwt_secret_key
      jwt.dispatch_requests = [ ['POST', %r{^/authentication_tokens/create$}] ]
    end
end 

user.rb

class User < ApplicationRecord

    devise :database_authenticatable, :registerable,
           :recoverable, :rememberable, :trackable, :validatable,
           :jwt_authenticatable, jwt_revocation_strategy: Devise::JWT::RevocationStrategies::Null

  end

authentication_tokens_controller.rb

class Api::V1::AuthenticationTokensController < Devise::SessionsController
  include Devise::Controllers::Helpers
  skip_before_action :verify_authenticity_token 

  prepend_before_action :require_no_authentication, only: [:create]

  before_action :rewrite_param_names, only: [:create]

  def new
    render json: { response: "Authentication required" }, status: 401
  end

  def create
    self.resource = warden.authenticate!(auth_options)
    sign_in(resource_name, resource)
    yield resource if block_given?
     render json: {success: true, jwt: current_token, response: "Authentication successful" }
  end

  private

  def rewrite_param_names
    request.params[:user] = {email: request.params[:email], password: request.params[:password]}
  end

  def current_token
    request.env['warden-jwt_auth.token']
  end

end

routes.rb

   get 'home#secret'
   devise_for :users
   resources :tasks
   #other routes for the website removed for brevity

  namespace :api, defaults: { format: :json } do
    namespace :v1 do
      resources :users
      devise_scope :user do
        post '/authentication_tokens/create', to: "authentication_tokens#create"
      end
    end
  end

For some reason request.env['warden-jwt_auth.token'] returns null all the time, however, the user is authenticated. Is there anything that I need to add to get the JWT token when a user signs in?

Update - routes and namespacing

After days of debugging, I believe I have found the source of my problem. My app has a frontend which uses normal routes. The code above doesn't work however if I do something like the code below. All is good.

  scope :api, defaults: {format: :json} do
    devise_for :users, controllers: {sessions: 'v1/authentication_tokens'}
  end

Is there a way of namespacing the devise_for for me API even though it has been used above for the website?

like image 911
Benjamin Avatar asked Jun 28 '18 07:06

Benjamin


2 Answers

You just need to make your route RESTful.

routes.rb

post '/authentication_tokens', to: "authentication_tokens#create"

devise.rb

config.jwt do |jwt|
  jwt.secret =  SECRETS.devise_jwt_secret_key
  jwt.dispatch_requests = [ ['POST', %r{^/authentication_tokens$}] ]
end
like image 51
Alex Tatarnikov Avatar answered Oct 19 '22 01:10

Alex Tatarnikov


I've briefly looked on your issue and, it's probably wrong, but something for you to give a try:

looking on the following lines

def create
  self.resource = warden.authenticate!(auth_options)
end

def current_token
  request.env['warden-jwt_auth.token']
end

If you say that user is being authenticated even with nil returned from current_token method, so that means that jwt is passing correctly, but your way of fetching it is wrong.

Try to debug self.resource = warden.authenticate!(auth_options) line and see what contains inside auth_options, probably you can take JWT from there, or you just trying to get warden-jwt_auth.token in a wrong way. Try to debug this line as well and see if you should probably take "warden-jwt_auth.token" from request.headers["warden-jwt_auth.token"], or something like this. Just print out the whole response of your request and search by needed header.

I hope this helps!

like image 32
Michail Belousov Avatar answered Oct 18 '22 23:10

Michail Belousov