Rails 3 - logging in a user automatically after creating the account - ruby-on-rails

In my users_controller I have two methods signup_process and login. I would like to call login automatically for a user after a successful signup_process call.
Is this possible? In other language frameworks I would usually just call the login controller action direct from the signup_process action passing the username and password - but I think that is frowned upon in Rails.
Is there some way instead to post the user data to the users.login action from my controller?
This must be a common pattern - what am I missing? :)
Thanks in advance!

Not sure what you mean by frowned upon, but here's one way
class UsersController < ...
def signup
// Do some stuff
do_login(username, password)
// render or redirect as you wish
end
def login
do_login(username,password)
// render or redirect as you wish
end
private
def do_login(username,password)
// do the actual login processing
// can even render or redirect here if it's common to both setup and login
end
end
Would that do what you want?

Well, what does the login action do?
Most likely, it sets something in the session indicating the user is logged in. You could do just that, after creating the user.
It does not make sense to call a controller action, since it's most likely hooked up with a view/form.
Please provide more information on what the login action does, if you still feel like you need to go through it.

Related

How do you make sure that after sign in the user completes the profile form first before they can use the rest of the website functions.

How do you make sure that after sign in the user completes the profile form first before they can use the rest of the website functions. I am trying to make sure that after the member has completed the sign up form and then completes there email confirmation with devise that when they sign in that when they are redirected to the new_member_profile_path(current_member) form that they stay on this page and that if they decide to go to a link and click that that they will automatically be redirected back to the complete your profile page with the notice before please complete your profile first. I have it set already once they have completed the form they will be redirected to their member's page. I have looked in multi-forms with wicked - I really feel that because I am still am a Novice rails developer that this would be unnecessary. I am thinking about putting an if clause in the application.html.erb where the site nav template is based and putting a clause with <% if current_member_profile.blank ? %> then redirect back to new_member_profile_path(current_member) with a flash notice tag written in the html file. I have tried this if clause but does not work - comes up as undefined method. Please could someone point me in the right direction or give me the simple solution of getting this idea to work. Thanks in advance ;)
There are various approaches to achieve what you're trying to do. Perhaps the cleanest with the least amount of code needed would be to first authenticate the user with Devise's own authenticate_user! filter and then check for a field that can only be there when the profile has been filled in.
# in user.rb
def has_completed_profile?
first_name.present?
end
# in application_controller.rb
before_action :authenticate_user!
before_action :require_user_profile
private
def require_user_profile
# nothing needs to be done if the profile was already filled in
return if user_signed_in? && current_user.has_completed_profile?
redirect_to edit_profile_url, alert: "Please complete your profile first!"
return false
end
Notice how I've extracted has_completed_profile? into the User model instead of putting the name check directly into the controller. This way, if you need to change the logic of what makes a profile "complete", you don't need to touch the controller at all ("complete profile" is a business concept, not a routing/HTTP concept and thus doesn't belong in the controller).
In controllers where you don't want the additional profile check – e.g. the controller where the user actually completes their profile, where they presumably need to be logged in but can't have a profile yet – you just skip the additional filter:
# in profiles_controller.rb
skip_before_action :require_user_profile
Side note: Over the years I've learned that it's best to keep things like profile data, address data, phone numbers and what not in a separate model and don't extend Devise's User model. This prevents various issues and keeps the already huge User model (Devise includes dozens of methods into it and turns it into a God Object as it is) a bit slimmer. And if you think about it, it also makes sense in terms of business logic: A user has_one profile, has_one address (or has_many addresses) etc.
Hope that's clear.
You can add a new method in the application_controller.rb
For eg
def current_member_profile
current_user.name.blank?
end
Check the params which should not be blank when creating a member profile. I have added 'name' here for example. if the params is blank, then it will redirect as you have specified in your application.html.erb
Instead for putting an if condition on the application.html.erb, You can use a before_action in your application_controller.
Something like this.
application_controller.rb:
before_action :check_for_profile_completion
def check_for_profile_completion
// your code of redirection to the page if the profile is incomplete
end
Also you can skip this action on controller which you don't wanna restrict user to go. Like
skip_before_action :check_for_profile_completion, only: [://actions you wanna skip seperated by comma]

Show page only once after sign up - Ruby on Rails

I have an app that is using Devise, I would like that after a user signs up, they are directed to a specific page, this page calls an API and saves the value from the API, I need this page to only be accessible or available after a user completes the sign-up form and clicks submit, and is then redirected to this page.
I do not want this page or URL accessible any other way but after sign-up, as the API will send a new value if accessed again. How can I accomplish this?
Once a user signs up they will be redirected to the page calling the API:
def after_sign_up_path_for(resource)
api_call_path ##path that can only be accessed after sign_up
end
The API is called and the response from the JSON data is automatically saved to the database once the page is opened, if the page gets opened again a new JSON response will be received with new data, which is what I would like to avoid.
So in a nutshell, my question is how can I restrict access to a specific path, and only make that path accessible if a user completes the sign-up form (devise) OR is there a way that I can run the code from the controller using a callback/filter after the user is created through the User model?
I was just busy with something similar. You do not need to direct the user to a page to run the code, you can just run the code that needs to be run after the user logs in the first time.
You need to create a Session Controller, and create a conditional statement that checks if the user has logged in previously:
#config/routes.rb
devise_for :users, controllers: { sessions: "sessions" }
#app/controllers/sessions_controller.rb
class SessionsController < Devise::SessionsController
def after_sign_in_path_for(resource)
if resource.sign_in_count == 1
##Do something awesome
else
root_path
end
end
end
As Emmanuel advised you can check futher info on the Devise Controllers.
Let's call the moment between after sign_up and showing the specific page - state A. So in the specific page controller you need to know - is the user in state A. You can achieve it by
1) saving to db (server side) that user is in state A after sign up and resetting state after showing specific page (you can do resetting triggered by client side to guarantee that page is showed).
2) saving to cookies (client side) after sign up then do as above.
Second solution is more clean in my opinion, but I do not know how strict is the rule to show only once
You can customize devise users controller by issuing
rails generate devise:controllers [scope]
Then customise UsersController such that after user is saved you can call your api code there
eg
def create
#user = ....
if #user.save
#user.call_api_method()
else
......
end
end
For more information check Configuring controllers

