Anyone try integrating this with Devise yet? I want Devise for it's user and password management, but need JWT since I have an API.
Knock does not aim to be compatible with Devise and I would not recommend mixing both.
In case someone else is trying to get this to work:
#####
# api/base_controller.rb
#####
include Knock::Authenticable
before_action :authenticate_user
# devise defines this, while knock is using `method_missing`
undef_method :current_user
#####
# models/user.rb
#####
# Knock requires :authenticate
alias_method :authenticate, :valid_password?
# Returns the user stored in the payload's subject
def self.from_token_payload payload
self.find payload["sub"]
end
Worked for me this way, but in the end I went with a custom solution since it's not a big deal to do this from scratch with the jwt gem, giving more flexibility for devise integration...
@amiuhle Thank you for this. I also agree with you, integrating knock + devise doesn't have much value. I would recommand using one or the other but not both at the same time. I reckon the best use case for knock is if you're relying on JWT for authentication exclusively.
The only thing I can think of using Devise is for generating forgot password tokens and links, other than that I see no need to have Devise. If we already have Devise integration, what would be the best way to remove Devise ad yet support "Forgot Password" flows.
@raviada You need to roll your own mailers, invitations, etc if you use this exclusively for auth. This is not an ideal gem if you have a full UI as well as an API.
In my case I have to accept API requests using the same controllers from both the browser and native apps. As controllers' classes are cached, after current_user gets replaced by Knock as per @amiuhle's solution, subsequent requests coming from the browser do not have access to Devise's current_user implementation anymore. So here is my take.
#####
# api/base_controller.rb
#####
include Knock::Authenticable
before_action :authenticate_user
before_action :skip_session
# JWT: Knock defines it's own current_user method unless one is already
# defined. As controller class is cached between requests, this method
# stays and interferes with a browser-originated requests which rely on
# Devise's implementation of current_user. As we define the method here,
# Knock does not reimplement it anymore but we have to do its thing
# manually.
def current_user
if token
@_current_user ||= begin
Knock::AuthToken.new(token: token).entity_for(User)
rescue
nil
end
else
super
end
end
private
# JWT: No need to try and load session as there is none in an API request
def skip_session
request.session_options[:skip] = true if token
end
# JWT: overriding Knock's method to manually trigger Devise's auth.
# When there is no token we assume the request comes from the browser so
# has a session (potentially with warden key) attached.
def authenticate_entity(entity_name)
if token
super(entity_name)
else
current_user
end
end
#####
# models/user.rb
#####
# JWT: Authentication within an API request
alias authenticate valid_password?
@amiuhle Thanks for saving so many life. I almost think of rewriting all the functions of devise 馃槗
When I add undef_method :current_user I receive the following error: ActionController::RoutingError (undefined method 'current_user' for class 'ApiController'):.
However, if I try to include before_filter :authenticate_user then I receive the error Filter chain halted as :authenticate_user rendered or redirected. Completed 401 Unauthorized in 4ms (ActiveRecord: 0.0ms)
I am using Devise to power authentication for ActiveAdmin and am using Knock to add authorisation to my API.
I somehow need to re-write authenticate_user so that it doesn't just try to use the Devise method but am struggling to work out how.
Any help would be much appreciated!
Most helpful comment
In case someone else is trying to get this to work:
Worked for me this way, but in the end I went with a custom solution since it's not a big deal to do this from scratch with the
jwtgem, giving more flexibility for devise integration...