waiting-for-dev / devise-jwt

JWT token authentication with devise and rails

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Question] How do I properly support both no-format and .json in url?

mein-beer-hu opened this issue · comments

commented

Hello,
I am using devise + devise_jwt
I am having trouble understanding the right way to support both the formats in url. I did read through the section of "supporting
request_formats here: https://github.com/waiting-for-dev/devise-jwt#request_formats

I have the following setup in my devise.rb (initializer folder)

  config.jwt do |jwt|
    jwt.secret =  ENV['DEVISE_JWT_SECRET_KEY']
    jwt.dispatch_requests = [
      ['POST', %r{^/login$}]
    ]
    jwt.revocation_requests = [
      ['DELETE', %r{^/logout$}]
    ]
    jwt.request_formats = {
      user: [nil, :json]  # Is this correct?
    }

    jwt.expiration_time = 5.day.to_i
  end

Expected behavior

Support .json in url as well as no formats

Actual behavior

I am going to pick one URL to help keep this scoped:

When I hit logout.json here is what happens:

  ↳ app/controllers/api/people/sessions_controller.rb:68:in `respond_to_on_destroy'
  TRANSACTION (0.2ms)  BEGIN
  ↳ app/controllers/api/people/sessions_controller.rb:68:in `respond_to_on_destroy'
  User Update (2.7ms)  UPDATE "users" SET "sign_in_count" = $1, "current_sign_in_at" = $2, "last_sign_in_at" = $3, "updated_at" = $4 WHERE "users"."id" = $5  [["sign_in_count", 266], ["current_sign_in_at", "2022-02-12 00:15:19.952937"], ["last_sign_in_at", "2022-02-12 00:15:03.064516"], ["updated_at", "2022-02-12 00:15:19.953450"], ["id", "df2a6186-a671-45f3-8846-00910efedcc2"]]
  ↳ app/controllers/api/people/sessions_controller.rb:68:in `respond_to_on_destroy'
  TRANSACTION (42.0ms)  COMMIT
  ↳ app/controllers/api/people/sessions_controller.rb:68:in `respond_to_on_destroy'
Filter chain halted as :verify_signed_out_user rendered or redirected
Completed 200 OK in 120ms (Views: 0.2ms | ActiveRecord: 55.2ms | Allocations: 26385)

Please observe there is no updates to jwt_denylist table.

Now, if I try logout (no .json in url)

It properly updates the jwt_denylist

*** Above output + and update to jwt_denylist ***

