admin管理员组

文章数量:1334817

In a rails API-only, I'm implementing JWT authentication using devise-jwt with JTIMatcher strategy. Aafter Logout, Login is giving 401 unauthorized. "You need to sign in or sign up before continuing."

  • Signup -> Ok, User with JTI is created
  • Login -> Ok, JTI remains the same
  • Logout (with previous auth token) -> Ok, JTI change
  • Login again -> 401 Unauthorized, "You need to sign in or sign up before continuing."

After the token expiration time, the issue remains

Any ideas?

See my attempt, I 'm expecting to login and logout normally:

app/controllers/api/v1/users/sessions_controller.rb

class Api::V1::Users::SessionsController < Devise::SessionsController
  respond_to :json 
  def respond_with(resource, _opts = {})
    @token = request.env['warden-jwt_auth.token']
    headers['Authorization'] = @token  
    Rails.logger.debug("Authorization Header: #{request.headers['Authorization']}")
    Rails.logger.debug("Current User: #{resource.inspect}")
    render json: {
      status: {
        code: 200, message: 'Logged in successfully.',
        token: @token,
        data: {
          user: UserSerializer.new(resource).serializable_hash[:data][:attributes]
        }
      }
    }, status: :ok
  end  

  def respond_to_on_destroy 
    if request.headers['Authorization'].present?
      jwt_payload = JWT.decode(request.headers['Authorization'].split.last,
      Rails.application.credentials.devise_jwt_key!).first   
      current_user = User.find(jwt_payload['sub'])
    end   
    Rails.logger.debug("Authorization Header: #{request.headers['Authorization']}")
    Rails.logger.debug("Current User: #{current_user.inspect}")
    if current_user
      render json: {
        status: 200,
        message: 'Logged out successfully.',
        user: current_user,
      }, status: :ok
    else
      render json: {
        status: 401,
        message: "Couldn't find an active session.",
        user: current_user,
      }, status: :unauthorized
    end
  end
end

app/controllers/api/v1/users/sessions_controller.rb

class Api::V1::Users::SessionsController < Devise::SessionsController
  respond_to :json 
  def respond_with(resource, _opts = {})
    @token = request.env['warden-jwt_auth.token']
    headers['Authorization'] = @token  
    Rails.logger.debug("Authorization Header: #{request.headers['Authorization']}")
    Rails.logger.debug("Current User: #{resource.inspect}")
    render json: {
      status: {
        code: 200, message: 'Logged in successfully.',
        token: @token,
        data: {
          user: UserSerializer.new(resource).serializable_hash[:data][:attributes]
        }
      }
    }, status: :ok
  end  

  def respond_to_on_destroy 
    if request.headers['Authorization'].present?
      jwt_payload = JWT.decode(request.headers['Authorization'].split.last,
      Rails.application.credentials.devise_jwt_key!).first   
      current_user = User.find(jwt_payload['sub'])
    end   
    Rails.logger.debug("Authorization Header: #{request.headers['Authorization']}")
    Rails.logger.debug("Current User: #{current_user.inspect}")
    if current_user
      render json: {
        status: 200,
        message: 'Logged out successfully.',
        user: current_user,
      }, status: :ok
    else
      render json: {
        status: 401,
        message: "Couldn't find an active session.",
        user: current_user,
      }, status: :unauthorized
    end
  end
end

app/controllers/api/v1/users/sessions_controller.rb

class Api::V1::Users::SessionsController < Devise::SessionsController
  respond_to :json 
  def respond_with(resource, _opts = {})
    @token = request.env['warden-jwt_auth.token']
    headers['Authorization'] = @token  
    Rails.logger.debug("Authorization Header: #{request.headers['Authorization']}")
    Rails.logger.debug("Current User: #{resource.inspect}")
    render json: {
      status: {
        code: 200, message: 'Logged in successfully.',
        token: @token,
        data: {
          user: UserSerializer.new(resource).serializable_hash[:data][:attributes]
        }
      }
    }, status: :ok
  end  

  def respond_to_on_destroy 
    if request.headers['Authorization'].present?
      jwt_payload = JWT.decode(request.headers['Authorization'].split.last,
      Rails.application.credentials.devise_jwt_key!).first   
      current_user = User.find(jwt_payload['sub'])
    end   
    Rails.logger.debug("Authorization Header: #{request.headers['Authorization']}")
    Rails.logger.debug("Current User: #{current_user.inspect}")
    if current_user
      render json: {
        status: 200,
        message: 'Logged out successfully.',
        user: current_user,
      }, status: :ok
    else
      render json: {
        status: 401,
        message: "Couldn't find an active session.",
        user: current_user,
      }, status: :unauthorized
    end
  end
end

config/initializers/devise.rb

  config.jwt do |jwt|
    jwt.secret = Rails.application.credentials.devise_jwt_key!
    jwt.dispatch_requests = [
      ['POST', %r{^/login$}]
    ]
    jwt.revocation_requests = [
      ['DELETE', %r{^/logout$}]
    ]
    jwt.expiration_time = 1.hours.to_i
  end

config/routes.rb

Rails.application.routes.draw do
  mount Rswag::Ui::Engine => '/api-docs'
  mount Rswag::Api::Engine => '/api-docs'
  get "up" => "rails/health#show", as: :rails_health_check
  resources :users
  devise_for :users, path: '', path_names: {
    sign_in: 'login',
    sign_out: 'logout',
    registration: 'signup'
  },  singular: :user, 
  controllers: {
    sessions: 'api/v1/users/sessions',
    registrations: 'api/v1/users/registrations'
  }
  namespace :api do
    namespace :v1 do
      resources :events
    end
  end
end

app/models/user.rb

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  include Devise::JWT::RevocationStrategies::JTIMatcher
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :jwt_authenticatable, jwt_revocation_strategy: self
  has_many :events
  validates :name, presence: true
  validates :email, presence: true
end

本文标签: rubyRails API devisejwt Unauthorized to login after logoutStack Overflow