waiting-for-dev / devise-jwt

JWT token authentication with devise and rails

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

current_user nil after authentication

Amystherdam opened this issue · comments

Expected behavior

That the current_user is filled with the logged in user

Actual behavior

When the user logs in and the request is going through one of Devise's default controllers, the current_user is populated, but when the user is already logged in, the current_user is lost and becomes null

Even in application_controller current_user is already null

Settings made

config/initializers/devise_jwt.rb

# https://github.com/waiting-for-dev/devise-jwt/pull/23
# https://github.com/plataformatec/devise/issues/4584

module Devise
  module JWT
    module WardenStrategy
      def authenticate!
        super
        env["devise.skip_trackable".freeze] = true if valid?
      end
    end
  end
end

Warden::JWTAuth::Strategy.prepend Devise::JWT::WardenStrategy

config/initializers/devise.rb

  config.jwt do |jwt|
    jwt.secret = "testtesttesttest"
    jwt.request_formats = { user: [:json] }
    jwt.expiration_time = 30.minutes.to_i
    jwt.dispatch_requests = [
      ['POST', %r{^/users/sign_in.json$}]
    ]
    jwt.revocation_requests = [
      ['DELETE', %r{^/users/sign_out.json$}]
    ]
  end

app/models/user.rb

class User < ApplicationRecord
  include Devise::JWT::RevocationStrategies::JTIMatcher
  
  devise :database_authenticatable, :registerable, :validatable,
         :jwt_authenticatable, jwt_revocation_strategy: self
end

JTI Migration

class AddJtiToUsers < ActiveRecord::Migration[7.0]
  def change
    add_column :users, :jti, :string, null: false
    add_index :users, :jti, unique: true
  end
end

Debugging information

Provide following information. Please, format pasted output as code. Feel free to remove the secret key value.

  • Version of devise-jwt in use: 0.10.0
  • Version of rails in use: 7.0.4
  • Version of warden-jwt_auth in use: 0.7.0
  • Output of Devise::JWT.config
#<Dry::Configurable::Config values={
  :secret=>"lkjahdfklajshdfkasjldhfklasdhjf",
  :request_formats=>{:user=>[nil, :json]},
  :expiration_time=>1800,
  :dispatch_requests=>[["POST", /^\/users\/sign_in.json$/],["POST", /^\/users\/sign_in$/],["POST", /^\/users\/sign_in.json$/],["POST", /^\/users$/], ["POST", /^\/users.json$/]],
  :revocation_requests=>[["DELETE", /^\/users\/sign_out.json$/],["GET", /^\/users\/sign_out$/],["GET", /^\/users\/sign_out.json$/]],
  :decoding_secret=>"lkjahdfklajshdfkasjldhfklasdhjf",
  :algorithm=>"HS256",
  :aud_header=>"JWT_AUD"
}>
  • Output of Warden::JWTAuth.config
#<Dry::Configurable::Config values={
  :secret=>"lkjahdfklajshdfkasjldhfklasdhjf",
  :expiration_time=>1800,
  :dispatch_requests=>[["POST", /^\/users\/sign_in.json$/],["POST", /^\/users\/sign_in$/],["POST", /^\/users\/sign_in.json$/],["POST", /^\/users$/],["POST", /^\/users.json$/]],
  :revocation_requests=>[["DELETE", /^\/users\/sign_out.json$/],["GET", /^\/users\/sign_out$/],["GET", /^\/users\/sign_out.json$/]],
  :aud_header=>"JWT_AUD",
  :mappings=>{:user=>User(id: integer, email: string, encrypted_password: string, reset_password_token: string, reset_password_sent_at: datetime, remember_created_at: datetime, created_at: datetime, updated_at: datetime, jti: string, name: string, social_id_number: string, phone: string)},
  :revocation_strategies=>{:user=>User(id: integer, email: string, encrypted_password: string, reset_password_token: string, reset_password_sent_at: datetime, remember_created_at: datetime, created_at: datetime, updated_at: datetime, jti: string, name: string, social_id_number: string, phone: string)},
  :algorithm=>"HS256",
  :decoding_secret=>"lkjahdfklajshdfkasjldhfklasdhjf"
}>
  • Output of Devise.mappings
{:user=>
  #<Devise::Mapping:0x0000000109dfe648
   @class_name="User",
   @controllers={:sessions=>"devise/sessions", :registrations=>"devise/registrations"},
   @failure_app=Devise::FailureApp,
   @format=nil,
   @klass=#<Devise::Getter:0x0000000109dfe080 @name="User">,
   @modules=[:database_authenticatable, :registerable, :validatable, :jwt_authenticatable],
   @path="users",
   @path_names={:registration=>"", :new=>"new", :edit=>"edit", :sign_in=>"sign_in", :sign_out=>"sign_out", :sign_up=>"sign_up", :cancel=>"cancel"},
   @path_prefix=nil,
   @router_name=nil,
   @routes=[:session, :registration],
   @scoped_path="users",
   @sign_out_via=:get,
   @singular=:user,
   @used_helpers=[:session, :registration],
   @used_routes=[:session, :registration]>}

Observation

I'm not using rack-cors, the app is a monolith

Putting this in application_controller or graphql_controller if using graphql should work, assuming your GraphqlApiService file or whatever frontend file is sending the bearer token in the header of each request

  def current_user
    return unless request.headers['Authorization'].present?
    token = request.headers['Authorization'].split(" ").last
    warden.authenticate!(auth_token: token)
  end