Search code examples
ruby-on-railsruby-on-rails-3devisewardendevise-confirmable

Allow unconfirmed users to access certain pages which require authentication


I use the Rails Stack with

  • devise
  • warden
  • confirmable

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:

  • no redirection
  • valid session

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