Login a User via email - Devise - ruby-on-rails

Within my application a User is created after a successful transaction
def create_real_user
return unless current_or_guest_user.is_guest?
generated_password = Devise.friendly_token.first(8)
#user = User.new(
is_guest: false,
first_name: params[:first_name],
last_name: params[:last_name],
email: params[:email],
password: generated_password,
password_confirmation: generated_password,
braintree_id: #result.transaction.customer_details.id
)
#user.save(validate: false)
RegistrationMailer.welcome(#user, generated_password).deliver_now
end
And as you can see an email is sent out and it advises that a password has been set for them but if they wish to change it then visit a link
Your current password is <%= #password %> but if you would like to change it then please visit <%= link_to 'Change my password', edit_user_registration_path(#user.id) %>
So when clicking this i get to the login screen but i would like to have the user automatically signed in and taken straight to the page where they can edit their password.
How would i go about this?
Thanks

There isn't a quick fix for this but it's possible.
Include a route for this special login case...
resources :users do
member do
get 'token_link'
end
end
This gives you a new helper method `token_link_user_path
You would need to create a token field in your User table when you create the record, and set it to some random value via a before_create
class User
before_create :generate_token
def generate_token
user.token = SecureRandom.urlsafe_base64(nil, false)
end
...
end
In your email include the link...
link_to 'access your account', token_link_user_path(#user.token)
In your User controller...
def token_link
#user = User.find_by(token: params[:id])
#user = nil if #user && #user.created_at < Time.now - 1.day
unless #user
flash[:error] = 'Invalid log in'
redirect_to root_path
end
#user.update_attribute(:token, nil)
... do here any processing, renders, or redirects you'd like
end
Note how we wipe out the token_link after it's been used, to prevent it from beign used twice. Along the same lines, we check that it's not older than a day since the record's been created.

Related

Rails user_authentication is not working, find method always returns nil

I can't seem to get my head around, this, I've been trying it for hours and it still doesn't seem to work, even though I've followed the tutorials available properly. I'm basically creating my own login validation with its own session controller. It has a form for a login and then some authentication steps. I'm using bcrypt to create hashes for passwords as well, I don't know if this is doing anything, or not, I suspect it isn't but still.
Here is the form I use:
- provide(:title, "Sign in")
%h1 Log in
.row
.col-md-6.col-md-offset-3
= form_for signin_path do |f|
= f.text_field :email, placeholder: 'Email'
= f.password_field :password, placeholder: 'Password', required: true
= f.submit "Sign in"
%p
New user? #{link_to "Create New Account", new_user_path}
And here's my sessions controller:
class SessionsController < ApplicationController
require 'bcrypt'
def new
end
def create
#google_user = GoogleUser.from_omniauth(env["omniauth.auth"])
#session[:google_user_id] = google_user.id
#redirect_to '/users'
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to user, notice: 'Logged In'
else
render :new
end
end
def destroy
#session[:google_user_id] = nil
session[:user_id] = nil
redirect_to root_path, notice: 'Logged Out'
end
end
Ignore the google stuff, that's something that I will have to handle later, as it would seem.
I'm also not allowed to use any such as devise, or any of the others that make this process easier. I'd appreciate if someone could check my code and tell me if there's something wrong, because I certainly don't know what else to do with this.
Query parameters:
SELECT users.* FROM users WHERE users.email = 'email'
The problem with that query is that it is not taking the proper email, when I type, "email#gmail.com" it doesn't get that, it just grabs the symbol 'email'
I believe you have a mistake in your code:
user = User.find_by_email(params[:password])
should be:
user = User.find_by_email(params[:email])
I think you have to change:
user = User.find_by_email(params[:password])
into
user = User.find_by_email(params[:email])
change
user = User.find_by_email(params[:password])
to
user = User.where(email: params[:email]).take

Access current_user in model (or some other workaround)

I've read several SO links on this topic. Even if you can hack it to get current_user in model, you shouldn't do it. So, what are my options in my case?
I'm using the devise_invitable gem, and one of the commands is User.invite!({:email => email}, current_user), which stores who the user is invited by (current_user). I'd like to have this information.
Currently, users are invited to join a private group, and this process is handled in my group.rb model:
# group.rb
def user_emails
end
def user_emails=(emails_string)
emails_string = emails_string.split(%r{,\s*})
emails_string.each do |email|
user = User.find_for_authentication(email: email)
if user
self.add user
GroupMailer.welcome_email(user)
else
User.invite!(email: email) # But I want this: User.invite!({:email => email}, current_user)
user = User.order('created_at ASC').last
self.add user
end
end
end
If relevant, it's just a text_area that receives these emails to process:
# groups/_form.html.erb
<%= f.text_area :user_emails, rows: 4, placeholder: 'Enter email addresses here, separated by comma', class: 'form-control' %>
Without having to re-arrange too much, how can I run User.invite!({:email => email}, current_user) in this process, so that this useful information (who is invited by whom) is stored in my database? Much thanks!
Update:
With #Mohamad's help below, I got it working.
# group.rb
def emails
end
def invite_many(emails, inviter)
emails.split(%r{,\s*}).each do |email|
if user = User.find_for_authentication(email: email)
add user
GroupMailer.group_invite user
else
add User.invite!({:email => email}, inviter)
end
end
end
# groups_controller.rb
def update
#group = Group.friendly.find(params[:id])
if #group.update_attributes(group_params)
emails = params[:group][:emails]
#group.invite_many(emails, current_user) # also put this in #create
redirect_to #group
else
flash[:error] = "Error saving group. Please try again."
render :edit
end
end
And then nothing in my User model because User.invite is defined already by devise_invitable and I didn't need to do anything else. This process is working great now!
There are some subtle issues with your code. There's a potential race condition on the else branch of your code where you try to add the last created user. I'm also unsure that you need a setter method here unless you are access emails from elsewhere in the instance of Group.
As suggested by others, pass the current user as an argument form the controller. I'm not sure how invite! is implemented, but assuming it returns a user, you can refactor your code considerably.
I would do somethng like this:
def invite_many(emails, inviter)
emails.split(%r{,\s*}).each do |email|
if user = User.find_for_authentication(email: email)
add user
GroupMailer.welcome_email user
else
add User.invite!(email, inviter)
end
end
end
# controller
#group.invite_many(emails, current_user)
# User.invite
def invite(email, inviter)
# create and return the user here, and what else is necessary
end
If you are calling user_emails() from the controller (and I'm guessing you are as that must be where you are receiving the form to pass in emails_string), you can pass in the current_user:
user_emails(emails_string, current_user)
and change user_emails to receive it:
def user_emails=(emails_string, current_user)
You can store the current_user with global scope ,like #current_user,which can be assigned in sessions controller,so in model you will just #current_user as the current user of the app.

Email Sign Up Confirmation with ActionMailer

Currently I have ActionMailer send an email when a user registers, and I generate a random :sign_in_token with the user.
How can a user then click on the link sent to his email and update the users :registration_complete boolean value to TRUE?
Currently, I am able to send the link and generates a random token, but I don't know how to update the boolean value through the email.
MODELS
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :sign_in_token,
:registration_complete
###This generates my sign_in_token
def generate_sign_in_token
self.sign_in_token = Digest::SHA1.hexdigest([Time.now, rand].join)
end
end
CONTROLLER
def create
#user = RegularUser.new(params[:regular_user])
if #user.save
###Sends the User an email with sign_in_token
UserMailer.registration_confirmation(#user, login_url+"/#{#user.sign_in_token}").deliver
flash[:success] = "Please Check Your Email to Verify your Registration!"
redirect_to (verifyemail_path)
else
render 'new'
end
end
USER_MAILER
def registration_confirmation(user, login_url)
#login_url = login_url
#user = user
mail(:to => "#{user.name} <#{user.email}>", :subject => "Welcome to APP")
end
VIEWS
###Redirects User to Login Page, But how do i connect it to my activate method?
<%= link_to "Complete Registration", #login_url %>
ROUTES
match '/login/:sign_in_token', :to => 'sessions#new'
When they click a link, it takes them to a controller with an action of set_complete using a GET request, which sets the boolean value.
Something like:
def set_complete
user = User.find(params[:user])
user.update_attribute(registration_complete => true)
redirect_to login_path # or whatever your login url is...
end
For the controller action and something like this for the link:
<a href="example.com/registrations/set_complete?user=1" />
Here is a sample of what might go in the routes file:
get "/users/set_complete", :to => "users#set_complete"
You'd probably need to set the user id to whatever you want using erb, andmake a few other app-specific customizations, but this is the general idea.
Hope this helps!

Unlock_instructions email

i am using the devise for my User account where i will get all the confirmation, user password instructions etc;
But i am having a condition where if user account is locked (where status is false in my scenario), he has to get a reset password link through email. This locking process is coded in other controller so we cannot use devise helpers.
my controller code:
def send_instruction
a=[]
if #answer1
a << '1'
end
if #answer2
a << '2'
end
if #answer3
a << '3'
end
if a.size <= 1
SiteMailer.unlock_user(current_user).deliver
current_user.update_attribute(:status,false)
destroy_user_session_path(current_user)
flash[:error]= "Your account is locked"
redirect_to new_user_session_path
else
redirect_to user_dashboard_path
end
mailers/site_mailer.rb:
class SiteMailer < ActionMailer::Base
include Devise::Mailers::Helpers
default from: "support#example.com"
def unlock_user(user)
#user = user
#url = "pwd_edit"
mail(to: #user.email, subject: 'Your account has been locked')
end
end
in mailer view:
Hi,
Your account with has been locked. Edit Profile:
"My link (edit_user_password_url(current_user)".
When i am going through this process, I am getting "No routes found" error. And my routes are correct. Please help me out.
That's because no current_user is present at that time. You should try this instead
edit_user_password_url(#user)

How to let user activate account through devise confirmations email link locally

I am trying to confirm user accounts in test mode while using devise and rails. Here's the scenario. I changed the devise confirmation email that is sent into a partial that is rendered after a user has successfully signed up. Problem is it shows the following error "undefined method email". Here's my confirmation partial.Welcome <%= #resource.email %>!
You can confirm your account through the link below:
<%= link_to 'Confirm my account', confirmation_url(#resource, :confirmation_token => #resource.confirmation_token) %>. And below is my application_helper in order to access the resource details. def resource_name
:user
end
def resource
#resource ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end</pre>
Is #resource actually the user in application_helper? I know if it is nil it will be assigned a new User, but what is it before it assigns it?
I suspect #resource isn't a User, but something else (a something that doesn't have an email field).

Resources