Just learning Rails via Michael Hartl's tutorial and one of the things we have to do is implement basic authentication with sessions instead of cookies.
I am trying to find any literature online that discusses it, but can't find anything.
The Rails Guides talk about sessions from a security point of view, so they assume you have your authentication working and everything - and are just interested in securing it.
But I would like to roll my own from scratch - a very simple version, nothing fancy at all.
Can someone explain to me, how a basic authentication system would work/look like in Rails 3 or show me some articles and stuff that explain how to roll my own.
Again, doesn't have to be fancy, I just want to understand how they work.
Also, assume that a User model has been created, and user data is stored in a db. So it's just a matter of confirming that there was a successful sign in, and showing them different content.
Thanks.
I figured it out, basically in my sessions controller I did this:
class SessionsController < ApplicationController
def create
user = User.authenticate(params[:session][:email], params[:session][:password])
if user.nil?
flash.now[:error] = "Invalid email/password combination."
render 'new'
else
session[:user_id] = user.id
redirect_to user
end
end
def destroy
session[:user_id] = nil
redirect_to root_path
end
end
Related
Hi clever programmers,
I've been searching and reading a couple days, but I need some Rails help-
Here is my goal: I want to make a multi-page profile for each user, and I'd like to handle this profile with a profile controller.
The problem? How do I make the 'show' REST action apply to an entire controller instead of just one page? Specifically, how can I have both a #current_user and a #user variable available in the Profile controller that correspond to the signed-in user and the current user's page.
I'm not sure if I should be making routes with multiple :id s in the route or if there is some way to persist the signed-in-user in something like #current_user when they sign in that is just available everywhere and then I would use the :id of the user who's profile it is in the route. I'm pretty sure facebook does something like facebook.com/{your_id}/{their_id}/ for example.
I tried accessing #current_user from my session_helper.rb class but it came up nil and I'm not sure how to pass the :id to use User.find(params[:id]) because the profile controller is not affiliated with the resource for the User model.
Any protips or links to helpful readings would be much appreciated. I'm a beginner so feel free to suggest a better course of action if I'm going against the rails way. Thanks in advance!
You may not know but you can store session data, and user_id it's really common thing that people saves on it.
So for saving at the sign in
session[user_id] = ....
Then you could have something like this on a helper
def current_user
User.find(session[user_id]) if session[user_id]
end
You should check the gem called devise. It provides all the functionality for aunthentication and it also provides a current_user method everywhere. You should check it at least to see how they have implemented that method.
I am using the devise, omniauth, omniauth-twitter and twitter gems in a rails 3 app. I want to make it so when a user signs out it also removes the twitter gem configuration. What I'm referring to when I say "twitter gem configuration" is this:
Twitter.configure do |config|
config.consumer_key = YOUR_CONSUMER_KEY
config.consumer_secret = YOUR_CONSUMER_SECRET
config.oauth_token = YOUR_OAUTH_TOKEN
config.oauth_token_secret = YOUR_OAUTH_TOKEN_SECRET
end
If I don't do that and another user logs onto the app from the same computer but doesn't have a user account, they will see the previous user's twitter information. I believe I can remove the configuration by calling
Twitter.reset
I guess my question is where would be the best place to put that? Also if that isn't the best way to remove the user's twitter configuration, how should I do it?
Thanks and let me know if you need any more details.
You can split your configuration into application-specific (which will be global) and client-specific settings. This twitter gem wiki page describes this nicely.
This seems to be working.
In /app/controllers/application_controller.rb you can redirect the default route devise sends the user when they sign_in/out. I'm not sure if this is the "proper" place to put this but it seems reasonable.
class ApplicationController < ActionController::Base
protect_from_forgery
def after_sign_in_path_for(resource_or_scope)
if current_user.authentications.find_by_provider("twitter")
ckey= YOUR_APPS_CONSUMER_KEY
csecret= YOUR_APPS_CONSUMER_SECRET
auth = current_user.authentications.find_by_provider("twitter")
atoken = auth.token
asecret = auth.secret
Twitter.configure do |config|
config.consumer_key = ckey
config.consumer_secret = csecret
config.oauth_token = atoken
config.oauth_token_secret = asecret
end
end
authentications_path
end
def after_sign_out_path_for(resource_or_scope)
Twitter.reset
authentications_path
end
end
The authentications_path is just the page I'm using to test the authentications and related things. You can redirect anywhere. When a user links an account I save their oauth token and secret in the authentication object. You will need this to access certain aspects of the Twitter gem.
I'll wait a little while to mark this as the answer, see if someone has a better solution.
I am using "check authorization" in the application controller so every action will require a permission. I'm starting with giving me, the superadmin :=], permissions to manage all. I thought manage all would give me access to the whole app without naming a resource.
user model:
def role?(role)
roles.include? role.to_s
end
application controller:
check_authorization
cancan's ability model:
def initialize(user)
if user.role? :superadmin
can :manage, :all
end
end
error message:
This action failed the check_authorization because it does not authorize_resource. Add skip_authorization_check to bypass this check.
Thank you.
As far as I am aware, you're going to need to call authorize_resource in your controller as a before filter so that this works.
Using Rails 3.0.6, Omniauth 0.2.0 and Devise 1.2.1, I'm encountering the following situation:
I want to offer users the option to authenticate via Facebook. I have a user system set up using Devise and I can successfully auth using Facebook. I've spent several hours trying to code the behavior I want for one specific situation:
user is not logged in
user has a site account
user authenticates via Facebook
I offer the user 2 choices at this point
create an account (can be a dummy account with no provided info)
link this Facebook authentication with an existing account
I'm having trouble with the latter option. The user has already authenticated but I still need him to log in with his site account. I have an action in my AuthenticationsController that will associate this authentication with a logged in user. Devise doesn't seem to offer a way for me to log the user in while staying in the same action, though. This was my first attempt to do this
class AuthenticationsController < ApplicationController
before_filter :authenticate_user!, :only => :auth_link_existing_user
...
def auth_link_existing_user
...
end
However, using this method, if the user logs in, they're simply redirected to my site's root page. I know I can change Devise's sign-in redirect, but that will be for all sign-ins. I wanted only this situation to have a separate redirect.
After reading through this mailing list question, I tried to extend SessionsController with my own custom behavior:
def create
resource = warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#new")
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
if params[:redirect] #new
redirect_to params[:redirect].to_sym #new
else
respond_with resource, :location => redirect_location(resource_name, resource)
end
end
This doesn't work either. I've defined my auth_link_existing_user route to use a POST verb (which seems accurate) and redirects can only be GETs.
So now I do have a solution in mind: copy and paste code from Devise's authenticate_user! helper into a new function which can be called within a controller action without redirecting. This seems less than ideal to me because it's duplication of code and increases coupling--a Devise or Warden update that changes this behavior will break my code as well.
Has anyone else tried something like this and come up with a more elegant solution? Do you see a simpler way for me to offer this or similar behavior to my users?
UPDATE: For anyone who wants to use my dirty solution at the end, this is what I did:
def auth_link_existing_user
# FROM Devise/sessions/create
resource = warden.authenticate!(:scope => :user, :recall => "registrations#auth_new")
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(:user, resource)
# method defined in Ryan Bates' Railscast for Omniauth w/Devise
current_user.apply_omniauth(session[:omniauth])
current_user.save
end
note that this action MUST be placed in your sessions controller. If not, Warden will give you an "invalid email/password" error. It was an incredibly long debugging process to find the source.
With this in place, I use a login form to submit to this action after the user has authenticated.
I like how clean your solution is, though it goes deeper into the stack.
Here is how I've implemented something similar by following the Devise+Omniauth Facebook example on the Devise wiki and modifying the facebook method to pass on the session information to the Login form, with something like this:
if #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
sign_in_and_redirect #user, :event => :authentication
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_session_url
end
Then, in your case, I'd check in Login controller action for session["devise.facebook_data"], submit the uid + token with the form and apply_omniauth if present.
I'm a newb with Rails and am trying to get out my first Rails 3 app with Devise. I have a situation where pretty much everything on my site is specific to a user. I'm creating a kind of private gallery that isn't public facing, so that every image belongs_to a user and a user has_many images. Here's my issue... I want to rework my routes so that showing a users images doesn't require a user id in the URL. I will be showing a user's images in the user/show route.
current route (from rake:routes):
/users/show(.:format) {:action=>"show", :controller=>"users"}
Is it possible to have devise use only "resource" instead of "resources"? so it would be /users/show/? Am I making sense? I am not sure the terminology to ask, but I want all of a user's functionality to imply that I know who the user is, then I can check that in the controllers.
Thanks!
In your controller, for the show action:
def show
#user = current_user
end
That's all you need to do. If you were EXPECTING an id, then you do something like #user = User.find(params[:id], but since you know what user you want (in this case, the current_user, which is exposed by Devise), you don't need to do anything special.