Skip to content

Allow users to change their own email address #8671

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

Open
wants to merge 24 commits into
base: master
Choose a base branch
from

Conversation

knollengewaechs
Copy link
Contributor

@knollengewaechs knollengewaechs commented Jun 4, 2025

URL of deployed dev instance (used for testing):

  • https://___.webknossos.xyz

more context in slack:

Steps to test:

  • Log in to wk, open user dropdown and click "change email"
  • change email, play around with the fields to check their validation
  • you should be locked out and only be able to log in with the new email address
  • best done locally: with the changed application.conf, check that the email verfication reminder toast shows up upon next login
  • lower grace period manually (e.g. to 1s) and check that you can not log in afterwards

TODOs:

  • check email verification workflow
  • backend: permissions for non-admin users
  • frontend
  • find out: should there be two routes, one for admin and one for user
  • validate pw

Issues:


(Please delete unneeded items, merge only when none are left open)

@knollengewaechs knollengewaechs self-assigned this Jun 4, 2025
Copy link
Contributor

coderabbitai bot commented Jun 4, 2025

📝 Walkthrough

Walkthrough

This change enables users to change their own email addresses through a new UI and backend flow, requiring password verification for the action. It removes the ability for admins to change user emails, updates the database schema to track email change timestamps, and adjusts email verification logic and configuration accordingly.

Changes

Files/Paths Change Summary
app/controllers/UserController.scala, app/models/user/UserService.scala Added password check and logic to allow users to change their own email, removed admin email change capability, updated update flow, and added logging for email changes.
app/models/user/EmailVerificationService.scala, app/models/user/MultiUser.scala Modified grace period logic to use new email change timestamp, added emailChangeDate field to user model, updated DAO for new column, and ensured timestamp updates on email change.
conf/application.conf Enabled email verification by default by setting activated and required to true.
conf/evolutions/135-extra-column-for-email-changed.sql, conf/evolutions/reversions/135-extra-column-for-email-changed.sql, tools/postgres/schema.sql Added migration and schema changes for new emailChangeDate column in multiUsers table, updated views and schema version, and provided reversion script.
frontend/javascripts/admin/auth/change_email_view.tsx Introduced new React component for users to change their own email with password and email validation, API integration, and post-update handling.
frontend/javascripts/admin/user/user_list_view.tsx Removed admin ability to change user emails inline; email field is now read-only.
frontend/javascripts/navbar.tsx Added "Change Email" menu item to user submenu in avatar dropdown.
frontend/javascripts/router.tsx Added secured route for new "Change Email" view.
conf/messages Added error message for incorrect password during email change.
unreleased_changes/8671.md Added changelog entry describing new feature and removal of admin email change.

Assessment against linked issues

