waiting-for-dev / devise-jwt

JWT token authentication with devise and rails

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Authenticate_user controller helper returns 401 regardless of token validity

michaelrevans opened this issue · comments

I have successfully set up Google omniauth with Devise to log a user in and used the custom JWT generator described in #3 to return a JWT to the client. Passing that token back in the Authorization header as Bearer <token>, however, always returns 401 Unauthorized when the authenticate_user! before_action is used.

To Reproduce
config/routes.rb

Rails.application.routes.draw do
  devise_for :users,
             controllers: { omniauth_callbacks: 'api/v1/users/omniauth_callbacks' },
             defaults: { format: :json },
             path: "api/v1/users"

  get 'health-check', to: 'health_check#show'
end

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

class Api::V1::Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def google_oauth2
    @user, token = User.from_omniauth(request.env["omniauth.auth"])

    response.headers["Authorization"] = "Bearer #{token}" # this works as intended and the token is returned to the client
    render json: { message: "Success" }
  end

  def failure
    render json: { error: "Failure" }
  end
end

app/models/user.rb

class User < ApplicationRecord
  include Devise::JWT::RevocationStrategies::JTIMatcher

  devise :omniauthable, :jwt_authenticatable,
         omniauth_providers: %i[google_oauth2],
         jwt_revocation_strategy: self

  validates_presence_of :uid, :provider, :jti
  validates_uniqueness_of :jti

  def self.from_omniauth(auth)
    # again, this works as intended and can create a new user with a token, as well as log an existing user back in and generate a token
    user = find_or_initialize_by(provider: auth.provider, uid: auth.uid)
    user.email = auth.info.email
    user.name = auth.info.name
    user.image_url = auth.info.image
    token, payload = Warden::JWTAuth::UserEncoder.new.call(user, :user, "")
    user.jti = payload["jti"] # this successfully stores the jti and it matches the jti in the decoded token that the client sends back in the Authorization header
    user.save

    [user, token]
  end
end

app/controllers/health_check_controller.rb

class HealthCheckController < ApplicationController
  before_action :authenticate_user! # this fails, despite giving a valid JWT in the Authorization header

  def show
    render json: { message: 'Success' }.to_json
  end
end

Current behavior

401 is returned by authenticate_user! even when a valid token is sent in the Authorization header.

Expected behavior

authenticate_user! should pass when a valid taken is sent

Desktop (please complete the following information):

  • OS: MacOS Ventura
  • Browser Chrome
  • Version 110

Additional information
Sending the request via cURL has given an error message about the wrong aud, so I also tried adding jwt.aud_header = '' to the configuration, but to no avail.

curl http://localhost:5555/health-check \
> -H 'Content-Type: application/json' \
> -H 'Accept: application/json' \
> -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI0NDIzYzBhMy1iZTYzLTQzMzgtYTlmNC05YjBhZGNjZGI5ZTAiLCJzdWIiOiIiLCJzY3AiOiJ1c2VyIiwiYXVkIjoiIiwiaWF0IjoxNjc2OTA5NDQyLCJleHAiOjE2NzgxMTkwNDJ9.T3jxQzGdpbdzDZ41CHa3oVOzZ04kaDsrchK_H1AcGNg'
{"error":"wrong aud"}

I'm sure the problem lies on my side, I find it unlikely this is a bug, but I'm at a loss as to what to try next in order to solve my problem.