Filter chain halted as :verify_signed_out_user rendered or redirected
Completed 200 OK in 31ms (Views: 0.3ms | ActiveRecord: 11.7ms | Allocations: 5278)


  User Load (0.8ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", "df2a6186-a671-45f3-8846-00910efedcc2"], ["LIMIT", 1]]
  JwtDenylist Load (0.4ms)  SELECT "jwt_denylist".* FROM "jwt_denylist" WHERE "jwt_denylist"."jti" = $1 AND "jwt_denylist"."exp" = $2 LIMIT $3  [["jti", "210d1632-c814-4b98-af7d-9fe3772761b5"], ["exp", "2022-02-17 00:15:03"], ["LIMIT", 1]]
  TRANSACTION (0.5ms)  BEGIN
  JwtDenylist Create (8.6ms)  INSERT INTO "jwt_denylist" ("jti", "exp") VALUES ($1, $2) RETURNING "id"  [["jti", "210d1632-c814-4b98-af7d-9fe3772761b5"], ["exp", "2022-02-17 00:15:03"]]
  TRANSACTION (107.6ms)  COMMIT

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.9.0
  • Version of rails in use: 6.1
  • Version of warden-jwt_auth in use
    devise-jwt (0.9.0)
      devise (~> 4.0)
      warden-jwt_auth (~> 0.6)
  • Output of Devise::JWT.config
#<Dry::Configurable::Config values={:secret=>"718fc06a601ec32111a2e9f48ab9b6ed6b99ca39785e77e4740dc12db5f350c7efa30c661adda4ae00b16e4d261beccd6d84a87c5192b77f3f4abc1a9af34f0b", :expiration_time=>432000, :dispatch_requests=>[["POST", /^\/login$/], ["POST", /^\/users\/sign_in$/], ["POST", /^\/users\/sign_in.json$/], ["POST", /^\/users$/], ["POST", /^\/users.json$/]], :revocation_requests=>[["DELETE", /^\/logout$/], ["DELETE", /^\/users\/sign_out$/], ["DELETE", /^\/users\/sign_out.json$/]], :aud_header=>"JWT_AUD", :request_formats=>{:user=>[nil, :json]}}>
  • Output of Warden::JWTAuth.config
#<Dry::Configurable::Config values={:secret=>"718fc06a601ec32111a2e9f48ab9b6ed6b99ca39785e77e4740dc12db5f350c7efa30c661adda4ae00b16e4d261beccd6d84a87c5192b77f3f4abc1a9af34f0b", :algorithm=>"HS256", :expiration_time=>432000, :aud_header=>"JWT_AUD", :mappings=>{:user=>User (call 'User.connection' to establish a connection)}, :dispatch_requests=>[["POST", /^\/login$/], ["POST", /^\/users\/sign_in$/], ["POST", /^\/users\/sign_in.json$/], ["POST", /^\/users$/], ["POST", /^\/users.json$/]], :revocation_requests=>[["DELETE", /^\/logout$/], ["DELETE", /^\/users\/sign_out$/], ["DELETE", /^\/users\/sign_out.json$/]], :revocation_strategies=>{:user=>JwtDenylist (call 'JwtDenylist.connection' to establish a connection)}}>
  • Output of Devise.mappings
{:user=>#<Devise::Mapping:0x00007fc6026610a0 @scoped_path="users", @singular=:user, @class_name="User", @klass=#<Devise::Getter:0x00007fc602660970 @name="User">, @path="users", @path_prefix=nil, @sign_out_via=:delete, @format=nil, @router_name=nil, @failure_app=Devise::FailureApp, @controllers={:confirmations=>"user_confirmations", :passwords=>"passwords", :invitations=>"invitations", :registrations=>"devise_invitable/registrations", :sessions=>"devise/sessions"}, @path_names={:registration=>"", :new=>"new", :edit=>"edit", :sign_in=>"sign_in", :sign_out=>"sign_out", :password=>"password", :sign_up=>"sign_up", :cancel=>"cancel", :confirmation=>"confirmation", :invitation=>"invitation", :accept=>"accept", :remove=>"remove"}, @modules=[:database_authenticatable, :rememberable, :recoverable, :registerable, :validatable, :confirmable, :trackable, :jwt_authenticatable, :invitable], @routes=[:session, :password, :registration, :confirmation, :invitation], @used_routes=[:session, :password, :registration, :confirmation, :invitation], @used_helpers=[:session, :password, :registration, :confirmation, :invitation]>} 
  • If your issue is related with not getting a JWT from the server:
    • Involved request path, method and request headers
    • Response headers for that request
  • If your issue is related with not being able to revoke a JWT:
    • Involved request path, method and request headers
      (have included these in above example)

Your configuration seems correct in that regard, make sure devise is well set up for an API: https://github.com/waiting-for-dev/devise-jwt/wiki/Configuring-devise-for-APIs

commented

One more difference from there is

  devise_for :users, controllers: {
    confirmations: :user_confirmations,
    passwords: :passwords,
    invitations: :invitations,
  }

  devise_scope :user do
    post 'login' => 'api/people/sessions#create', :as => :login
    delete 'logout' => 'api/people/sessions#destroy', :as => :logout
    # post 'register' => 'api/people/registrations#create', :as => :registers
    delete 'delete_account' => 'api/people/registrations#destroy', :as => :delete_account
  end

This is how my routes look like. Maybe I should add { defaults: :json }

Did you go through all the guides I linked before? One common issue is overriding controllers beyond making them recognizable by Devise.

commented

Yes, I think I also observed that its only the logout path thats partially failing when invoked without .json. For rest of the controllers, with/without json in URL is not causing any issues

Closing it, as I don't think there's anything we can fix on our end. Please, feel free to get back if you have any new information.