waiting-for-dev/devise-jwt

Signature verification raised

Mei152 opened this issue · 6 comments

Hello,

I realize this is not a help forum, but I have exhausted all efforts to figure out if my issue is caused by my own ignorance.

I am trying to set up this gem for two models, user, and api_user. user will use regular Devise authentication, and api_user will use jwt with the whitelist strategy. I have the log in and log out for api_user working. The issue is that a get request for another resource results in "error": "Signature verification raised". If I add an extra character to the token in the request header, it will change the error to "error": "Invalid segment encoding", so at least I know the token is making it to decoding.

Judging from a previous issue like this in this repo, I am guessing you would want the following info:

irb(main):002:0> Warden::JWTAuth.config
=> #<#<Class:0x0000562b9d392ff0>:0x0000562b9d392e10 
@config={
  :secret=>"86630d90134bb9b5f105409635f61962761f59f2c9f3595c414cbca6ee1a4f47388c728b9fb61a12abe003b8e83cd0c57dd924f92162a005c001dd38480e8dd4",
  :expiration_time=>3600,
  :aud_header=>"JWT_AUD",
  :mappings=>{:api_user=>"ApiUser"},
  :dispatch_requests=>[
    ["POST", /^\/api_users\/sign_in.json$/],
    ["POST", /^\/api_users.json$/]
  ],
  :revocation_requests=>[["DELETE", /^\/api_users\/sign_out.json$/]],
  :revocation_strategies=>{:api_user=>"ApiUser"}
}


irb(main):005:0> Devise::JWT.config
=> #<#<Class:0x0000562b9d3f9c28>:0x0000562b9d3f9a48
@config={
  :secret=>"86630d90134bb9b5f105409635f61962761f59f2c9f3595c414cbca6ee1a4f47388c728b9fb61a12abe003b8e83cd0c57dd924f92162a005c001dd38480e8dd4",
  :expiration_time=>3600,
  :dispatch_requests=>nil,
  :revocation_requests=>nil,
  :aud_header=>nil,
  :request_formats=>{:api_user=>[:json]

Running Warden::JWTAuth::TokenDecoder.new.call(token) on the Devise issued token does fail. However, decoding the token in jwt.io works fine, and the signature does pass verification using the same secret cited in the above configurations.

Here is what the request looks like in insomnia:

* Preparing request to http://localhost:3000/api/v1/things.json
* Using libcurl/7.67.0 OpenSSL/1.1.1d zlib/1.2.11 nghttp2/1.29.0
* Current time is 2019-12-15T01:53:10.268Z
* Disable timeout
* Enable automatic URL encoding
* Enable SSL validation
* Enable cookie sending with jar of 0 cookies
* Hostname in DNS cache was stale, zapped
*   Trying 127.0.0.1:3000...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3000 (#38)

> GET /api/v1/things.json HTTP/1.1
> Host: localhost:3000
> User-Agent: insomnia/7.0.5
> Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwic2NwIjoiYXBpX3VzZXIiLCJhdWQiOm51bGwsImlhdCI6MTU3NjM1MjY3NywiZXhwIjoxNTc2MzU2Mjc3LCJqdGkiOiIwZDUzYjZkYy0xNjBjLTRiNTktYTVmZS0wMDdmMWMyNjExNTYifQ.qjXqEddayVHhj4-ah6bxZW6eCGUZKm5n5jQCZgG41F
> Accept: */*
> Content-Length: 0

* Mark bundle as not supporting multiuse

< HTTP/1.1 401 Unauthorized
< Content-Type: application/json; charset=utf-8
< Cache-Control: no-cache
< X-Request-Id: 6b0a2886-826a-4643-8251-cbb949384ce2
< X-Runtime: 0.024096
< Vary: Origin
< Transfer-Encoding: chunked

* HTTP error before end of send, stop sending



* Received 52 B chunk
* Closing connection 38

If anyone can help point me in the right direction to solve this, that would be great!

Can't say for sure what fixed my issue. I tried many, many things. All I can say is that everything works at the moment. Sorry if I troubled anyone.

No worries. Feel free to post here any doubt you have in regard of devise-jwt usage.

Thank you. Great gem. It solves my use case perfectly!

I have all the functionality for whitelisted strategy working properly but ran into an issue writing tests for edge cases. When a request is sent to an action protected with before_action :authenticate_api_user!, and the token has been tampered with, the application blows up with an JWT::DecodeError. We should be able to handle JWT::DecodeErrors gracefully.

The error is raised at this point at lib/jwt/decode.rb:97

      JWT::JSON.parse(JWT::Base64.url_decode(segment))
    rescue ::JSON::ParserError
      raise JWT::DecodeError, 'Invalid segment encoding'
    end
  end
end

I tried to rescue the error in a logout scenario in the api_users devise sessions controller with the following:

  def destroy
    puts "hello form the destroy action"
    begin
      super
    rescue #JWT::VerificationError, JWT::DecodeError => e
      render json: { errors: ['Not Authenticated'] }, status: :unauthorized
    end
  end

But it does not work. I am not even getting the puts statement in the rails log. A similar puts in the sessions create action does show up in the log.

Here are my routes at this point in the project:

Rails.application.routes.draw do
  devise_for :api_users, 
              path: 'api_users',
              controllers: {
                sessions: 'api_users/sessions', defaults: {format: :json},
                confirmations: 'api_users/confirmations',
                passwords: 'api_users/passwords',
                registrations: 'api_users/registrations',
                unlocks: 'api_users/unlocks'
              }

  devise_for :users,
              path: 'users',
              controllers: {
                sessions: 'users/sessions',
                confirmations: 'users/confirmations',
                passwords: 'users/passwords',
                registrations: 'users/registrations',
                unlocks: 'users/unlocks'
              }

  get 'welcome/index'

  namespace :api, defaults: {format: :json} do
    namespace :v1 do
      resources :things
    end
  end

  get  '/users', to: 'users/users#index'
  root to: 'welcome#index'

end

Does anyone have an idea how to properly handle this situation? I am probably overlooking something obvious. I only half know what I am doing.

I appreciate any advice.

FYI, I noticed the following in the rails log when signing out an api_user:
Filter chain halted as :verify_signed_out_user rendered or redirected

I did some Googling, which guided me to add the following to my ApiUsers::SessionsController:
skip_before_action :verify_signed_out_user , only: :destroy

Now the puts statement in the destroy action shows up in the log, but the rescue still does not work.

Hey @Mei152 . If you are getting the puts but not getting to the raise, probably you are not having those exceptions... Please, open a new issue filling up the template with all the debugging information so that it is easier to help you. Thanks!