I'm using Rails 3 and Devise for authentication. I have a proper working devise for the website and basic authentication for API (json handler). How do I enable the digest authentication?
Their Wiki is telling me to add
def http_authenticate
authenticate_or_request_with_http_digest do |user_name, password|
user_name == "foo" && password == "bar"
end
warden.custom_failure! if performed?
end
Where do I add it to and how do I make user_name/password match?
That wiki entry sure assumes a lot.
My best guess is you need to add it to the appropriate controller (or the Application controller if you want it for everything).
And then add a :before_filter :http_authenticate!
You could also try tracking down the person who wrote that wiki page and asking them.
Note. This relies on Warden to perform your authentication - Devise only handles accounts.
One of the reasons this stuff isn't documented so well is most people use a sophisticated authentication management system (eg. OmniAuth), and something else for permissions/authorization eg. DeclarativeAuthorization or CanCan if you prefer something more light weight.
HTTPBasic (and I assume Digest) tends not to play nicely with these.
Related
At this point, I'm not sure how or where the helper current_user is loaded. I'd like to select only certain properties from the user table, but devise to
select * from users table
I want to do something like select (id, email, additional_stuff) from users
I would like to be able to modify the current_user set by devise so I can optimise my application from a security point.
Using Rails version 7.0.4
RUby Version 3.1.3
Devise is build on top of the Warden gem which handles the grunt work of actually authenticating users.
Fetching the user from the database is done by the Warden::Manager.serialize_from_session method which can be reconfigured.
# app/initializers/devise.rb
config.warden do |manager|
manager.serialize_from_session(:users) do |id|
User.select(:id, :email, :additional_stuff)
.find(id)
end
end
However, I'm very sceptical that this will have any real benefits to security and you'll most likely just end up breaking parts of Devise/the rest of your application. For example authenticating the user for password updates may fail unless you load the password digest.
Make sure you have tests covering your whole Devise implementation before you monkey around with it.
It's hard to know what you actually mean by "Certain class in the application use the current_user object, one such class is template creator" . But that smells like a huge gaping security hole in itself. If this is running code that comes from the user it should not have access to the entire view context (for example the current_user method). If the user has access to the actual object then whats preventing them from querying and getting any missing data?
If you really need this feature you should be sandboxing it in a separate renderer (not using Rails build in render methods) which only has access to the context and data you explicitly pass (like a struct or decorator representing a user) which is deemed safe.
I'm using Devise, but not using the Devise controllers directly because I'm performing all of the actions through a custom built GraphQL API. One issue I have, for example, is that after enabling confirmable, if a user tries to sign in and I call Devise::Controllers::Helpers#sign_in the user gets redirected to /api/v1/users/sign_in, which doesn't exist and it would be wrong even if it exist. Instead, I need the failure to sign in to be returned back to my code (return value, exception, whatever), so that my API can encode that response to the frontend.
How can I do that?
For example, this is my log in function:
def resolve(email:, password:)
user = User.find_for_authentication(email: email)
if user&.valid_password?(password)
context[:sign_in].call(user)
{ current_user: user }
else
{ errors: [{ message: 'Email or password incorrect.' }] }
end
end
context[:sign_in] is set up in the GraphqlController by including Devise::Controllers::Helpers and then simply:
context = {
current_user: current_user,
sign_in: method(:sign_in),
sign_out: method(:sign_out)
}
Note: I am not using GraphqlDevise because I don't want to use Devise Token Auth, I'm using cookies.
I believe passing devise's sign_in/sign_out methods via context is probably a deadend.
The suggestion in the comment to your question from #Int'l Man Of Coding Mystery is good ie you could use: https://github.com/graphql-devise/graphql_devise.
If you're not keen in introducing another dependency and figuring out how to wire everything you can perhaps go with overriding devise's SessionController.
See for some examples here: Rails - How to override devise SessionsController to perform specific tasks when user signs in?
(but also don't hesitate to look at the source code for the matching Devise release: https://github.com/heartcombo/devise/blob/master/app/controllers/devise/sessions_controller.rb)
Depending on your use case you might be even able to do what you need by using some of the config options - e.g. you can perhaps try to override after_sign_in_path etc.
I've got a cross-website integration to handle. Basically I'm passing a param into the rails application and if it evaluates correctly ... then I'd like to log a user in.
Can this be done without the users password?
something like simply evaluating the password as true?
This is called "token authentication" and is supported by Devise, or can be relatively easily ginned up on your own. You want to generate a non-guessable secret token (your param), and then use that in lieu of a username. The devise wiki has links to a couple of examples:
https://github.com/plataformatec/devise/wiki/How-To:-Simple-Token-Authentication-Example
If you want a lighter-weight solution, you can also simply generate an auth token (using something like bcrypt) and then do something like:
#user = User.find_by_auth_token(params[:auth_token])
if #user is nil, then return a 403.
E.g. API_key: 4faa86aa5848207502000002 and API_secret 7375d7d1e89d3d602b184432fbcf3c09c7cb30676f19af9ac57d228be401.
Should I use SecureRandom?
Thanks!
ActiveSupport::SecureRandom would work for the actual generation, but you should also consider a way to invalidate and reset the token on different events.
Since you're using Devise, take a look at the Token Auth Strategy. You could write a similar strategy with two tokens (API Key and API Secret, respectively). You need to write both the strategy and the Model, but in both cases the Token Auth example gets you pretty far.
As a starting point (from the Token Auth example), your model should declare both required parameters.
module Devise
module Models
module APIKeyAuthenticatable
...
def self.required_fields(klass)
[:api_key, :api_secret]
end
def reset_keys
self.api_key = self.class.api_key
self.api_secret = self.class.api_secret
end
You might also want to read Custom authentication strategy for devise. If you're looking to provide a more full-featured API auth solution atop devise devise_oauth2_providable looks pretty good.
I have tried authlogic_api. It was fairly easy to implement.
how can I make devise enforce getting correct password before canceling registration (deleting account)
You can either:
Do something along the lines of pst's answer: have a text box for :canceled in a form that when saved, cancels the account. Since it would be part of the user model, devise would force the password check upon the update action.
Do it yourself via a button that warns (similar to the delete buttons often in Rails). The controller that receives the request would simply do something like the following (I seem to remember that Devise uses MD5, maybe it's SHA1, SHA2, unsure- see documentation; the key is to use the same type):
if params[:password] == Digest::MD5.hexdigest(params[:password])
cancel_account(…)
…
end
Yeah, the key here is knowing how to encrypt params[:password] to be able to compare it to the current_user.encrypted_password
Older versions of Devise use a password_salt as well. My advice to you would be to look at how devise does this on sign in, and use the same method in your destroy action, or whatever user-facing page you have for that.