Implicit user creation with Authlogic and Authlogic OAuth plugin - ruby-on-rails

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.

Related

Verifying user is authorized after using redirect_back

I have a method that creates a new user session and will redirect back to the previous screen after successful login. It has introduced a security issue where someone can type in a url navigate to a page they shouldn't have access to. The app will direct them to login, but after logging with valid credentials (just not the proper level of clearance) it will redirect them to the page they had manually typed into the url. How can I verify that redirect_back isn't sending a user to a page they shouldn't have access to?
Here is the session create method:
def create
#user = authenticate(params)
sign_in(#user) do |status|
if status.success?
redirect_back root_path
else
flash.now.alert = status.failure_message
render :new, status: :unauthorized
end
end
end
Is there a way to see what address it will be sending them back to because I could just do something like
if back_url.includes? "admin"
redirect_to root_path
end
You can’t rely on URL obscurity for security of your application. Your admin routes or controllers should be protected so they can only be accessed by signed in admins.
You can do this with a route constraint in your routes file (my preference) or a before action that is consistently applied across all of your admin controllers that returns a 403 if the current user is not an admin.

Passing a long string for a param value

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.

ruby rails - redirect to original request url

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.

Rails Way to Restrict Page Access Without Sessions

Context
I'm building a super simple, knock-your-socks-off sexy sign-up page at http://hivechatter.com. (Yes, I feel strongly about her.)
The root page is the new user action, where I ask for email only. If the visitor submits a valid email, a new user is created and I redirect to that user's edit page and ask for additional, optional info.
Problem
The edit page url is of the usual form: http://hivechatter.com/users/19/edit. One can visit any user's edit page by simply visiting this url with whichever id number they choose.
Question
How do I restrict access to the edit user page so that it can only be visited once, and only immediately after having created that user_id from the root new user page?
I can think of a variety of methods to explore. I'd appreciate a pointer on the most elegant, rails way to do this. Note that I don't need any additional functionality like sessions, etc. This two step sign-up process is the extent of what I need right now.
Thanks!
Add new column to your users table. Let it be opened_once:boolean with DEFAULT false
Then in your users_controller
def edit
#user = User.find( params[:id], :conditions => ['opened_once => ?', false] ) rescue ActiveRecord::RecordNotFound
#user.update_attribute :opened_once, true
...
end
so now it can be showed only once right after creating new user when you redirect to edit page
UPD
What you can do more Rails way? Without adding new stuff to your database and so on. You can remove your edit action at all, so your edit view will rendered at create:
def create
#user = User.new params[:user]
respond_to do |format|
if #user.save
format.html{ render :action => :edit }
else
format.html{ render :action => :new }
end
end
end
User will see edit form only once if validation passed and his profile created.
So this is specific "Rails way" :)
The point of a cookie is to maintain state in the form of a session. HTTP by spec is stateless, and there for if you have people logging in then they need a session. RoR has a great session handler, I recommend using it.
The only other way to restrict access would be using a .htaccess file or similar method of doing basic-auth. This doesn't scale well and is less secure.

Create a new user with new openid login authlogic

I've implemented authlogic in a rails site, and I'm trying to get openid to work correctly. So far, you can login just fine as long as you have an existing account, but not so much if you don't. I'd like to be able to automagically create a new account if the identity_url is not already in the database.
The problem is that I also need to store some additional info. if the user is logging in for the first time with their openid, I'd like to ask them to fill in basic info (name, email), BEFORE the account is created.
I've played around with a few methods, but nothing seems to be working.
Thanks in advance for any input!
acts_as_authentic do |c|
c.openid_required_fields = [:email,"http://axschema.org/contact/email"]
end
Will allow you to require an email. I'm unsure of how to require other fields, but maybe check that axschema.org page. There is no need for the user to fill anything out other than their OpenID provider URL.
Combining login and registration could be done with something like this (untested create method from UserSessions controller, like from the authlogic tutorial stuff)
def create
if User.find_by_openid_provider(params[:user_session]).nil? # or something like that
#user = User.new(params[:user_session])
if #user.save
redirect_to whatever_path
else
# handle error
end
else
#user_session = UserSession.new(params[:user_session])
if #user_session.save
add_message 'Login successful!'
redirect_to whatever_path
else
render :action => :new
end
end
end
Maybe try putting the additional information into a temp table of some kind, and keep track of the session the user is in. Once they have authenticated, connect the previously entered information with the OpenID information to create the real user.

Resources