Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Email Confirmation Changes #923

Merged
merged 27 commits into from
Nov 6, 2024
Merged

Conversation

aaronskiba
Copy link
Collaborator

Fixes *

Changes proposed in this PR:

  • Re-implement email confirmation via Devise's :confirmable module
  • Add rake task that after execution, all non-superadmins will have to confirm their email to use the app
  • Customise UX flow for existing, unconfirmed users

The corresponding value for `devise.mailer.confirmation_instructions.subject` in `config/locales/translation.fr-CA.yml` specifies "Assistant PGD", but maybe it should be verified as well.

Note: I don't think this is the proper way to update the locales files. Rather, I believe only `config/locales/en.yml` should be directly modified. However, #920 is currently preventing that.
This commit customises Devise's default value for the key `devise.failure.unconfirmed`. The value adds a link to the email confirmation page.
Note: The values for this key in both the `...en-CA.yml` and `...fr-CA.yml` files were provided solely by me and need to be approved/finalised by The Alliance
This commit seeks to improve the UX flow for existing users with unconfirmed emails.

By default, when using Devise's `:confirmable` module, confirmation instructions are sent to the user at the time of account creation. This will be perfect for newly created users, but poses some challenges for existing users.

A "first category" of these existing users would be those who were created while `:confirmable` was absent from the codebase. These users are unconfirmed and a confirmation token was never generated for them.

A "second category" of these existing users are those that are unconfirmed but do have an existing confirmation token. Since `:confirmable` has been absent from the codebase for years, the most recent `confirmation_sent_at` value is quite old (`Mon, 22 Feb 2021 15:18:24.000000000 UTC +00:00`).

The code changes in `app/controllers/sessions_controller.rb` and `app/models/user.rb` improves the UX flow for users in the "first category". Rather than requiring the user to manually request new confirmation instructions, these changes auto-send the email when these users first attempt to sign in.

`lib/tasks/dmp_assistant_upgrade.rake` enables this improved UX flow for the aforementioned "second category" of users. `def handle_unconfirmed_users_with_outstanding_invitations` queries for these unconfirmed users and then updates both `confirmation_token` and `confirmation_sent_at` to null. As a result, these users now meet the criteria for the "first category" and can also make use of the improved UX flow.
An "SSO created user" is a user whose account was created via the `Sign in with institutional or social ID` button. This occurs when the chosen SSO email is neither already linked to an existing account, nor does there exist a user account with the same email as the chosen SSO supplied email.

In order to sign in via SSO, the user must provide the password/credentials for the SSO account that they have chosen. This could be considered a form of email confirmation. And because the user's account email will be set to match this SSO email, it follows that email confirmation via Devise's `:confirmable` module is redundant and can be bypassed.

Taking the above reasoning into account, `app/models/user.rb` sets `confirmed_at: Time.now` for any future SSO created accounts.

`lib/tasks/dmp_assistant_upgrade.rake` sets `lib/tasks/dmp_assistant_upgrade.rake` for any "existing SSO created accounts". Specifically, to be an "existing SSO created account", BOTH of the following conditions must be met:
1. An Identifier corresponding to the "openid_connect" `IdentifierScheme` exists for the user
 2. For the identifier and corresponding user found in 1., the values of `identifier.created_at` and `user.created_at` are within 1 second of each other.
This commit re-allows existing users to link via SSO while signed out.  This functionality  was previously removed via commit e112202.

The logic works as follows:
1) A user clicks the "Sign in with institutional or social ID" button
2) Within CILogon, the user chooses an email to sign in with. For this particular functionality to be used, the following conditions must both be true for the chosen email:
  a) It is not currently linked to any user accounts
  b) It matches an existing user.email in the database
3) If the matching user.email is confirmed, then the SSO email is linked to the user account and the user is signed-in. Otherwise, the user remains signed out and a flash notice is rendered indicating that the user.email requires confirmation.
`config/environments/test.rb`
- Add "SMTP From address" to mock Devise's sending of confirmation instructions email at time of account creation

`spec/factories/users.rb`
- Set `confirmed_at { Time.now }` in user factory

`spec/features/registrations_spec.rb`
- Change expected path to `root_path`. This is because the user should not be able to access `plans_path` until after they confirm their email
- Add check to verify that the confirmation-related flash message was sent

