Align Phone Number Plugin API with Email OTP Plugin #2592

Open
opened 2026-03-13 10:05:35 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @junwen-k on GitHub (Dec 23, 2025).

Is this suited for github?

  • Yes, this is suited for github

The Phone Number plugin has inconsistent API design compared to Email OTP, making it confusing and less flexible.

Key Issues:

  1. No separate OTP check method - Can't validate OTP before performing actions (addresses #1148)
  2. Inconsistent naming - Some methods use code instead of otp, missing verificationOtp suffix
  3. No type parameter - Can't distinguish between verification, sign-in, and password reset flows for OTP verification
  4. Confusing passwordless sign-in - verify() creates sessions by default (essentially signs in) but name doesn't reflect this behavior

Describe the solution you'd like

Current vs Proposed API

Feature / Method Email OTP Plugin Phone Number Plugin (Current) Phone Number Plugin (Proposed)
Send OTP emailOtp.sendVerificationOtp({ email, type }) phoneNumber.sendOtp({ phoneNumber }) phoneNumber.sendVerificationOtp({ phoneNumber, type })
OTP Types (type param) "email-verification"| "sign-in"| "forget-password" Not available "phone-number-verification"| "sign-in"| "forget-password"
Check OTP (validate w/o action) emailOtp.checkVerificationOtp({ email, type, otp }) Not available phoneNumber.checkVerificationOtp({ phoneNumber, type, otp })
Sign In (credentials) Not supported (OTP only) signIn.phoneNumber({ phoneNumber, password }) Unchanged
Sign In (OTP/passwordless) signIn.emailOtp({ email, otp }) phoneNumber.verify({ phoneNumber, otp })
(creates session by default)
signIn.phoneNumberOtp({ phoneNumber, otp }) with disableSignUp? option
Verify Email / Phone Number emailOtp.verifyEmail({ email, otp }) phoneNumber.verify({ phoneNumber, otp }) phoneNumber.verifyPhoneNumber({ phoneNumber, otp })
Forgot Password (send OTP) emailOtp.sendVerificationOtp({ email, type: "forget-password" })
or
forgetPassword.emailOtp({ email })
phoneNumber.requestPasswordReset({ phoneNumber }) phoneNumber.sendVerificationOtp({ phoneNumber, type: "forget-password" })
Forgot Password (reset w/ OTP) emailOtp.resetPassword({ email, otp, password }) phoneNumber.resetPassword({ phoneNumber, otp, newPassword }) phoneNumber.resetPassword({ phoneNumber, otp, password })
Parameter name for OTP otp code | otp otp
Method naming suffix sendVerificationOtp, checkVerificationOtp,
verifyEmail
sendOtp, verify sendVerificationOtp, checkVerificationOtp,
verifyPhoneNumber
Ability to check OTP before action Yes No Yes

Note: The proposed API might not be 100% complete, but this is the general direction we want—something along these lines to align the experience across plugins.

Benefits

  1. Consistency - Both plugins follow identical patterns
  2. Flexibility - Multi-step flows with early validation
  3. Clarity - Method names reflect their actual behavior
  4. Better DX - Predictable API across authentication methods

Describe alternatives you've considered

I considered adding a separate "verify OTP" check only for the password reset flow in the phone number plugin, to mirror my current immediate need. This would enable a multi-step process where the OTP is first validated before proceeding with password reset, similar to the pattern I use for email OTP.

However, this solution felt too one-off and specific, addressing only the reset password use case. The better approach is to align both the Phone Number and Email OTP plugins with a consistent API surface, adding checkVerificationOtp, associating a type parameter for context, and renaming methods for clarity.

Additional context

Request for Feedback & Broader Considerations

The proposed API alignment is not 100% finalized. There may be edge cases or scenarios I've missed. Suggestions to further stabilize and make the API consistent and flexible are very welcome!

My primary goals are:

  • Consistency: Both plugins (and all other plugins in better-auth) should behave and look the same (where applicable), so developers can switch contexts with minimal friction.
  • Flexibility: Support multi-step flows (check OTP before action), clear method naming.
  • Clarity & DX: It's always clear what a method does, and the ergonomic patterns are predictable for developers.

The goal: Minimize surprises for users by making the plugin APIs behave as similarly as possible, unless there’s a strong reason not to.

