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.
Related
Ive read plenty of questions asking this exact question, but havent gotten a great answer.
Right now a user goes to the site, and enters their email in. After they click submit I would like to clear their session/log them out so that they could enter in another email address, so it will save to the DB.
My hacky solution was to call the reset session in my user controller after a user was saved. This did not work.
I then proceeded to call the sign_out method provided via devise on user save, but that did not work.
Does anyone have any thoughts/solutions?
user_controller.rb
def create
#user = User.new(user_params)
#if #user.save
#reset_session
#sign_out :user
#flash: 'User was successfully created.'
#User.find(user_params).forget_me!
#else
#render action: 'new'
#end
end
I did this using the following code
reset_session
#current_user = nil
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.
How can I implement PRG in Rails?
I used PRG in Rails, but I am not totally convinced it's right. I was wondering is there any better way to handle it in Rails?
I don't know how popular PRG pattern is and why one has to religiously stick to the "redirect" on failure aspect of it (actually, one good reason is sometimes you dont want to deal with the "setup" complexity at create failure and keep things dry).
What you basically need is to transfer the params for :user to new. I think #Hitesh's solution above is quite close.
class UsersController < ApplicationController
def new
if flash[:user_params]
#user = User.new(flash[:user_params])
#user.valid?
else
#user = User.new
end
end
def create
#user = User.new(params[:user])
if #user.save
# clears previously stored user if there is any
flash[:notice] = "User created."
redirect_to '/'
else
flash[:error] = "Error saving User"
flash[:user_params] = params[:user]
redirect_to :action => :new
end
end
end
Use the session, Luke
The way you implemented it in your blog post is quite fine, however you may want to use session instead of flash to store your #user and optionally use the ActiveRecord session store to keep cookies from getting bloated.
From ActionController::Base documentation
ActiveRecord::SessionStore - Sessions are stored in your database, which works better than PStore with multiple app servers and, unlike CookieStore, hides your session contents from the user. To use ActiveRecord::SessionStore, set
config.action_controller.session_store = :active_record_store
in your config/environment.rb and run rake db:sessions:create.
So you should…
class UsersController < ApplicationController
def new
#user = session[:user] || User.new
end
def create
#user = User.new(params[:user])
if #user.save
# clears previously stored user if there is any
session[:user] = nil
redirect_to '/'
else
session[:user] = #user
redirect_to :action => :new
end
end
end
I'm no expert in these matters, but this looks good. From what I understand flash is a part of the session. So the answers telling you to switch to session seem a bit misguided. In this case you want the data to be cleared after the redirect. Other than shoving it in the session, I'm not sure where you would put it.
As far as your cookie size increasing, well, the default session provider for Rails is a cookie in Rails 3. You could swap the session provider out if you wanted to keep the data server side. It is encrypted though, so you are probably okay with the data in the cookie, unless size is an issue.
use below code
class UsersController < ApplicationController
def new
#user = User.new(session[:user_param])
session[:user_param]=nil
end
def create
#user = User.new(params[:user])
if #user.save
# clears previously stored user if there is any
flash.discard(:user)
redirect_to '/'
else
session[:user_param] = #user
redirect_to :action => :new
end
end
end
It is true, though, that you should not do redirect_to '/'. You should define root in your routes file and then do redirect_to root_path.
Edit: Oops, that was supposed to be a comment to SpyrosP's answer.
Also: Here is some excellence guidance on flash. Particularly this may ease your mind:
The flash is a special part of the session which is cleared with each request. This means that values stored there will only be available in the next request, which is useful for storing error messages etc.
The interesting things there is that, yes it is a part of the session, so answers to "use the session instead of flash" are misguided, as Justin Etheredge's answer already put it. The other thing is that it says it is useful for storing messages instead of only for storing messages. With the added "etc" it would lead me to believe that it is within the intended usage to store user information in there as well.
One last thing, I would agree with Aditya Sanghi that you should just store the user parameters and not an entire user object in the flash.
I didn't read the question properly.
The validation failure you have necessitates going to a different page where a different process will occur. You tried to update a domain object, it doesn't exist. The usual response to a validation failure is to re-render the page, but you need to go to the create page.
The flash hash seems wrong for this. I'd agree with the idea of stuffing your entered data into the session and redirecting.
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'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.