[PR #7968] [MERGED] feat(email-otp): add change email flow with OTP #33259

Closed
opened 2026-04-17 23:54:25 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/7968
Author: @cdy-peters
Created: 2/14/2026
Status: Merged
Merged: 2/28/2026
Merged by: @Bekacru

Base: canaryHead: feat/email-otp-change-email


📝 Commits (8)

  • 99c5a36 feat(email-otp): add change email methods
  • d57397d feat(email-otp): add current email verification before email change request
  • 09fbb28 feat(email-otp): add enable change email option
  • 209a45b fix(email-otp): reject change-email OTP type in sendVerificationOTP
  • 037ce56 chore: apply lint fixes
  • 93b033f fix(email-otp): implement Copilot suggestions
  • b7e6698 Merge branch 'canary' into feat/email-otp-change-email
  • d2f5208 Merge branch 'canary' into feat/email-otp-change-email

📊 Changes

5 files changed (+1113 additions, -24 deletions)

View changed files

📝 docs/content/docs/plugins/email-otp.mdx (+96 -0)
📝 packages/better-auth/src/plugins/email-otp/email-otp.test.ts (+612 -20)
📝 packages/better-auth/src/plugins/email-otp/index.ts (+18 -0)
📝 packages/better-auth/src/plugins/email-otp/routes.ts (+365 -2)
📝 packages/better-auth/src/plugins/email-otp/types.ts (+22 -2)

📄 Description

Implemented email change flow using email OTP's, this ensure consistent use of OTP's throughout the application.

The overrideDefaultEmailVerification option does not work with changeEmail() as sendVerificationOTP() requires a user with the provided email exists, as such, sending an OTP to the new email address is unsuccessful.
Instead of implementing a workaround to address this, I believe implementing dedicated endpoints for the change email flow provides a better DX as a developer may not want to override all email verification with OTP's.

I have added 2 endpoints, emailOTP.requestEmailChangeOTP() and emailOTP.changeEmailOTP().
I have also added an option to enable verification of the current email before sending an OTP to the new email. This requires a valid OTP for the current email be passed to emailOTP.requestEmailChangeOTP().

Closes #5540 #879


Summary by cubic

Adds a dedicated change-email-with-OTP flow with two authenticated endpoints. Supports optional current-email verification, triggers standard email verification callbacks, and refreshes the session on success.

  • New Features

    • Endpoints: POST /email-otp/request-email-change and POST /email-otp/change-email (require a valid session).
    • Plugin option: changeEmail { enabled, verifyCurrentEmail }.
    • Rejects sendVerificationOtp with type "change-email"; use the new endpoints.
    • Added rate limits for both change-email endpoints; OTP types now include "change-email".
    • On success: updates the session cookie and sets emailVerified = true.
    • Docs and tests cover success, invalid/expired OTP, unauthorized/disabled flow, and edge cases.
  • Migration

    • If you used sendVerificationOtp with type "change-email", switch to POST /email-otp/request-email-change and POST /email-otp/change-email, and set changeEmail.enabled = true.
    • To require confirming the current email first, set changeEmail.verifyCurrentEmail = true and verify with an email-verification OTP before requesting the change.

Written for commit d2f52082bf. Summary will update on new commits.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/better-auth/better-auth/pull/7968 **Author:** [@cdy-peters](https://github.com/cdy-peters) **Created:** 2/14/2026 **Status:** ✅ Merged **Merged:** 2/28/2026 **Merged by:** [@Bekacru](https://github.com/Bekacru) **Base:** `canary` ← **Head:** `feat/email-otp-change-email` --- ### 📝 Commits (8) - [`99c5a36`](https://github.com/better-auth/better-auth/commit/99c5a3669b47e92adae4cf6e90661ea8a07a9610) feat(email-otp): add change email methods - [`d57397d`](https://github.com/better-auth/better-auth/commit/d57397d3dcc339314b8c9fb8ab8ce6b559c5f5c3) feat(email-otp): add current email verification before email change request - [`09fbb28`](https://github.com/better-auth/better-auth/commit/09fbb2841105bf548bc0a94392e17f776543edb3) feat(email-otp): add enable change email option - [`209a45b`](https://github.com/better-auth/better-auth/commit/209a45bdc8f41f3328f94e86e9e82721db138fdc) fix(email-otp): reject change-email OTP type in `sendVerificationOTP` - [`037ce56`](https://github.com/better-auth/better-auth/commit/037ce56937e380575a519975e66015997065b731) chore: apply lint fixes - [`93b033f`](https://github.com/better-auth/better-auth/commit/93b033f04d06e730658a22c0e17569cddbf71172) fix(email-otp): implement Copilot suggestions - [`b7e6698`](https://github.com/better-auth/better-auth/commit/b7e6698c17fddfe9a2affbcf008171a674f2c0b0) Merge branch 'canary' into feat/email-otp-change-email - [`d2f5208`](https://github.com/better-auth/better-auth/commit/d2f52082bfd171146fcfdb24af9c83ccb4314719) Merge branch 'canary' into feat/email-otp-change-email ### 📊 Changes **5 files changed** (+1113 additions, -24 deletions) <details> <summary>View changed files</summary> 📝 `docs/content/docs/plugins/email-otp.mdx` (+96 -0) 📝 `packages/better-auth/src/plugins/email-otp/email-otp.test.ts` (+612 -20) 📝 `packages/better-auth/src/plugins/email-otp/index.ts` (+18 -0) 📝 `packages/better-auth/src/plugins/email-otp/routes.ts` (+365 -2) 📝 `packages/better-auth/src/plugins/email-otp/types.ts` (+22 -2) </details> ### 📄 Description Implemented email change flow using email OTP's, this ensure consistent use of OTP's throughout the application. The `overrideDefaultEmailVerification` option does not work with `changeEmail()` as `sendVerificationOTP()` requires a user with the provided email exists, as such, sending an OTP to the new email address is unsuccessful. Instead of implementing a workaround to address this, I believe implementing dedicated endpoints for the change email flow provides a better DX as a developer may not want to override ***all*** email verification with OTP's. I have added 2 endpoints, `emailOTP.requestEmailChangeOTP()` and `emailOTP.changeEmailOTP()`. I have also added an option to enable verification of the current email before sending an OTP to the new email. This requires a valid OTP for the current email be passed to `emailOTP.requestEmailChangeOTP()`. Closes #5540 #879 <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Adds a dedicated change-email-with-OTP flow with two authenticated endpoints. Supports optional current-email verification, triggers standard email verification callbacks, and refreshes the session on success. - **New Features** - Endpoints: POST /email-otp/request-email-change and POST /email-otp/change-email (require a valid session). - Plugin option: changeEmail { enabled, verifyCurrentEmail }. - Rejects sendVerificationOtp with type "change-email"; use the new endpoints. - Added rate limits for both change-email endpoints; OTP types now include "change-email". - On success: updates the session cookie and sets emailVerified = true. - Docs and tests cover success, invalid/expired OTP, unauthorized/disabled flow, and edge cases. - **Migration** - If you used sendVerificationOtp with type "change-email", switch to POST /email-otp/request-email-change and POST /email-otp/change-email, and set changeEmail.enabled = true. - To require confirming the current email first, set changeEmail.verifyCurrentEmail = true and verify with an email-verification OTP before requesting the change. <sup>Written for commit d2f52082bfd171146fcfdb24af9c83ccb4314719. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-17 23:54:25 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#33259