Usage with Devise
kevinhughes27 opened this issue · comments
Hello! First I want to thank you for this gem. It's been great to work with so far.
I'm using it in a larger project that has a few clients including a standard Rails web client. The project already uses Devise and now needs JWT for a new frontend client. I ran into some issues because Devise adds a current_user
method to every controller. When knock tries to authenticate it checks if this method already exists and then just uses the one provided by Devise:
https://github.com/nsarno/knock/blob/master/lib/knock/authenticable.rb#L44
To work around this I just re-defined my own current_user
method the way knock would have defined it. I'm opening this issue in case anyone else encounters this and to start a discussion to see about fixing it somehow.
Instead of using unless self.respond_to?(getter_name)
to check if knock has defined the method we could set an instance variable so we know that knock has defined the method and not something else. This would remove the ability to define your own current_ method on purpose though.
I've had to do something similar, but for Sorcery:
# Override for Sorcery
def authenticate_for entity_class
getter_name = "current_#{entity_class.to_s.parameterize.underscore}"
self.class.undef_method getter_name.to_sym # NOTE: undefine Sorcery's "current_user"
define_current_entity_getter(entity_class, getter_name)
public_send(getter_name)
end
# Override for Sorcery
def define_current_entity_getter entity_class, getter_name
return if respond_to?(getter_name)
memoization_var_name = "@#{getter_name}" # NOTE: modified to conform to Sorcery's "@current_user" instance variable
self.class.send(:define_method, getter_name) do
unless instance_variable_defined?(memoization_var_name)
current =
begin
::Knock::AuthToken.new(token: token).entity_for(entity_class)
rescue ::Knock.not_found_exception_class, JWT::DecodeError
nil
end
instance_variable_set(memoization_var_name, current)
end
instance_variable_get(memoization_var_name)
end
end
Maybe a couple of config variables, one to define Knock's current_user
method and another to allow Knock to override the underlying authentication library's current_user
method, would be helpful.
Hi @kevinhughes27, I think you'll find this video useful.
ah so they use undef_method
That would work too.
@kevinhughes27 I would be really interested in seeing how your defined your own current_user method because I'm currently facing exactly the same problem but am unsure how to go about solving it.
I have Devise powering authentication for ActiveAdmin but require Knock to authenticate my Rails API. At the moment my current_user is returning as nothing when the user authenticates which means I can't get the current user :(
Just to make my issue even more clear.
If I add undef_method :current_user
then i'm receiving the error undefined method 'current_user' for class 'ApiController'
and if I try to use before_action :authenticate_user
I'm receiving an error that says Filter chain halted as :authenticate_user rendered or redirected. Completed 401 Unauthorized in 8ms
.
I'm really not sure where to go from here so any help would be much appreciated.
Sure thing!
class ApiController < ActionController::API
include Knock::Authenticable
private
# overwrite current_user added by devise with
# a method for knock (gem would add this if it were not already defined)
# https://github.com/nsarno/knock/issues/220
def current_user
@current_user ||= begin
Knock::AuthToken.new(token: token).entity_for(User)
rescue Knock.not_found_exception_class, JWT::DecodeError
nil
end
end
end
Awesome thanks for this Kevin!!
My issue actually turned out to be nothing to do with Devise and I have it all working now but this is still super helpful for when I undoubtedly hit the issue at some point in the future 👍
Thanks again!