kawadhiya21
kawadhiya21

Reputation: 2528

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.

Upvotes: 12

Views: 2034

Answers (2)

Tim Krins
Tim Krins

Reputation: 3846

You should take a look at the gem 'pundit' - it works well with devise.

https://github.com/varvet/pundit

Rather than writing controller before_actions etc, you write policies which will cover each of your authorization requirements, and then use those policies inside your controllers.

For example, in a controller:

class ExampleController < ApplicationController
    before_action { authorize :example }

    def case_one
       # action
    end

    def case_two
       # action
    end

    def case_three
       # action
    end
end

Then your policy would be kept under app/policies/example_policy.rb

class ExamplePolicy < ApplicationPolicy
  attr_reader :user

  def initialize(user, _)
    @user = user
  end

  def case_one?
    true
  end

  def case_two?
    user.present? && user.confirmed_at.present?
  end

  def case_three?
    user.present?
  end
end

It works really well, especially in other cases where you are determining authorization against a type of resource.

Upvotes: 3

smallbutton
smallbutton

Reputation: 3437

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

Upvotes: 11

Related Questions