I am using Omniauth to make users sign up with Facebook account. When they first click on the auth link, I direct them to the sign up page for them to put some additional information, unless they already have an account for my application.
SessionsController
def create_facebook
#make an environment variable
auth = request.env['omniauth.auth']
if User.find_by_provider_and_uid(auth["provider"], auth["uid"])
user = User.find_by_provider_and_uid(auth["provider"], auth["uid"])
session[:user_id] = user.id
redirect_to lessons_path, flash[:notice] => "Signed in!"
else
#go to signup page to get additional info
redirect_to new_user_path(:auth => auth) #problem
end
end
In this case, I'm passing the entire auth hash using a parameter in the URL because I want to call User.create_with_omniauth(auth) in Users#new. Should I avoid from doing it? What are the other alternatives?
You've got a few approaches that I can see:
Store the auth value in the user's session (but bear in mind that the session doesn't encrypt values by default; it only signs them), and then pull them out when viewing the new user action.
Alternatively:
Just call the new user action code directly (either share it via a helper, or if it's on the same controller - call the action and then make sure you call render to set up the correct view)
A third and more robust alternative that is similar to the session approach, but a little more heavyweight:
Set up the user with what information you have at this step (and set them to some sort of "partially registered" state). Sign them in. Then make sure you read in any existing data when viewing the new_user action. Might also want to set up your routing layer to always redirect users in this state to the sign up page no matter where they are.
Related
I am using devise for user registrations. I set up a custom edit page '/info' to use as an additional signup page. The only problem is after you submit the edits on the info page it redirects to the user profile. I am able to change it redirect to the home page (where I want after /info) but then it also redirects there from the normal edit page. I am not sure how to redirect based on what page the user is on.
My one idea was to get the users current path and use an if statement but I haven't been able to get that to work.
registrations_controller.rb:
def update
...
current_uri = request.env['PATH_INFO']
if current_uri == '/info'
redirect_to root_path, notice: "Welcome"
else
redirect_to user_path(#user)
end
end
Normally it just looks like this:
def update
...
redirect_to user_path(#user)
end
The problem seems to be you need to keep data around to determine where to redirect. A few ideas:
You could store data in the session when a user visits a previous page and check this data in your action to determine where to redirect the user. The session is often a convenient place to store things that are consistent between requests but that should not be persisted to the database.
You could store data in the database (for instance, add a flag to your user model) and check this to determine where to redirect.
You could check request.referer and redirect based on the value there. This may not be as reliable.
I have implemented redirect_to_back method in my ApplicationController for users that are not signed in (from this blogpost:
def redirect_to_back_or_default(default = root_url)
if request.env["HTTP_REFERER"].present? and request.env["HTTP_REFERER"] != request.env["REQUEST_URI"]
redirect_to :back
else
redirect_to dashboard_url
end
end
and I'm using in sessions_controller#create:
if #user && #user.authenticate(params[:password])
sign_in(#user)
redirect_to_back_or_default(dashboard_url)
else
...
The problem is, this only works if the request is coming from the application and not if it is a direct link, like in an email.
Is there a solution for this?
I would recommend altering how you accomplish this. If you follow the flow below I think it will produce the results you're looking for:
User arrives at URL (either by clicking link in email or typing it or whatever) but is not yet authenticated.
Store the URL that the user is at in the session
Redirect to the sign-in controller/action
After authenticating the user look in the session for where you stored the arrival URL
If present redirect to that URL (and clear stored URL out of the session) otherwise redirect to dashboard_url (it won't be present if someone navigates directly to the sign-in controller/action).
Here is what I have for redirecting to a default url(myapp_url). But I want to change redirect to go the request url was entered by the user after authentication.
How do I do that? I tried couple of options from searching here, like :back. But no go.
User enters an url, if not authenticated then gets redirected to the login page, then after login user shall be redirected to the original request url.
def create
user = User.Authenticate(params[:user_id], params[:password])
if user
session[:user_id] = user.id
redirect_to myapp_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
end
You can read the chapter about "Friendly forwarding" in the "Ruby on Rails Tutorial" by Michael Hartl to see how you can easily implement it.
Basically you have 3 helper methods:
store_location to store the user desired location in the session
redirect_back_or(url) redirect the user to the location stored in the session or, if not set, to the location contained in the url method param
and clear_return_to used "internally" by the redirect_back_or method to clear this piece of information once used
And then you use these methods:
A) when you see a guest user try to access a page that needs authentication use store_location before redirect him to the login page.
B) when a user is logged in, you use redirect_back_or(url) to redirect him to the right location (if present of course)
This is an overview of how this work, you get the idea but I suggest to read that chapter for the implementation (few) details.
You need to save path in session before redirection on authentication, and after successful auth redirect to this path.
I am trying to force a user to login once they call this update action in my article controller (I am trying to work with gradual engagement) but once they login, I want to still call this action instead of halting.
def update
#article.attributes = params[:article]
#article.save
#store this article as a session variable
session[:pending_article] = #article.body
respond_with(#article, :location => article_url(#article))
end
Right now I am using a before_filter for the action that requires the user to login
def require_user
unless current_user
store_location
flash[:notice] = "You must be logged in to access this page"
redirect_to login_url
return false
end
end
However, I understand that before filters halt the original action once they redirect, so update never gets called. Basically, I want a user to be logged in to save an article but I want to save their work so I'm storing the article body in a session variable which I grab later. Is there a better way to require a user to login for an action but call it afterwards anyway?
In your require_user method you can do something like this:
session[:article] = params[:article]
Then in your login method (/sessions/create?) do this:
# this should take you back /articles/new,
# you may have to move your call to store_location
# or manually set session[:return_to]
redirect_back_or_default
Then in ArticlesController#new
def new
#article = Article.new(session[:article] || {})
end
Then the saved article params from the session are still there so the form is pre-filled out.
Be careful storing too much content in the session though. In Rails the default session store is a cookie, and cookies only hold about 4k of data. You may need to change your session store to pull this off.
I'm trying to write a simple OAuth consumer app in Rails. I'm using Authlogic for handling authentication and the Authlogic OAuth plugin to do the oauth thing.
The oauth plugin provides a couple of helpers to render the sign in button: oauth_login_button and oauth_register_button. Together with the Authlogic logics and the plugin's request filters these two buttons somehow create the session/user.
What happens next is as follows:
- if I use the oauth_login_button helper, then the session object fails to save as there's no such user locally.
- if I use the oauth_register_button helper, then, on any login after the first one, Rails complains that the token has been taken already... that means it can't create the second copy for the same user, which is right.
The issue is: I don't want to have BOTH Register AND Login buttons on my site.
On the user side, what I want to achieve is a single button on the start page, saying smth. like "Sign In with Twitter", which the user must click to proceed to inner pages of the site.
On the server side, I want to implicitly create the local user account, if the user is a first time visitor to my site.
Any hints on how to do this?
All the samples on Authlogic+OAuth I was able to find don't seem to care about having only a single button for sign in. :(
Seems like I'm going to answer the question myself.
I use the following code to generate the Sign In button (in HAML):
- form_tag({:controller => "users", :action => "create"}, {:method => "post"}) do
= oauth_register_button :value => "Sign In with Twitter"
and then I simply create the user's session object in the create method of the UsersController class, if the user already exists:
def create
#user = User.new(params[:user])
#user.save do |result| # LINE A
if result
flash[:notice] = "Account registered!"
redirect_to some_inner_path
else
unless #user.oauth_token.nil?
#user = User.find_by_oauth_token(#user.oauth_token)
unless #user.nil?
UserSession.create(#user)
flash.now[:message] = "Welcome back!"
redirect_to some_inner_path
else
redirect_back_or_default root_path
end
else
redirect_back_or_default root_path
end
end
end
end
If the user is a first time visitor, then the user object is successfully saved in the LINE A. And if it's not and there's an oauth token available, then we try to fetch the user from the DB and log him/her in.