Originally created by @junwen-k on GitHub (Dec 23, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### Is your feature request related to a problem? Please describe. The [Phone Number](https://www.better-auth.com/docs/plugins/phone-number) plugin has inconsistent API design compared to [Email OTP](https://www.better-auth.com/docs/plugins/email-otp), making it confusing and less flexible. **Key Issues:** 1. **No separate OTP check method** - Can't validate OTP before performing actions (addresses [#1148](https://github.com/better-auth/better-auth/issues/1148#issuecomment-3675639113)) 2. **Inconsistent naming** - Some methods use `code` instead of `otp`, missing `verificationOtp` suffix 3. **No type parameter** - Can't distinguish between `verification`, `sign-in`, and `password reset` flows for OTP verification 4. **Confusing passwordless sign-in** - `verify()` creates sessions by default (essentially signs in) but name doesn't reflect this behavior ### Describe the solution you'd like ## Current vs Proposed API | Feature / Method | Email OTP Plugin | Phone Number Plugin (Current) | Phone Number Plugin (Proposed) | | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------- | | **Send OTP** | `emailOtp.sendVerificationOtp({ email, type })` | `phoneNumber.sendOtp({ phoneNumber })` | `phoneNumber.sendVerificationOtp({ phoneNumber, type })` | | **OTP Types (`type` param)** | `"email-verification"\| "sign-in"\| "forget-password"` | Not available | `"phone-number-verification"\| "sign-in"\| "forget-password"` | | **Check OTP (validate w/o action)** | `emailOtp.checkVerificationOtp({ email, type, otp })` | Not available | `phoneNumber.checkVerificationOtp({ phoneNumber, type, otp })` | | **Sign In (credentials)** | Not supported (OTP only) | `signIn.phoneNumber({ phoneNumber, password })` | Unchanged | | **Sign In (OTP/passwordless)** | `signIn.emailOtp({ email, otp })` | `phoneNumber.verify({ phoneNumber, otp })`<br/>(_creates session by default_) | `signIn.phoneNumberOtp({ phoneNumber, otp })` with `disableSignUp?` option | | **Verify Email / Phone Number** | `emailOtp.verifyEmail({ email, otp })` | `phoneNumber.verify({ phoneNumber, otp })` | `phoneNumber.verifyPhoneNumber({ phoneNumber, otp })` | | **Forgot Password (send OTP)** | `emailOtp.sendVerificationOtp({ email, type: "forget-password" })`<br/>or<br/>`forgetPassword.emailOtp({ email })` | `phoneNumber.requestPasswordReset({ phoneNumber })` | `phoneNumber.sendVerificationOtp({ phoneNumber, type: "forget-password" })` | | **Forgot Password (reset w/ OTP)** | `emailOtp.resetPassword({ email, otp, password })` | `phoneNumber.resetPassword({ phoneNumber, otp, newPassword })` | `phoneNumber.resetPassword({ phoneNumber, otp, password })` | | **Parameter name for OTP** | `otp` | `code` \| `otp` | `otp` | | **Method naming suffix** | `sendVerificationOtp`, `checkVerificationOtp`,<br/>`verifyEmail` | `sendOtp`, `verify` | `sendVerificationOtp`, `checkVerificationOtp`,<br/>`verifyPhoneNumber` | | **Ability to check OTP before action** | Yes | No | Yes | *Note: The proposed API might not be 100% complete, but this is the general direction we want—something along these lines to align the experience across plugins.* ## Benefits 1. **Consistency** - Both plugins follow identical patterns 2. **Flexibility** - Multi-step flows with early validation 3. **Clarity** - Method names reflect their actual behavior 4. **Better DX** - Predictable API across authentication methods ### Describe alternatives you've considered I considered adding a separate "verify OTP" check only for the password reset flow in the phone number plugin, to mirror my current immediate need. This would enable a multi-step process where the OTP is first validated before proceeding with password reset, similar to the pattern I use for email OTP. However, this solution felt too one-off and specific, addressing only the reset password use case. The better approach is to align both the Phone Number and Email OTP plugins with a consistent API surface, adding `checkVerificationOtp`, associating a `type` parameter for context, and renaming methods for clarity. ### Additional context ## Request for Feedback & Broader Considerations The proposed API alignment is not 100% finalized. There may be edge cases or scenarios I've missed. Suggestions to further stabilize and make the API consistent and flexible are very welcome! **My primary goals are:** - **Consistency:** Both plugins (and all other plugins in `better-auth`) should behave and look the same (where applicable), so developers can switch contexts with minimal friction. - **Flexibility:** Support multi-step flows (check OTP before action), clear method naming. - **Clarity & DX:** It's always clear what a method does, and the ergonomic patterns are predictable for developers. The goal: **Minimize surprises for users by making the plugin APIs behave as similarly as possible, unless there’s a strong reason not to.**
GiteaMirror added the enhancement label 2026-03-13 10:05:35 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Dec 23, 2025):