`spec/integration/openid_connect_sso_spec.rb`
- Removing `it 'does not create SSO link when user is signed out and SSO email is an existing account email'` test. Commit 965c336  ("Re-add SSO linking capability for signed-out users") updated the SSO behaviour in a manner that this test is no longer relevant.
- These changes replace the changes made to `lib/tasks/dmp_assistant_upgrade.rake` in commits 6087aee and 465d671. We are now setting `confirmed_at`, `confirmation_token`, and `confirmation_sent_at` to nil for all users. (the rake task subsequently sets `confirmed_at = Time.now` for all superusers).
@aaronskiba aaronskiba changed the title Re-Implement Email Confirmation Email Confirmation Changes Oct 7, 2024
This commit changes the flash message we are using for our custom UX flow. Now, rather than a custom message, we are reusing `devise.registrations.signed_up_but_unconfirmed`.
Copy link
Collaborator

@lagoan lagoan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work all around ! I added just some comments on minor changes.

app/models/user.rb Outdated Show resolved Hide resolved
Comment on lines 232 to 233
You have to confirm your account before continuing.
<a href="/users/confirmation/new" class="a-orange">(Click to request a new confirmation email)</a>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now we are sending the email verification message sent when the user tries to log in and is not verified. What do you think about letting the user know about this sent email in this message instead of asking the user to request the email confirmation again ?

Copy link
Collaborator Author

@aaronskiba aaronskiba Oct 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. This flash message is actually only rendered when the user already has an outstanding confirmation. An additional confirmation email will not be auto-sent in this case and they will have to manually request a new one.

If a user is unconfirmed and has no outstanding confirmation token, then a different message will render after they attempt to sign in (specifically, devise.registrations.signed_up_but_unconfirmed).

3.1.4 :001 > I18n.t('devise.registrations.signed_up_but_unconfirmed')
 => "A message with a confirmation link has been sent to your email address. Please open the link to activate your account. If you do not receive the confirmation email, please check your spam filter." 
# app/models/user.rb
def confirmation_instructions_handled?
  confirmed? || confirmation_token.present?
end
# app/controllers/sessions_controller.rb
unless existing_user.confirmation_instructions_handled?
  handle_confirmation_instructions(existing_user)
  return
end

def handle_confirmation_instructions(user)
  user.send_confirmation_instructions
  flash[:notice] = _('A confirmation email has been sent to you. Please check your inbox to confirm your account.')
  flash[:notice] = I18n.t('devise.registrations.signed_up_but_unconfirmed')
  redirect_to root_path
end

config/locales/translation.en-CA.yml Show resolved Hide resolved
config/locales/translation.fr-CA.yml Outdated Show resolved Hide resolved
spec/factories/users.rb Outdated Show resolved Hide resolved
This commit takes the idea in commit 6087aee and applies it to users signing in via SSO. Via the addition of `app/controllers/concerns/email_confirmation_handler.rb`, some refactoring has been applied here as well.
Some refactoring was performed in `def openid_connect` to  avoid adding `Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity` to `rubocop:disable`.
- These tests are meant to confirm that the desired behaviour is encountered via our custom email confirmation UX flow.
  - For more info, See comments for `app/controllers/concerns/email_confirmation_handler.rb`
In order to properly test the values of our customised `I18n.t` values (specifically, `I18n.t('devise.failure.unconfirmed')` and `I18n.t('devise.registrations.signed_up_but_unconfirmed')`), we need to set `I18n.default_locale` to match our app's default locale.

NOTE: These changes only set `I18n.default_locale = :'en-CA'` for the duration of the email confirmation tests. However, it may be best to apply this update globally throughout the tests.
- Rename `def missing_confirmation_instructions_handled?(user)` to `def confirmation_instructions_missing_and_handled?(user)` and add comments to where it is called for easy reference
- Move `def confirmed_or_has_confirmation_token?` from User model to `EmailConfirmationHandler`. This check is only accessed by the concern, and it seems easiest to make it a private method and encapsulate the logic there.
@aaronskiba aaronskiba marked this pull request as ready for review October 21, 2024 15:31
@lagoan lagoan self-requested a review October 22, 2024 20:18
Copy link
Collaborator

@lagoan lagoan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome work. Just a minor comment on how we are setting the time.

app/models/user.rb Outdated Show resolved Hide resolved
aaronskiba and others added 2 commits November 4, 2024 13:44
Intended to resolve this issue: #912
The following PR was used as a reference: #915

Co-Authored-By: Omar Rodriguez Arenas <[email protected]>
- Updated via `bundle exec rake translation:sync` with prod key.
- These changes finalise some of the initial changes we made for testing in commit 6c41bd7
- The corresponding translation was commited via the following commit: 09e731e
lagoan
lagoan previously approved these changes Nov 6, 2024
Copy link
Collaborator

@lagoan lagoan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Add rake task for `openid_connect` / CILogon cleanup
Copy link
Collaborator

@lagoan lagoan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@aaronskiba aaronskiba merged commit 1ea6214 into integration Nov 6, 2024
11 checks passed
@aaronskiba aaronskiba deleted the aaron/add-email-confirmation branch November 6, 2024 18:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants