waiting-for-dev / devise-jwt

JWT token authentication with devise and rails

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

rails autoloading devise models after changes

couchbelag opened this issue · comments

Expected behavior

Changes to devise models (e.g. User) while rails server is running should take effect immediately without raising an exception.

Actual behavior

Using devise's current_user (e.g. in assignments) after User model has been changed, an ActiveRecord::AssociationTypeMismatch is raised in rails development environment. We were able to narrow it down to JWTAuth.config.mappings still referencing the 'old' version of User before changed. Hence current_user is initialized by the auth strategy with this obsolete version of User. Any assignment to (correctly reloaded) models will then raise the exception because ActiveRecord::Associations::Association::raise_on_type_mismatch() will detect that expected class and actual object's class do not match (object_id differs).

Steps to Reproduce the Problem

  1. start rails server (assuming devise scope is User)
  2. request a resource that requires authentication
  3. response is OK
  4. change User model implementation
  5. request same resource again
  6. ActiveRecord::AssociationTypeMismatch is raised
  7. restart rails server
  8. request same resource again
  9. response is OK

Debugging information

Some Background information: We're implementing a rails application using devise & oauth2 for authentication of the web interface and JWT authentication for REST API. Web interface works correctly as no JWT auth strategy is involved, only the REST API shows the actual behavior and only in development environment settings.

  • Version of devise-jwt is 0.7.0
  • Version of rails is 5.2.5
  • Version of warden-jwt_auth is 0.5.0
  • Output of Warden::JWTAuth.config
Warden::JWTAuth.config = {
    :secret=>";-P", 
    :algorithm=>"HS256", 
    :expiration_time=>315360000, 
    :aud_header=>"JWT_AUD", 
    :mappings=>{
        :user=>User(id: integer, first_name: string, last_name: string, jwt_token: string, jti: string, ...)
    },
    :dispatch_requests=>[
        [
            "POST", 
            /^\/users\/sign_in$/
        ], 
        [
            "POST", 
            /^\/users$/
        ]
    ], 
    :revocation_requests=>[
        [
            "DELETE", 
            /^\/users\/sign_out$/
        ]
    ], 
    :revocation_strategies=>{
        :user=>User(id: integer, first_name: string, last_name: string, jwt_token: string, jti: string, ...)
    }
}
  • Output of Devise.mappings
Devise.mappings = {
    :user=>#<Devise::Mapping:0x0000560d31de31b0 @scoped_path="users",
    @singular=:user,
    @class_name="User",
    @klass=#<Devise::Getter:0x0000560d31de2508 @name="User">,
    @path="users",
    @path_prefix=nil,
    @sign_out_via=:delete,
    @format=nil,
    @router_name=nil,
    @failure_app=Devise::FailureApp,
    @controllers={
        :omniauth_callbacks=>"users/omniauth_callbacks",
        :sessions=>"devise/sessions",
        :passwords=>"devise/passwords",
        :confirmations=>"devise/confirmations",
        :unlocks=>"devise/unlocks"
    },
    @path_names={
        :registration=>"",
        :new=>"new",
        :edit=>"edit",
        :sign_in=>"sign_in",
        :sign_out=>"sign_out",
        :password=>"password",
        :confirmation=>"confirmation",
        :unlock=>"unlock"
    },
    @modules=[
        :database_authenticatable,
        :rememberable,
        :omniauthable,
        :recoverable,
        :registerable,
        :validatable,
        :confirmable,
        :lockable,
        :trackable,
        :jwt_authenticatable
    ],
    @routes=[
        :session,
        :omniauth_callback,
        :password,
        :registration,
        :confirmation,
        :unlock
    ],
    @used_routes=[
        :session,
        :omniauth_callback,
        :password,
        :confirmation,
        :unlock
    ],
    @used_helpers=[
        :session,
        :omniauth_callback,
        :password,
        :registration,
        :confirmation,
        :unlock
    ]
}

It should be fixed on version v0.8.1. Please, update it and report if it works for you.

Thanks for the feedback! I tried with master (is there some pre-release for 0.8.1?) and yes, it works as expected. And of course I want to thank you for this excellent gem!

It's already released: https://rubygems.org/gems/devise-jwt/versions/0.8.1

Happy it works and thanks for the nice words!