1

I am using Hartl's Tutorial for Account Activation

user.rb
attr_accessor :activation_token

def User.digest(string)
  cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                              BCrypt::Engine.cost
  BCrypt::Password.create(string, cost: cost)
end    

# Returns a random token.
def User.new_token
  SecureRandom.urlsafe_base64
end

def send_activation_email
    UserMailer.account_activation(self).deliver_now
end

def create_activation_digest
  self.activation_token  = User.new_token
  self.activation_digest = User.digest(activation_token)
end

Once the update action shown below in my UsersController is complete and the user is redirected to the root_url

def update
  @user = User.find(params[:id])
    ...
    elsif !params[:user][:email].blank?
      if @user.authenticate(params[:user][:current_password])
        @user.update_attributes(email_user_params)
        if @user.save
          @user.create_activation_digest
          @user.deactivated
          @user.send_activation_email
          log_out
          flash[:info] = "Please check email dude"
          redirect_to root_url
        else
          flash[:danger] = "Email Update Failed"
          redirect_to edit_user_email_path(@user)
        end
     else
       flash[:danger] = "Current Password Is Incorrect"
       redirect_to edit_user_email_path(@user)
     end
   ...

def edit
  @user = User.find(params[:id])
end    

then:

:activation_token = nil.

Is that correct?

I am asking because there are a bunch of topics on the subject of allowing the user to request a second validation email in a separate controller action and in all of those topics the discussion is stuck on routing issues, because in the email that is sent, the :activation_token is used as :id and the error message comes up :id -> nil

Edit:

class AccountActivationsController < ApplicationController

def edit
  user = User.find_by(email: params[:email])
    if user && !user.activated? && user.authenticated?(:activation, params[:id])
      user.activate
      log_in user
      flash[:success] = "Account activated!"
      redirect_to user
    else
      flash[:danger] = "Invalid activation link"
      redirect_to root_url
    end
  end
end

 user.rb
 def authenticated?(attribute, token)
    digest = send("#{attribute}_digest")
    return false if digest.nil?
    BCrypt::Password.new(digest).is_password?(token)
 end
Community
  • 1
  • 1
Timmy Von Heiss
  • 2,160
  • 17
  • 39
  • Can you show the `edit` user controller action? – C dot StrifeVII Oct 21 '16 at 19:50
  • I added it to the question. Thanks. – Timmy Von Heiss Oct 21 '16 at 19:56
  • actually I am sorry I meant the actions that this goes to `edit_user_email_path` – C dot StrifeVII Oct 21 '16 at 20:02
  • I added it. But the first email gets sent correctly. The problem is that we want to setup a way for the user to request a **second validation email**. When we try to set this up, for example, `ResendController` we get told that `:id => nil` because the mailer uses `@user.activation_token` for `:id`. What I am trying to confirm is whether or not `@user.activation_token = nil` the moment we complete the `edit/create` actions... thus making it impossible to send another validation email. – Timmy Von Heiss Oct 21 '16 at 20:07
  • for example, in another topic someone wrote `attr_accessor can be used for values you don't want to store in the database directly and that will only exist for the life of the object (e.g. passwords).` which confirms my suspicions that we will not be able to send a second validation email from another controller action because `@user.activation_token = nil` after the first email is sent and we are redirected. http://stackoverflow.com/questions/2793098/usage-of-attr-accessor-in-rails – Timmy Von Heiss Oct 21 '16 at 20:11
  • Yea that is right if at any point of time you are reloading that object from the database that value will be nil because its a virtual attribute not a real one. You might be better off just adding that to you user table and just updating it when it changes. I will extract this to an answer. I meant to ask you if that was an attribute in my first comment. – C dot StrifeVII Oct 21 '16 at 20:14

2 Answers2

0

So it appears that since you are using a virtual attribute to store that token at some point in time that object is leaving scope and being reloaded or some such even that is cause you to loose context. The best bet is just to ad a column to your User table to persist it between contexts and just update it where appropriate.

C dot StrifeVII
  • 1,885
  • 1
  • 16
  • 21
  • Are there any security issue with storing activation_token in the user model? – Timmy Von Heiss Oct 21 '16 at 20:18
  • 1
    if the toke is just used to activate an account there is not much risk in storing it in the data base. I don't know if you are familiar with devise but it stores password reset tokens in the database on the user model. If "they" are already that far in to your database that the can execute structured queries and have the understanding of your app to reconstruct your activation links you have bigger issue to worry about. – C dot StrifeVII Oct 21 '16 at 20:23
0

I also met this problem. Even though I removed "redirect_to root_url", after the code runs into a different action (in the same controller), the attr_accessor still becomes nil.

Thus my conclusion is that the lifetime of an attr_accessor only exists within the same action.

Jade
  • 1