waiting-for-dev / warden-jwt_auth

JWT token authentication with warden
MIT License
113 stars 56 forks source link

Rack env `warden.user.<scope>.key` not set #37

Closed redox closed 2 years ago

redox commented 2 years ago

Expected behavior

I'm playing with Rack::Attack and would love the rate-limits to be different for logged in requests. While this is working just fine with regular devise/cookie login (I can fetch the warden.user.<scope>.key from request.env['rack.session'] to identify the user), I just cannot make it work with JWT tokens. Meaning I don't have the warden.user.<scope>.key set and request.env['warden'].winning_strategy keeps being nil.

However, I can see the "warden-jwt_auth.revocation_manager"=>true and "warden-jwt_auth.token_dispatcher"=>true debug attributes being added to my Rack env, so I'm really not sure what's going on.

Actual behavior

Isn't warden-jwt_auth supposed to add such warden.user.<scope>.key key?

Debugging information

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

0.6.0

#<Dry::Configurable::Config values={:secret=>"****hidden*****", :algorithm=>"HS256", :expiration_time=>1 year, :aud_header=>"JWT_AUD", :mappings=>{:user=>User (call 'User.connection' to establish a connection)}, :dispatch_requests=>[["POST", /^\/users\/sign_in$/], ["POST", /^\/users$/]], :revocation_requests=>[["DELETE", /^\/users\/sign_out$/]], :revocation_strategies=>{:user=>User (call 'User.connection' to establish a connection)}}>

Query I'm sending:

curl 'http://localhost:3000/my_action' \
-H 'JWT_AUD: my-app' \
-H 'Authorization: Bearer ****hidden****'

And within the my_action, I confirm the current_user is set accordingly; so the token + aud are valid.

Would love your thoughts :/

waiting-for-dev commented 2 years ago

Hey @redox, off the top of my head, I guess that having request.env['warden'].winning_strategy set to nil means that your users are actually authenticated through the session.

redox commented 2 years ago

Hey @redox, off the top of my head, I guess that having request.env['warden'].winning_strategy set to nil means that your users are actually authenticated through the session.

Thanks for the swift answer @waiting-for-dev. Not sure I'm following here, are you saying it's expected to get nil there? Do you know where/when warden.user.<scope>.key is set?

waiting-for-dev commented 2 years ago

Not sure I'm following here, are you saying it's expected to get nil there?

Warden won't go through the cascade of strategies if it can take the user from the session. But I don't know if, in that case, the winning_strategy will be nil or not set.

Do you know where/when warden.user..key is set?

https://github.com/wardencommunity/warden/blob/3f197cee0511c9b3c940204bab706407170d9611/lib/warden/session_serializer.rb#L12

redox commented 2 years ago

I wrongly understood what warden-jwt_auth.token_dispatcher was doing; I thought this would manipulate the request, but it's rather the response.

And so indeed, the warden.user.<scope>.key key is stored in the session, which isn't the case when authenticated with JWT tokens.

Ultimately, I ended up implementing some logic within my Rack::Attack:

    def self.session_user(request)
      request.env['rack.session'].dig('warden.user.user.key', 0, 0)
    end

    def self.jwt_user(request)
      token = Warden::JWTAuth::HeaderParser.from_env(request.env)
      return nil unless token

      payload = Warden::JWTAuth::TokenDecoder.new.call(token)
      return nil unless Warden::JWTAuth::PayloadUserHelper.scope_matches?(payload, :user)

      aud = Warden::JWTAuth::EnvHelper.aud_header(request.env)
      return nil unless Warden::JWTAuth::PayloadUserHelper.aud_matches?(payload, aud)

      payload['sub']
    rescue JWT::DecodeError
      nil
    end

and rely on those to understand whether a request is authenticated (acknowledging it won't check whether the JWT token has been invalidated within the DB).

Thank you for your help @waiting-for-dev