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.
Related
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.
The RoR Security Guide states that you should "issue a new session identifier and declare the old one invalid after a successful login" using the reset_session method to counter session fixation.
I haven't been able to find any guidance on calling reset_session when using Authlogic. Is it simply a case of including the method in the controller method (as below)?
I'm just concerned about causing problems for Authlogic as I can see both user_credentials and user_credentials_id keys and values in the session hash prior to calling reset_session.
class UserSessionsController < ApplicationController
def create
#user_session = current_client.user_sessions.new(params[:user_session])
if #user_session.save
reset_session
flash[:success] = I18n.t(:msg_login_success)
redirect_back_or_default application_root_path
else
render :action => :new
end
end
For reference this is my current method:
def create
#user_session = current_client.user_sessions.new(params[:user_session])
if #user_session.save
# reset session to counter session fixation
# whilst retaining values except for those that the application has created specific to the current user
temp_session = session.merge("current_user" => {}).clone
reset_session
session.reverse_merge!(temp_session)
# set flash msg and redirect
flash[:success] = I18n.t(:msg_login_success)
redirect_back_or_default application_root_path
else
render :action => :new
end
end
With the call to reset_session still performed after a successful login as per the recommendation in http://guides.rubyonrails.org/security.html#session-fixation-countermeasures
yeah, resetting the session AFTER you log the user in (which is what looks like happening?) is definitely not right. You want to do it BEFORE you log the user in.
Ideally you'd want to do it before you log the user in but only if the login is actually going to be succesful -- but I'm not sure if you can get auth_logic to do that, I'm not very experienced with auth_logic, although it's a REALLY good question for auth_logic, if I were you I'd file it as a support ticket with auth_logic.
But in the meantime, you might want to just try putting the reset_session at the top of the action method, before #user_session = current_client.user_sessions.new(params[:user_session]). I think this will work, and at worse reset the session in some cases where you really didn't have to (if the user's credentials were invalid), but I don't think that will cause a serious problem. (uh-oh, unless it causes you to lose your validation errors?)
But again, not an auth_logic expert here. I don't expect you to accept this answer since I don't have the expertise to really answer it, just sharing what I think in case it helps you and gives you some pointers as to how to think about it.
I'm a beginner programmer and am working on an email confirmation. Once users have registered it sends them to a page to enter a confirmation code, which will be sent to the email they used. If submitted correctly it saves the User and logs them in.
I had looked into using Devise and maybe that's the way to go, but it seems I would spend as much time learning someone else's code when I could learn to do it myself. My repo is Here.
What I was thinking of coding (in my User controller) is shaping up like this...what do you think? Am I completely off and would be better figuring out Devise/Authlogic or am I on the right track? I'm using rails 3.1. Any help would be much appreciated. Thanks in advance.
def confirmation_code_to_register(string)
#confirmation_code = #random number
end
def create_start
#user = User.new(params[:user])
#send email with #confirmation_code via ActionMailer
redirect_to page_to_enter_confirmation_code
email_authenticate
end
def email_authenticate
if #confirmation code user enters == #confirmation_code
create_finish
else
#sorry, you entered the wrong confirmation code.
end
end
def create_finish
if #user.save
sign_in #user
flash[:success] = "Welcome to the Site"
redirect_to #user
else
#title = "Sign Up"
render 'new'
end
end
Consider using Sorcery instead of Devise. There is Railscast about it.
https://github.com/NoamB/sorcery
It more simpler to use, and I think it fulfill your authentication needs.
PD: Check the Edit I did to your post. Try to use that syntax when you post code (just indent the code, no html markup)
EDIT:
And that code should be in the User model, not the controller. Try to keep your controller small, very small. Put all the logic you can in the models.
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.
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.