Devise stored_location_for(resource) returns nil

I read the few posts about troubleshooting stored_location_for here, but can't seem to figure it out and not sure how to troubleshoot.
I tried deleting my custom after_sign_in_path_for, but that didn't work either. My location is never getting saved, although as I understand it after each session/page update it should store the location. Do I need to through that in as a filter manually?
def after_sign_in_path_for(resource)
stored_location_for(resource) ||
if resource.is_a?(Account)
add_quote_to_account(resource)
if resource.applications.any?
edit_application_path(resource.applications(true).last)
else
root_path
end
else
super
end
end
May be you are not storing the location where you want to redirect_to after signing in with devise. Devise provides two methods - store_location_for and stored_location_for
https://github.com/plataformatec/devise/blob/master/lib/devise/controllers/store_location.rb
Suppose your user model is named "user" then
A call to store_location_for(:user, my_desired_path) in your controller will store the url "my_desired_path" in session with key "user_return_to". Basically this method will simply do this - session["user_return_to"] = my_desired_path. Probably you are missing this. I have a booking controller and a "login" action which stores the checkout location for booking in booking controller and then displays a login form for users in rendered view -
def login
my_desired_path = url_for(controller: 'bookings', action: 'checkout')
store_location_for(:user, my_desired_path)
end
Now you can use stored_location_for(:user) to retrieve my_desired_path from your session. So to say, a call to stored_location_for(:user) will return "my_desired_path.
Now if you use stored_location_for in your custom after_sign_in_path_for(:user) then it shall return "my_desired_path".
Additional Point -
A call to stored_location_for(:user) returns session[:user_return_to] but also clears this session after returning the value if your redirect format is navigational format. So a second call to stored_location_for(:user) will not return my_desired_path. And sometimes this is how we want our application to behave. On contrary, if your redirect format is not navigational format then session wont be cleared and a second sign-in will again redirect to same "my_desired_path".
Sometimes you want to redirect to different locations in signing-in from different pages in your application. Suppose, you want to redirect to "\X" on page A and to "\Y" on page B. Now follow these steps -
User in on page A - store_location_for(:user, "\X") is saved in session.
Application provides a sign-in form but User does not sign-in and just browse here and there in your application.
User is on page B and perform a sign-in expecting to land on "\Y" but unexpectedly lands to "\X".
So take care of it in your application. This scenario is more likely to occur in applications which uses AJAX for signing-in.

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

rails 3 / heroku / devise: how specify different 'landing page' when users sign in?

We'd like to have 3 different signin forms for our app:
the default signin form, takes them to their normal dashboard (as we do now)
a "foo" signin form that, if they use THAT form to sign in, takes them to a special purpose screen
a "bar" signin form that takes them to yet anogther special-purpose screen
I assume the right approach is to somehow
a) create a new route for /foo and /bar, probably directing both to the SAME signin method but in the route add a url parameter 'signin_type' telling us which "type" of signin form it is?
b) implement a custom RegistrationsController method(s) (what name?) to handle the signin form (we already have a custom new and create method for when they register, since our registration form needed a 'referral code' field added), and have the method look at the url parameter 'signin_type' to redirect the sign to either the normal, or foo, or bad
c) implement another method that handles the signin submit (is that a different method?) that looks at some special embedded form value to figur eout which signing form was used?
That's my best guess. If correct, it's how to do (b) and (c) that has me stumped. Any thoughts will be appreciated!
I think you may be trying to over engineer this. I would approach this with a single sign_in page, and just use conditional logic in overriding the after_sign_in_path_for(resource) method for the Devise controller. Not only will this be much easier to implement now, it will be a lot easier to maintain in the future. Simply add to your ApplicationController.rb:
protected
def stored_location_for(resource)
nil
end
def after_sign_in_path_for(resource)
if condition_foo
redirect_to foo_url
elsif condition bar
redirect_to bar_url
else
redirect_to dashboard_url
end
end
The first method overrides Devise's default location of root and sets it to nil, then the logic after that is pretty self explanatory. This should work for what you are wanting.

Resources