It looks like you have created an issue regarding aligning the Phone Number Plugin API with the Email OTP Plugin due to inconsistencies in API design that may confuse developers. The key issues include the lack of a separate OTP check method, inconsistent naming conventions, and confusion around the passwordless sign-in process. Your proposed solutions aim to create a more unified and developer-friendly API across both plugins, enhancing flexibility and clarity for end-users [1].

Your request for feedback is timely, and improvements to the API can greatly benefit the community. For a clear next step, consider discussing your proposed changes in more detail with the development team for further refinement or implementation priorities.

If you have more specific questions or would like additional input on a particular aspect of the proposal, feel free to ask! What areas do you feel need the most focus for adjustments?

If you need more help, tag @better-auth-agent in a comment so I can respond.

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Dec 23, 2025): It looks like you have created an issue regarding aligning the Phone Number Plugin API with the Email OTP Plugin due to inconsistencies in API design that may confuse developers. The key issues include the lack of a separate OTP check method, inconsistent naming conventions, and confusion around the passwordless sign-in process. Your proposed solutions aim to create a more unified and developer-friendly API across both plugins, enhancing flexibility and clarity for end-users [[1]](https://github.com/better-auth/better-auth/pull/4102). Your request for feedback is timely, and improvements to the API can greatly benefit the community. For a clear next step, consider discussing your proposed changes in more detail with the development team for further refinement or implementation priorities. If you have more specific questions or would like additional input on a particular aspect of the proposal, feel free to ask! What areas do you feel need the most focus for adjustments? _If you need more help, tag @better-auth-agent in a comment so I can respond._ <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) • [Discord](https://discord.gg/fG2XXEuQX3) • [GitHub](https://github.com/Skyvern-AI/Skyvern) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/fG2XXEuQX3) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/Skyvern-AI/Skyvern)
Author
Owner

@meetgoti07 commented on GitHub (Jan 11, 2026):

@junwen-k

I've refactored the Phone Number plugin to align with the Email OTP plugin's API design. Here's what was implemented:

Key Changes

  1. New Methods:

    • sendVerificationOtp({ phoneNumber, type }) - Replaces sendOtp() with type context
    • checkVerificationOtp({ phoneNumber, type, otp }) - Validates OTP without action (enables 3-step flows)
    • signInPhoneNumberOtp({ phoneNumber, otp }) - Dedicated passwordless sign-in
    • verifyPhoneNumber({ phoneNumber, otp }) - Phone verification (no session by default)
  2. OTP Type System: Added type parameter ("phone-number-verification" | "sign-in" | "forget-password") to distinguish OTP contexts

  3. Parameter Standardization: Changed codeotp for consistency

  4. 3-Step Password Reset: checkVerificationOtp enables early validation before password entry

  5. User Enumeration Prevention: checkVerificationOtp works without requiring user existence (unlike Email OTP's current implementation)

Technical Rationale

  • Consistency: Both plugins now follow identical patterns, reducing cognitive load
  • Flexibility: Multi-step flows with early validation improve UX
  • Security: Better enumeration prevention and clearer separation of concerns
  • Backward Compatibility: Old methods deprecated but functional for smooth migration

All 42 tests passing with proper isolation and industry-standard practices. No breaking changes.

@meetgoti07 commented on GitHub (Jan 11, 2026): @junwen-k I've refactored the Phone Number plugin to align with the Email OTP plugin's API design. Here's what was implemented: ### Key Changes 1. **New Methods**: - `sendVerificationOtp({ phoneNumber, type })` - Replaces `sendOtp()` with type context - `checkVerificationOtp({ phoneNumber, type, otp })` - Validates OTP without action (enables 3-step flows) - `signInPhoneNumberOtp({ phoneNumber, otp })` - Dedicated passwordless sign-in - `verifyPhoneNumber({ phoneNumber, otp })` - Phone verification (no session by default) 2. **OTP Type System**: Added `type` parameter (`"phone-number-verification" | "sign-in" | "forget-password"`) to distinguish OTP contexts 3. **Parameter Standardization**: Changed `code` → `otp` for consistency 4. **3-Step Password Reset**: `checkVerificationOtp` enables early validation before password entry 5. **User Enumeration Prevention**: `checkVerificationOtp` works without requiring user existence (unlike Email OTP's current implementation) ### Technical Rationale - **Consistency**: Both plugins now follow identical patterns, reducing cognitive load - **Flexibility**: Multi-step flows with early validation improve UX - **Security**: Better enumeration prevention and clearer separation of concerns - **Backward Compatibility**: Old methods deprecated but functional for smooth migration All 42 tests passing with proper isolation and industry-standard practices. No breaking changes.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2592