scambra / devise_invitable

An invitation strategy for devise
MIT License
2.66k stars 553 forks source link

Update action in InvitationsController ignores conditions and triggers inappropriate validations on User model #891

Closed thibaudclement closed 1 year ago

thibaudclement commented 1 year ago

I am building a Rails 7 application, with Devise to manage authentication, and Devise Invitable to manage invitations (on the User model).

When a user receives an invite and clicks on the invitation link, they are redirected to the invitations/edit view, where they are able to sign up, by creating a password (their email address is auto-populated in the email field).

Because I have implemented a multi-step signup process, users are not required to provide a name and select a role until after they have confirmed their email address (i.e. confirmed? is true), or in the case of users who sign up from an invite, after they have accepted their invite (which confirms their account automatically behind the scenes).

Therefore, in the User model, I have the following validation:

validates :name, presence: true, if: :confirmed?
validates :role, presence: true, if: :confirmed?

This works as expected for users who sign up on their own (i.e. users who do not sign up from an invite), since Devise's registrations/new (i.e. the create action of the RegistrationsController) successfully ignores validation on the :name and :roleattributes until the user is confirmed?.

However, this does not work as expected for users who sign up from an invite, since Devise Invitable's invitations/edit (i.e. the update action of the InvitationsController) seem to enforce the validation on the :name and :role of the user even when the user is not confirmed?, which results in the following error:

Errors: ["Name can't be blank", "Role can't be blank"]
Completed 422 Unprocessable Entity

As a consequence, the form in invitations/edit is served again to the user, with the password field cleared, and the user is not updated, and the user has no way of providing a name or a role, since those are (again) not required until after they have confirmed their email address (or accepted an invitation, when signing up from an invite, which confirms their email address behind the scenes).

Here is what I have tried to avoid this issue so far, to no avail:

Here are some approaches I refrained from employing:

Am I missing something obvious, is this an known issue, or is there a bug somewhere? I am ears for suggestions to fix or work around this.

scambra commented 1 year ago

I think the good way is your first attempt, adding conditions to validations. There are some methods on invitable: accepting_invitation?, invitation_accepted?, accepted_or_not_invited?.

I can think 2 ways, although I haven't tested:

thibaudclement commented 1 year ago

@scambra thank you very much for your response : I did not even have to try your second suggestion, because the first worked right away, like a charm.

So, if it's any helpful for anyone else, to solve this issue, in the User model, I replaced:

validates :name, presence: true, if: :confirmed?
validates :role, presence: true, if: :confirmed?

with:

validates :name, presence: true, if: [:confirmed?, :accepted_or_not_invited?]
validates :role, presence: true, if: [:confirmed?, :accepted_or_not_invited?]