Objective Addressed Explanation
Allow users to change their own email address via a user-accessible UI (similar to "change password") (#8619)
Add a "Change Email" entry to the avatar menu for user access (#8619)
Remove admin ability to change user email; make email field read-only for admins (#8619)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes found.

Suggested labels

bug

Poem

A hop, a skip, an email anew,
Now bunnies can change it—admins, adieu!
With password in paw and a click on the way,
The menu now sparkles with "Change Email" today.
The database remembers, the schema’s in sync,
This bunny’s email hops faster than you think!
🐇📧✨


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Comment on lines -86 to 88
activated = false
required = false
activated = true
required = true
gracePeriod = 7 days # time period in which users do not need to verify their email address
Copy link
Contributor Author

Choose a reason for hiding this comment

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

for testing purposes. REMOVE BEFORE MERGING

Copy link
Contributor

@MichaelBuessemeyer MichaelBuessemeyer left a comment

Choose a reason for hiding this comment

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

The password verification should now be working. I did not add an explicit route for this and instead made it part of the update route. In case the current user wants to update their email, their password is needed in the request. If an admin changes the email of another user, the password should not be required in the update route.

Moreover, I noticed that the email verification resending is triggered even when the email update call returns an error due to e.g. wrong password. Could you please give this a try @knollengewaechs?

@MichaelBuessemeyer
Copy link
Contributor

Oh and I also slightly changed the frontend to accommodate the newest backend changes to make sure the password entered by the user is sent as part of the update request so that it gets verified properly

@knollengewaechs
Copy link
Contributor Author

knollengewaechs commented Jun 18, 2025

@MichaelBuessemeyer thanks for including the password check and adjusting the frontend!

If an admin changes the email of another user, the password should not be required in the update route.

From what I can tell, this shouldnt be possible any more? see slack

@knollengewaechs
Copy link
Contributor Author

I would have done the necessary frontend changes myself of course :) but I think its working now, so apart from the question before, this PR should be ready for review, right?

@MichaelBuessemeyer
Copy link
Contributor

MichaelBuessemeyer commented Jun 18, 2025

From what I can tell, this shouldnt be possible any more? see slack

Whoopsie, thanks for telling me. I'll remove this option then :)

I would have done the necessary frontend changes myself of course :)

Yeah sorry for fiddling with it but I needed this to test (and fix 🙈) the backend code. The inital version wasn't working 😅 .

but I think its working now, so apart from the question before, this PR should be ready for review, right?

Yeah sounds like it 🚀

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
conf/application.conf (1)

86-88: Verify if enabling email verification by default is intentional.

The configuration now enables and requires email verification by default. However, there's a past review comment from knollengewaechs stating these were "for testing purposes. REMOVE BEFORE MERGING."

Please confirm whether enabling email verification by default in production is the intended behavior for this PR, or if these configuration changes should be reverted before merging.

🧹 Nitpick comments (4)
frontend/javascripts/admin/auth/change_email_view.tsx (2)

48-48: Fix parameter type annotation.

The parameter otherEmailFieldKey is typed as string[] but the function expects and uses it as a single string value.

-function checkEmailsAreMatching(value: string, otherEmailFieldKey: string[]) {
+function checkEmailsAreMatching(value: string, otherEmailFieldKey: string) {

176-176: Remove unnecessary router wrapper.

The component doesn't appear to use any router props, making the withRouter wrapper unnecessary.

-export default withRouter<RouteComponentProps, any>(ChangeEmailView);
+export default ChangeEmailView;

Also remove the unused import:

-import { type RouteComponentProps, withRouter } from "react-router-dom";
app/controllers/UserController.scala (2)

275-275: Address the TODO comment about guest users.

The comment questions whether the super user check should remain given the introduction of guest users. This needs clarification or resolution.

Should I help analyze the impact of guest users on this permission check or create an issue to track this decision?


309-309: Fix comment formatting.

The comment has inconsistent formatting.

Apply this diff to fix the comment:

-          // properties for team managers dataset manager and admins only: experiences, teams
+          // Properties for team managers, dataset managers and admins only: experiences, teams
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c240321 and 8be7adb.

📒 Files selected for processing (12)
  • MIGRATIONS.unreleased.md (1 hunks)
  • app/controllers/UserController.scala (7 hunks)
  • app/models/user/EmailVerificationService.scala (1 hunks)
  • app/models/user/MultiUser.scala (3 hunks)
  • conf/application.conf (1 hunks)
  • conf/evolutions/135-extra-column-for-email-changed.sql (1 hunks)
  • conf/evolutions/reversions/135-extra-column-for-email-changed.sql (1 hunks)
  • frontend/javascripts/admin/auth/change_email_view.tsx (1 hunks)
  • frontend/javascripts/admin/user/user_list_view.tsx (1 hunks)
  • frontend/javascripts/navbar.tsx (1 hunks)
  • frontend/javascripts/router.tsx (2 hunks)
  • tools/postgres/schema.sql (2 hunks)
🔇 Additional comments (25)
tools/postgres/schema.sql (2)

24-24: LGTM: Schema version increment is correct.

The schema version increment from 134 to 135 aligns with the new migration script.


462-462: LGTM: New emailChangeDate column is properly defined.

The column definition follows existing patterns with appropriate type, nullability, and default value.

conf/evolutions/135-extra-column-for-email-changed.sql (1)

1-24: LGTM: Well-structured migration script.

The migration follows best practices:

  • Proper transaction handling for atomicity
  • Schema version validation before applying changes
  • Correct handling of dependent views
  • Sensible initialization of the new column with existing created timestamps
MIGRATIONS.unreleased.md (1)

13-13: LGTM: Documentation updated correctly.

The new migration file is properly documented in the unreleased migrations list.

frontend/javascripts/navbar.tsx (1)

680-680: LGTM: New email change menu item is well-positioned.

The "Change Email" menu item follows existing patterns and is logically placed next to the "Change Password" option.

app/models/user/EmailVerificationService.scala (1)

81-81: LGTM! Logical improvement to grace period calculation.

Using emailChangeDate instead of created for the grace period calculation makes perfect sense - users should get a grace period from when they last changed their email, not from when their account was created.

app/models/user/MultiUser.scala (3)

29-29: LGTM! Proper addition of email change tracking.

The new emailChangeDate field with a sensible default value properly supports tracking when emails are changed.


61-61: LGTM! Correct database field parsing.

The parsing correctly reads the emailchangedate field from the database row.


96-97: LGTM! Proper email update implementation.

Setting both isEmailVerified = false and emailChangeDate = NOW() when updating email ensures proper tracking and forces re-verification of the new email address.

frontend/javascripts/router.tsx (2)

3-3: LGTM! Correct import addition.

Import for the new ChangeEmailView component is properly added.


648-652: LGTM! Properly secured route for email changes.

The new route is correctly configured with authentication requirements and follows the established pattern for auth-related routes.

frontend/javascripts/admin/user/user_list_view.tsx (1)

20-20: LGTM! Proper cleanup of unused imports.

Removing unused imports after eliminating inline email editing functionality keeps the code clean and reduces bundle size.

frontend/javascripts/admin/auth/change_email_view.tsx (2)

22-28: LGTM! Secure email change implementation.

The changeEmail function properly includes password verification by adding it to the user object, ensuring backend validation of the user's identity.


72-79: LGTM! Excellent user experience design.

The info alert clearly communicates the logout consequence, setting proper user expectations before they proceed with the email change.

conf/evolutions/reversions/135-extra-column-for-email-changed.sql (4)

1-3: LGTM - Proper transaction setup and version assertion.

The transaction boundary and schema version assertion provide good safety measures for the reversion process.


5-8: LGTM - Correct dependency management and column removal.

The script properly drops dependent views before removing the column, which prevents constraint violations. The emailChangeDate column removal is handled correctly.


10-19: LGTM - View recreation matches expected schema.

The recreated views properly exclude the emailChangeDate column and maintain the original filtering and join logic for the pre-135 schema state.


21-23: LGTM - Proper schema version update and transaction commit.

The schema version is correctly updated to 134 and the transaction is properly committed.

app/controllers/UserController.scala (7)

18-21: LGTM - Appropriate imports for authentication functionality.

The new imports support the password verification feature for email changes.


29-29: LGTM - Dependency injection for credentials provider.

The CredentialsProvider dependency is correctly injected for password verification.

Comment on lines +34 to +39
.then(async () => {
handleResendVerificationEmail();
Toast.success("Email address changed successfully. You will be logged out.");
await Request.receiveJSON("/api/auth/logout");
window.location.href = "/auth/login";
Store.dispatch(logoutUserAction());
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix potential race condition between redirect and logout action.

There's a potential race condition where window.location.href = "/auth/login" initiates navigation before Store.dispatch(logoutUserAction()) can complete.

Move the logout dispatch before the redirect to ensure proper state cleanup:

 Toast.success("Email address changed successfully. You will be logged out.");
 await Request.receiveJSON("/api/auth/logout");
+Store.dispatch(logoutUserAction());
 window.location.href = "/auth/login";
-Store.dispatch(logoutUserAction());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.then(async () => {
handleResendVerificationEmail();
Toast.success("Email address changed successfully. You will be logged out.");
await Request.receiveJSON("/api/auth/logout");
window.location.href = "/auth/login";
Store.dispatch(logoutUserAction());
.then(async () => {
handleResendVerificationEmail();
Toast.success("Email address changed successfully. You will be logged out.");
await Request.receiveJSON("/api/auth/logout");
Store.dispatch(logoutUserAction());
window.location.href = "/auth/login";
🤖 Prompt for AI Agents
In frontend/javascripts/admin/auth/change_email_view.tsx around lines 34 to 39,
the logout action is dispatched after setting window.location.href, causing a
race condition where navigation may occur before the logout completes. To fix
this, move Store.dispatch(logoutUserAction()) to execute before setting
window.location.href to "/auth/login" to ensure the logout state is properly
updated before redirecting.

@MichaelBuessemeyer
Copy link
Contributor

Sorry, backend CI doesn't run due to some download server issues / outage

@MichaelBuessemeyer
Copy link
Contributor

@knollengewaechs I requested @daniel-wer for the frontend review. Hope thats ok :)

Copy link
Member

@fm3 fm3 left a comment

Choose a reason for hiding this comment

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

Backend looking good! Just adding minor comments. Basic testing worked for me as well :)

Note that the backend test database multiusers csv has to be adapted to the new column for the e2e tests to run.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
unreleased_changes/8671.md (1)

5-5: Fix grammatical error: missing apostrophe.

The phrase "a users email address" should be "a user's email address" to indicate the possessive form.

-- A administator of the organization can no longer update a users email address. This has to be done by the user themselves.
+- An administrator of the organization can no longer update a user's email address. This has to be done by the user themselves.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc94854 and 34bb221.

⛔ Files ignored due to path filters (1)
  • test/db/multiusers.csv is excluded by !**/*.csv
📒 Files selected for processing (5)
  • app/controllers/UserController.scala (6 hunks)
  • app/models/user/UserService.scala (1 hunks)
  • conf/application.conf (1 hunks)
  • conf/messages (1 hunks)
  • unreleased_changes/8671.md (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • conf/messages
🚧 Files skipped from review as they are similar to previous changes (2)
  • conf/application.conf
  • app/controllers/UserController.scala
🧰 Additional context used
🪛 LanguageTool
unreleased_changes/8671.md

[uncategorized] ~5-~5: It seems likely that a singular genitive (’s) apostrophe is missing.
Context: ...the organization can no longer update a users email address. This has to be done by t...

(AI_HYDRA_LEO_APOSTROPHE_S_XS)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build-smoketest-push
  • GitHub Check: frontend-tests
  • GitHub Check: backend-tests
🔇 Additional comments (1)
app/models/user/UserService.scala (1)

201-204: LGTM! Good addition of audit logging for email changes.

The for-comprehension properly sequences the email update with logging, providing valuable audit trail for email changes. The implementation correctly maintains existing error handling while adding transparency.

@hotzenklotz hotzenklotz mentioned this pull request Jun 23, 2025
7 tasks
Copy link
Member

@daniel-wer daniel-wer left a comment

Choose a reason for hiding this comment

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

Frontend code lgtm 👍

Notes from testing:

  • When entering the wrong password, the user is still logged out without any indication that the email update did not succeed. It would be great if the user is only logged out if the email update succeeded and sees an error that the password is wrong otherwise.
  • It feels a bit weird to be logged out and then after a second of waiting have the page reload. Do you also observe this or is it specific to my setup? Could we avoid reloading the page or is that tied to being logged out?

@MichaelBuessemeyer MichaelBuessemeyer requested a review from fm3 June 24, 2025 12:03
Co-authored-by: Daniel <daniel.werner@scalableminds.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
unreleased_changes/8671.md (1)

2-6: Tighten wording & fix missing possessive apostrophe

Minor copy-editing to improve clarity and correct grammar.

- A user can now update the email address of their account by themselves.
+ Users can now update their own email addresses.

- An administrator of the organization can no longer update a users email address. This has to be done by the user themselves.
+ Organization administrators can no longer update a user's email address; users must make this change themselves.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 34bb221 and 4477287.

📒 Files selected for processing (2)
  • app/models/user/UserService.scala (1 hunks)
  • unreleased_changes/8671.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/models/user/UserService.scala
🧰 Additional context used
🪛 LanguageTool
unreleased_changes/8671.md

[uncategorized] ~5-~5: It looks like there might be a possessive apostrophe missing. Consider inserting it.
Context: ...the organization can no longer update a users email address. This has to be done by t...

(AI_EN_LECTOR_MISSING_NOUN_POSSESSIVE)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build-smoketest-push
  • GitHub Check: frontend-tests
  • GitHub Check: backend-tests

Copy link
Member

@fm3 fm3 left a comment

Choose a reason for hiding this comment

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

Backend LGTM. Leaving final approval to @daniel-wer

hotzenklotz added a commit that referenced this pull request Jun 27, 2025
This PR adds a new "Account Settings" page to host various other
sub-pages for:
- changing email (soon)
- changing password
- appearance/theme
- passkeys (future)
- API Auth Token

<img width="1269" alt="Screenshot 2025-06-25 at 10 45 45"
src="https://github.com/user-attachments/assets/848cc79b-b636-4de1-b495-e15059e7d452"
/>
<img width="1268" alt="Screenshot 2025-06-25 at 10 45 54"
src="https://github.com/user-attachments/assets/fcfb3c08-8a58-450e-b50d-96bd58ed9f1a"
/>
<img width="1269" alt="Screenshot 2025-06-25 at 10 46 01"
src="https://github.com/user-attachments/assets/4a7da90e-a2ed-4a60-ac9b-ebff65d8e140"
/>


### URL of deployed dev instance (used for testing):
- https://accountsettingspage.webknossos.xyz/

### Steps to test:
- Go to new Account settings page
- Change PW 
- Try dark/light mode

### Issues:
- Blocked by #8671
- Blocked by #8679
- fixes #5408

------
(Please delete unneeded items, merge only when none are left open)
- [x] Updated
[changelog](../blob/master/CHANGELOG.unreleased.md#unreleased)
- [ ] Updated [migration
guide](../blob/master/MIGRATIONS.unreleased.md#unreleased) if applicable
- [ ] Updated [documentation](../blob/master/docs) if applicable
- [ ] Adapted [wk-libs python
client](https://github.com/scalableminds/webknossos-libs/tree/master/webknossos/webknossos/client)
if relevant API parts change
- [ ] Removed dev-only changes like prints and application.conf edits
- [x] Considered [common edge
cases](../blob/master/.github/common_edge_cases.md)
- [ ] Needs datastore update after deployment

---------

Co-authored-by: Philipp Otto <philippotto@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Philipp Otto <philipp.4096@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow users to change their own email address
5 participants