Reputation: 1323
I have a requirement where user should be logged in, and should be landed in a specific page.
Basically user gets a link in his email, up on clicking of that link user will be logged in to system and landed in a specific page. Currently I am implementing this feature with the record ID(user id).
the url looks like this.
localhost:3000/details?user_id=412
def details
@user = User.find(user_id)
signin(@user)
end
because of the above url, anybody can randomly access the other user account by changing the ID. i want this id to be encrypted in url, and in controller action i want to decrypt it. i don't want this encryption through out the application i want to implement for only this action.
for example:
localhost:3000/details?user_id=SnrIcaMA8rikrnNX6RRKNw
def details
@user = User.find(decrypt(user_id))
signin(@user)
end
Upvotes: 2
Views: 2726
Reputation: 901
What you need is pretty simple. You're making this sound more complicated than it should.
You can achieve this in two ways. I actually use both in my app.
/user/1
use /user/560a56b2-627b-4c28-a685-8d18674060cb
. You can generate a UUID by using SecureRandom.uuid
user_controller
before_action :check_ownership
def check_ownership
redirect_to authenticated_root_path and return unless user == current_user || current_user.try(:admin)
end
Code is pretty self explanatory. It will redirect to root_path
if current_user
which has ID 1 is trying to access user/2
. If user is admin, it will allow him to see user/2
Upvotes: 0
Reputation: 102001
Rails 5 introduced has_secure_token
which like has_secure_password
is intended to improve security by removing the need to reinvent the wheel. This uses a token exactly like in Sergio Tulentsev's excellent answer but the token is generated by SecureRandom.base58
.
# Schema: User(token:string, auth_token:string) class User < ActiveRecord::Base has_secure_token has_secure_token :auth_token end user = User.new user.save user.token # => "pX27zsMN2ViQKta1bGfLmVJE" user.auth_token # => "77TMHrHJFvFDwodq8w7Ev2m7" user.regenerate_token # => true user.regenerate_auth_token # => true
To authenticate a user based on the token you can use:
@user = User.find_by!(token: params[:token])
If you are using Devise or just Warden you can create a token authentication strategy.
module Devise
module Strategies
class Token < Base
def valid?
params['token']
end
def authenticate!
u = User.find_by(token: params[:token])
if u
fail!("Invalid token")
else
success!(u)
end
end
end
end
end
Upvotes: 4
Reputation: 230336
No need to involve any encryption here.
The simplest solution would be to add a new field to users table, email_token
(or something) and set it to any random value you want. SecureRandom.hex
or SecureRandom.uuid
are good examples.
Then you just use this token to generate links, and not the user id. Your links will look like this:
example.com/details?token=d3fff15f2e1707719a2acbd4c1edd110
And in your controller you do something like
user = User.where(email_token: params[:token]).first
if user
# show page
else
# redirect to "not authorized" page
end
Good luck trying to guess another existing token.
One of advantages of this scheme over deriving some value from user id is that if some link/token gets posted to forums or something, you just change the compromised token, without affecting other users.
Upvotes: 7