Devise: Log user out after the confirmation period ends

256 Views Asked by At

Devise has a way of allowing users to login without confirming their email immediately by setting the allow_unconfirmed_access_for config var.

I have a setup for logging the user out of the application once the confirmation period ends. Also, I need the user to be only logged out at night at 2 AM.

For this, I overrode the confirmation_period_valid? method in the confirmable module of devise for some custom logic -

Original Devise Method -

def confirmation_period_valid?
  return true if self.class.allow_unconfirmed_access_for.nil?
  return false if self.class.allow_unconfirmed_access_for == 0.days

  confirmation_sent_at && confirmation_sent_at.utc >= self.class.allow_unconfirmed_access_for.ago
end

Overriden method I defined in the user model file.

def confirmation_period_valid?
  confirmation_time = confirmation_sent_at.in_time_zone(time_zone).end_of_day + 2.hours
  unconfirmed_access_time = self.class.allow_unconfirmed_access_for.ago.in_time_zone(time_zone)
  confirmation_sent_at && confirmation_time >= unconfirmed_access_time
end

But when I check after the expiration period, I am still able to use the application. The user is not logged out.

Checking the rails console shows that the confirmation period is not valid for the user so that works.

1

There are 1 best solutions below

0
Tom Lord On

Okay... Looking carefully through your logic, I think there are several issues.

I don't know what value you set allow_unconfirmed_access_for to, because you haven't told us, but for the sake of this answer let's arbitrarily assume it's 1 day. Now, your code says the following:

def confirmation_period_valid?
  # This is a confusing variable name.
  # It's more like an unconfirmed access *expiry* time
  confirmation_time = confirmation_sent_at.in_time_zone(time_zone).end_of_day + 2.hours

  # This makes no sense. Why is the user always "unconfirmed" 1 day ago?
  unconfirmed_access_time = self.class.allow_unconfirmed_access_for.ago.in_time_zone(time_zone)

  # if confirmation_sent_at == nil then the method would have blown up in line 1
  # so this check is poorly placed. And the second part is just wrong --
  # you're asking if the user was supposed to be logged out more than a day ago, not now!
  confirmation_sent_at && confirmation_time >= unconfirmed_access_time
end

So... Here's what I think you actually meant to write instead:

def confirmation_period_valid?
  # Note: allow_unconfirmed_access_for is completely redundant in the logic now!
  return false unless confirmation_sent_at

  unconfirmed_access_expires_at = confirmation_sent_at.in_time_zone(time_zone).end_of_day + 2.hours
  unconfirmed_access_expires_at > Time.current
end

Now with that said, I do think this is an over-engineered solution, and you could have just stuck with the default behaviour of allow_unconfirmed_access_for = 1.day rather than adding in this arbitrary 2am logic, but as you like it...