Logout with JTIMatcher as revocation strategy not revoking token
Lary15 opened this issue · comments
Expected behavior
While using JTIMatcher the expected outcome of the logout action is to update the jti column. As I read in this issue #167,
this is the only time the jti column will update.
Actual behavior
When I logout, the jti doesn't change, so, while repeatedly logging in, the jti is always the same.
Steps to Reproduce the Problem
- Calling login method with credentials of a user
- Calling logout method
- Calling login method with the same credentials
- The jti field remains the same
Debugging information
- Version of
devise-jwt
in use: 0.7.0 - Version of
rails
in use: 6.1.1 - Version of
ruby
in use: 2.7.1 - Output of
Devise::JWT.config
#<Dry::Configurable::Config values={:secret=>#<Dry::Configurable::Settings elements=#<Concurrent::Map:0x00000000048df910 entries=8 default_proc=nil>>,
:expiration_time=>#<Dry::Configurable::Settings elements=#<Concurrent::Map:0x00000000048df910 entries=8 default_proc=nil>>,
:dispatch_requests=>#<Dry::Configurable::Settings elements=#<Concurrent::Map:0x00000000048df910 entries=8 default_proc=nil>>,
:revocation_requests=>#<Dry::Configurable::Settings elements=#<Concurrent::Map:0x00000000048df910 entries=8 default_proc=nil>>,
:aud_header=>#<Dry::Configurable::Settings elements=#<Concurrent::Map:0x00000000048df910 entries=8 default_proc=nil>>, :request_formats=>{:user=>[:json]}}>
- Output of
Warden::JWTAuth.config
#<Dry::Configurable::Config values={:secret=>secret, :algorithm=>"HS256",
:expiration_time=>86400,
:aud_header=>nil,
:mappings=>{:user=>User(id: integer, email: string, encrypted_password: string, reset_password_token: string, reset_password_sent_at: datetime, remember_created_at: datetime, sign_in_count: integer, current_sign_in_at: datetime, last_sign_in_at: datetime, current_sign_in_ip: string, last_sign_in_ip: string, confirmation_token: string, confirmed_at: datetime, confirmation_sent_at: datetime, unconfirmed_email: string, failed_attempts: integer, unlock_token: string, locked_at: datetime, created_at: datetime, updated_at: datetime, jti: string)},
:dispatch_requests=>[["POST", /api\/v1\/login/],
["POST", /api\/v1\/login.json/], ["POST", /^\/api\/v1\/login.json$/], ["POST", /^\/api\/v1\/signup.json$/]],
:revocation_requests=>[["DELETE", /api\/v1\/logout/], ["DELETE", /api\/v1\/logout.json/], ["DELETE", /^\/api\/v1\/logout.json$/]],
:revocation_strategies=>{:user=>User(id: integer, email: string, encrypted_password: string, reset_password_token: string, reset_password_sent_at: datetime, remember_created_at: datetime, sign_in_count: integer, current_sign_in_at: datetime, last_sign_in_at: datetime, current_sign_in_ip: string, last_sign_in_ip: string, confirmation_token: string, confirmed_at: datetime, confirmation_sent_at: datetime, unconfirmed_email: string, failed_attempts: integer, unlock_token: string, locked_at: datetime, created_at: datetime, updated_at: datetime, jti: string)}}>
- Output of
Devise.mappings
{:user=>#<Devise::Mapping:0x0000000005a57b70 @scoped_path="users",
@singular=:user,
@class_name="User",
@klass=#<Devise::Getter:0x0000000005a57738 @name="User">,
@path="",
@path_prefix=nil,
@sign_out_via=:delete,
@format=nil,
@router_name=nil,
@failure_app=Devise::FailureApp,
@controllers={:sessions=>"api/v1/users/sessions", :registrations=>"api/v1/users/registrations", :passwords=>"api/v1/users/passwords"},
@path_names={:registration=>"api/v1/signup", :new=>"new", :edit=>"edit", :sign_in=>"api/v1/login", :sign_out=>"api/v1/logout", :password=>"api/v1/reset_password", :sign_up=>"sign_up", :cancel=>"cancel"},
@modules=[:database_authenticatable, :rememberable, :recoverable, :registerable, :validatable, :jwt_authenticatable], @routes=[:session, :password, :registration],
@used_routes=[:session, :password, :registration],
@used_helpers=[:session, :password, :registration]>}
here is the devise configuration on my routes.rb
devise_for(
:users,
path: "",
path_names: {
sign_in: "api/v1/login",
sign_out: "api/v1/logout",
registration: "api/v1/signup",
password: "api/v1/reset_password"
},
controllers: {
sessions: "api/v1/users/sessions",
registrations: "api/v1/users/registrations",
passwords: "api/v1/users/passwords"
},
defaults: { format: :json }
)
user.rb
class User < ApplicationRecord
include Devise::JWT::RevocationStrategies::JTIMatcher
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable, :recoverable, :rememberable,
:jwt_authenticatable, :validatable, jwt_revocation_strategy: self
validates :email, presence: true, uniqueness: true
def token
token, _payload = Warden::JWTAuth::UserEncoder.new.call(self, :user, nil)
token
end
end
jwt config in devise.rb
config.jwt do |jwt|
# Setting secret key that will be used to sign generated tokens
jwt.secret = secret
jwt.dispatch_requests = [
["POST", %r{api/v1/login}],
["POST", %r{api/v1/login.json}]
]
jwt.revocation_requests = [
["DELETE", %r{api/v1/logout}],
["DELETE", %r{api/v1/logout.json}]
]
jwt.expiration_time = 1.day.to_i
jwt.request_formats = { user: [:json] }
end
and sessions_controller.rb
module Api
module V1
module Users
class SessionsController < Devise::SessionsController
# before_action :configure_sign_in_params, only: [:create]
# GET /resource/sign_in
# def new
# super
# end
# POST /resource/sign_in
# DELETE /resource/sign_out
# def destroy
# super
# end
# protected
# If you have extra params to permit, append them to the sanitizer.
# def configure_sign_in_params
# devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute])
# end
private
def respond_with(resource, _opts = {})
if resource.errors.empty?
render json: resource.to_json(methods: :token), status: :ok
else
render json: resource.errors.full_messages, status: :unauthorized
end
end
def respond_to_on_destroy
head :no_content
end
end
end
end
end
I look up various issues to see if could find the cause of the problem, but to no avail. I guess the login is working as expected, it is generating the token and putting on the header, but the logout is not doing much.
Ok, now so this was really dumb. I forgot to put the Authorization header with the token in the logout request 🤦♀️