ruby-on-railsruby-on-rails-3devisewardendevise-confirmable

Allow unconfirmed users to access certain pages which require authentication


I use the Rails Stack with

Now I have a certain requirement related to email confirmation and access provision to unverified users. Let's say there are 3 categories of pages:

  1. case 1 - requires no authentication.
  2. case 2 - requires authentication and also require the user to be confirmed.
  3. case 3 - requires authentication (correct username and password combination) but user need not be confirmed to access them.

With devise and confirmable, implementing case 1 and case 2 is a breeze. Once the user does login/signup, I redirect to "confirm your email page".

My problem is with case 3. Suppose the user does his login/signup. He is yet to confirm his email address but should be allowed to visit certain case 3 routes. When he visits a case 3 routes, I expect:

Devise with confirmable either allows all the pages to be visited by confirmed users or none. It does not allow access to certain pages with authentication but without confirmation.

I tried overriding the devise confirmed? by implementing this logic:

class ApplicationController < ActionController::Base
   before_filter :verify_user
   def verify_user
       $flag = true if CERTAIN_ROUTES.include?(action_class)
   end
end

class User < ActiveRecord::Base
   def confirmed?
      $flag || !!confirmed_at
   end
end

This barely works for sign in but not for sign up. Also, this is an extremely bad way to achieve it. How should I approach this problem? Other functionalities work fine.


Solution

  • Instead of overwriting confirmed? you could just overwrite the confirmation_required? model method (docs):

    # user.rb
    class User < ActiveRecord::Base
      protected
    
      def confirmation_required?
        false
      end
    end
    

    After that you can handle the confirmation logic yourself, by redirecting all unconfirmed users in a before_action when the controllers require confirmation, or you can pull this into your authorization logic (e.g. with pundit).

    class ApplicationController < ActionController::Base
       def needs_confirmation
         redirect_to root_path unless current_user.confirmed?
       end
    end
    
    class SomeController < ApplicationController
      before_action :needs_confirmation
    end