Divjot Mehton
Divjot Mehton

Reputation: 47

A url issue when implementing a resend token feature

I will briefly explain the feature before I start explaining the issue. When a user signs up they get an activation token in their email which expires in 2 hours. I was trying to implement a feature which allows users to resend the activation token in their email.

The code for the resend activation token feature is given below.

Controller code in users controller

 def resend_verification_email
    @user = User.find_by(email: params[:resend_verification_email] [:email].downcase)
    if valid_email(params[:resend_verification_email] [:email])
      if !@user 
        redirect_to resend_verification_path
        flash[:danger] = "Email does not exist"
      elsif
        [email protected]?
        UserMailer.resend_activation(@user).deliver_now
        flash[:success] = "Check your email for the activation token"
        redirect_to resend_verification_path
      else
        redirect_to resend_verification_path
        flash[:success] = "User is already activated."             
      end
    else
      flash[:danger] = "Email format is Invalid"
      redirect_to resend_verification_path
    end
  end

Account activations controller

class AccountActivationsController < ApplicationController

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


end

The mailer controller method

def resend_activation(user)
    @user = user
    @userid = user.id
    mail to: user.email, subject: "Account activation"
  end

The mailer view

<%= link_to "Activate your account", edit_account_activation_url(id: @user.activation_token, email: @user.email) %>

I get the following error. No route matches {:action=>"edit", :controller=>"account_activations", :email=>"[email protected]", :id=>nil} missing required keys: [:id].

I understand what the error is saying. The url's first argument is the token and the way to grab is through params[:id] because in RESTful routes, id is always the first argument. The email is sent out fine if I get rid of the URL. The URL is throwing that error and I am not really sure why. Any help would be appreciated. Thanks!

I only included the relevant routes.

  get 'resend_verification'  => 'users#resend_verification'
  post 'resend_verification_email'  => 'users#resend_verification_email'

  resources :account_activations, only: [:edit]
  resources :password_resets,     only: [:new, :create, :edit, :update]

Relevant rake routes

edit_account_activation GET    /account_activations/:id/edit(.:format)                                          account_activations#edit

                    users GET    /users(.:format)                                                                 users#index
                          POST   /users(.:format)                                                                 users#create
                 new_user GET    /users/new(.:format)                                                             users#new
                edit_user GET    /users/:id/edit(.:format)                                                        users#edit
                     user GET    /users/:id(.:format)                                                             users#show
                          PATCH  /users/:id(.:format)                                                             users#update
                          PUT    /users/:id(.:format)                                                             users#update
                          DELETE /users/:id(.:format)                                                             users#destroy


resend_verification GET    /resend_verification(.:format)                                                   users#resend_verification
resend_verification_email POST   /resend_verification_email(.:format)                                             users#resend_verification_email

Upvotes: 0

Views: 86

Answers (3)

Timmy Von Heiss
Timmy Von Heiss

Reputation: 2218

:activation_token is created virtually as attr_accessor

This is not a routing issue.

@user.activation_token

is actually returning nil.

You will need to save activation_token to the user model if you wish to send further validation emails.

Upvotes: 0

Darpan Chhatravala
Darpan Chhatravala

Reputation: 520

Redefine your email link as follows:

<%= link_to "Activate your account", edit_account_activation_path( @user.activation_token, @user.email) %>

Or you can just pass the primary key of your object to your controller and ex: @user.id and use

before_action :set_user, only: [:edit]  

define private method inside of your controller for set user ex:

private 
  def set_user
    @user = User.find_by_id(params[:id])
    redirect_to xyz_path if @user.blank?
  end

Upvotes: 0

user988558
user988558

Reputation:

Well, I wouldn't define account_activations as a resourceful route.

The comment: #the token is actually available by params id explains why, imo id should refer to a model resource instead.

Here's my workaround for the issue:

  1. Define route as:

get "/account_activations/edit", to: "account_activations#edit", as: 'edit_account_activation'

  1. Define the activation link as follows:

<%= link_to "Activate your account", edit_account_activation_url(token: @user.activation_token, email: @user.email) %>

  1. Update controller:

class AccountActivationsController < ApplicationController def edit user = User.find_by(email: params[:email]) if user && !user.activated? && user.authenticated?(:activation, params[:token]) #... rest of code...

Alternatively, if you really wanna keep as it currently is, then try redefining you email link as follows:

<%= link_to "Activate your account", edit_account_activation_url(@user.activation_token, email: @user.email) %>

Then, params[:id] might refer to the token you're providing.

Upvotes: 1

Related Questions