Skip to content

Confirming an already confirmed user -- still not quite working. #1123

Closed
@Evan-M

Description

@Evan-M

NOTE: Much of this has been referenced before in several different Issues and PRs, which I will attempt to link to. If I've left any out, I apologize in advance.


Although it appears that this was first noted in either #410 or #519, and eventually merged in #1001, the current behavior is still not great.

After confirming a user's email address, I can continue to click the confirmation link (i.e. make GET requests to the /auth/confirmations endpoint, triggering the devise_token_auth/confirmations#show action). It seems that the current behavior will generate a new auth token for the user, and redirect to the successful confirmation URL (either from the params if present, or the DeviseTokenAuth.default_confirm_success_url).

As pointed out in #1064, #1067, and #1075, when the new token is created, it conditionally adds a expiration if the user has signed in (the :sign_in_count attribute), which creates a dependency on :trackable. And while #1064 + #1061 effectively uncouple the dependency, the result is that if :trackable is not included, then no expiration is set on the token! What-the-huh!!?

The confirmation process should happen in a consistent manner, regardless of the presence of the :trackable module. A review of the Devise internals shows that checking if a user has signed in (i.e. @resource.has_attribute?(:sign_in_count) && @resource.sign_in_count > 0) is not an indicator of the user having been confirmed! Rather, it is the presence of a timestamp in the :confirmed_at attribute that indicates if a user has confirmed their account. (See: Devise::Models::Confirmable#confirm and Devise::Models::Confirmable#confirmed?)

Furthermore, the Devise::ConfirmationsController#show action simply and concisely checks for the presence of validation errors on the user resource, as it handles checking for already confirmed users at the model level. (The Devise::Models::Confirmable#confirm method sets errors on the user's :email attribute, if that user has already been confirmed).

We're already using the same Devise::Models::Confirmable::ClassMethods#confirm_by_token method in:

def show
@resource = resource_class.confirm_by_token(params[:confirmation_token])

Therefore, it shouldn't really be necessary to check for specific attributes (or their values) on the user instance (e.g. :sign_in_count, :confirmed_at, etc...); just check for errors on the returned user instance like the Devise::ConfirmationsController#show action does. The question is, what does a failure response from the controller action look like? It currently is just a 404 Not Found, but presumably it would call DeviseTokenAuth::ApplicationController#render_error, passing in the relevant validation errors where appropriate.

Lastly, it seems like the tests in the confirmations_controller_test.rb not actually test most model level attributes; instead the tests should assert that various requests (each in known states) lead to the correct set of controller responses given those states.

Specifically, the tests for :trackable attributes should not exist at all here:

test 'the sign_in_count should be 1' do
assert @resource.sign_in_count == 1
end
test 'User shoud have the signed in info filled' do
assert @resource.current_sign_in_at?
end
test 'User shoud have the Last checkin filled' do
assert @resource.last_sign_in_at?
end

And additional tests are needed to check the various possible validation errors on the user that could happen when calling Devise::Models::Confirmable::ClassMethods#confirm_by_token.


I do intend on making a PR for all this, but I'm hoping to collect some feedback in the meantime.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions