Devise, Ruby on Rails, Change format of login details before authenticating - ruby-on-rails

We were using devise with an email, but we are now transitioning to a mobile login (user gives their mobile number) since this is the only info we need. Email would be good but not required.
I have got it working with mobile number authentication with:
config.authentication_keys = [ :mobile ]
It works, if you enter the number exactly as entered. Though there are many ways people can enter in a number (vs email)
i.e
in the local format
0125551234
or international
27125551234
I actually normalize the mobile number when i store it in the DB, so would like to use the same normalization before authenticating.
Is there a way to do it other then just overriding the sessions controller?
Update:
Used the following method: https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-sign-in-using-their-username-or-email-address
The reason is we have some users already, and we need to allow them to login with email as well if they did not capture their mobile.

You can specify a method in your ApplicationController (only since you don't want to override SessionsController) that will convert params[:mobile] to whatever format you want.
prepend_before_filter :format_mobile_param
def format_mobile_param
return unless params[:mobile]
params[:mobile] = your_formatting_method(params[:method])
end
This will alter the params before the call is made to authenticate_user! (assuming you call authenticate_user! in a before filter).

I think the best way would be to override the SessionsController, just add a prepend_before_filter :format_mobile_param. Don't forget to scope this method to the create method of SessionsController.
Also, dont forget to add the controller routes. Hope it helps.

Related

Devise Password Controller edit method renders nothing

I've been having a lot of trouble getting devise's reset password page to actually render. I've overrided
the edit function and have been trying to force it to render the view but it ends up as an empty string. Any ideas as to how to make it work?
Side note is that I'm using rails in API mode, is there anything I need to configure to get the page to render?
class PasswordsController < Devise::PasswordsController
def edit
self.resource = resource_class.new
set_minimum_password_length
resource.reset_password_token = params[:reset_password_token]
render template: 'devise/users/passwords/edit'
end
end
well, еhough strictly speaking, need to add view, but if your app is kind of API with endpoints only, it's not a way for sure, so you shouldn't redefine device methods or whatever.
In the case of API mode, you need to implement route on the FE part, and then use the Device's endpoints to reset-password logic.
Btw, this is a common feature and Device's wiki is presented at least 3 solutions to implement this, please check this link:
https://github.com/heartcombo/devise/wiki/How-To:-Allow-users-to-edit-their-password
I hope it's help

Is it possible to do Devise sign in, out, etc without any redirection whatsover?

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.

how to implement second level authentication in rails?

I have a devise authentication already setup in my application.Now i want to implement second level authentication in rails. i want to hard code the email and password in coding in controllers. i want to compare this email and password with the email and password provided by the user. rather than saving in database and picking for second level authentication.,Because these 5 to 6 links are going to be used by only one person in the company at any point of time. i want to implement this for a 5 to 6 actions in my application in controller. how can i implement this? i checked a gem gem 'devise-authy' but it seems it sends a password to mobile for otp authentication. i dont want otp authentication. i want a second level authentication with the same email as devise email but the password should be different from the devise original password.(like in devise table i want to take one more column second_level_password.) is there any gem for this kind of requirement or is this manually coded. if it is manually coded then how can i do that?
You can use before_action in rails controller.
Add following line of code in your controller.
before_action :second_level_access
private
def second_level_access
#You can use your logic to get password
unless current_user.second_level_password == params[:second_level_password]
# signout to user because user is already logged in with devise.
redirect_to session_path
end
end
EDIT
You can use
attr_accessor_with_default :second_level_logged_in, false
add following line to application controller:
before_action :logged_in_with_second_level, if: 'current_user and !second_level_logged_in'
def logged_in_with_second_level
redirect_to 'second_level_logged_in_form_page'
end
in create action of second_level_logged_in
add code current_user.second_level_logged_in=true

How to make devise gem go to specific page when the option 'Remember me' is ticked?

So, a returning user in my app who has ticked "Remember me" has to land in a different page inside the app instrad of the home page and being logged in there.
I went through the documentation but i cant find a function that does this.
I also struggle into make devise go to a different page when the user updates the profile and i use wrong methods for this. Can you give me the correct method and where to put it as well?
I tried using the method 'after_remembered' in my ApplicationController but it doesn't work while the method after_sign_in_path_for works!
Do i need to overwrite something?
Devise sets a remember_created_at value for the user if "Remember Me?" is selected. In the after_sign_in_path_for you could have it check for resource.remember_created_at and then perform the redirect there.
A quick example would be in app/controllers/application_controller.rb
def after_sign_in_path_for(resource)
resource.remember_created_at ? path_for_remember : path_not_for_remember
end
To handle redirects after updates you want to use the after_update_path_for which is set up the same way:
def after_update_path_for(resource)
some_path
end

Authlogic and Single Access Token

I am having a hard time finding a simple tutorial on how to enable single access token authentication using authlogic. There is some documentation but it isn't very helpful.
I added single_access_token to my db, I added this:
single_access_allowed_request_types :any
to my Session class. but I still don't understand how a user is authenticated using the credentials param that is passed over every call. My require_authentication before filter does a standard check for current_user like this:
def current_session
return #current_session if #current_session
#current_session = Session.find
end
def current_user
#current_user = current_session && current_session.record
end
But is that enough to work? Does the Session.find method do the magic to log the user is based on my params or do I have to create separate method that actually check if the user_credentials param is there and then find the user based on it and then log that user in. I am confused if I really am "creating" a new session everytime I use a SAT or if I'm just setting current user in a before filter every time an API call is made.
Any help would be amazing! Thanks!
I implemented a single_access_token solution with authlogic and what I had to do was add single_access_allowed_request_types :all to the UserSession model.
Then I added the following to the controller where I wanted to allow single_access_token authentication.
def single_access_allowed?
["some_action_1","some_action_2","some_action_3"].include?(action_name)
end
It looks like you're missing the controller code. So if you had two actions "get_user_info" and "update_user_info" you would add.
def single_access_allowed?
["get_user_info","update_user_info"].include?(action_name)
end
The only thing I had to do make this work was
add a field called single_access_token to my users-table
add a method called single_access_allowed? to each controller where single access should be allowed.
This method would look like this:
# method for authlogic: defines for which action the single-access-token can be used
def single_access_allowed?
(action_name == "deliver") || (action_name == "delivery_status")
end
I did not have to add anything in UserSessionsController or the UserSession object. Authlogic handles that for you. With a single-access-token only one request is authenticated, so there is not a persistent session. Each request has to send the single-access-token. Hence the name: a token to get a single access :)
Hope this helps.
The source code of authlogic is the best documentation on the single access token. This is the specific section that discusses it.
You will need to add a private method called single_access_allowed? in the controller where you are trying to let users access. The Single Access Token is passed by default as a URL encoded parameter using the name user_credentials. So to hit your controller without logging in it will be /your_route/?user_credentials=xxxxxx

Resources