Add encryption for OTP codes in verification table when using Email OTP plugin #507

Closed
opened 2026-03-13 07:50:15 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @lmiguelside on GitHub (Jan 1, 2025).

Is this suited for github?

  • Yes, this is suited for github

When using the Email OTP plugin, the verification codes are currently stored in plain text in the database's verification table. While OTPs are temporary by nature, encrypting these codes would add an extra layer of security to prevent potential exposure of valid codes in case of a database breach during the short validity window.

Describe the solution you'd like

  • Implement encryption for the OTP code value in the verification table
  • Use a secure encryption method to encrypt the OTP before storing it
  • Decrypt the value when validating the OTP
  • Maintain the current expiration mechanism
  • Keep the performance impact minimal since OTP validation needs to be quick

Describe alternatives you've considered

  • Hashing the OTP instead of encryption (similar to password storage), but this would require storing the plain text OTP temporarily for email sending (I believe).
  • Keeping the current implementation, relying on the short expiration time as the main security measure

Additional context

This enhancement would be particularly valuable for applications that need to meet specific security compliance requirements or handle sensitive data. While OTPs are temporary by nature, encrypting them follows security best practices and provides an additional level of defense.

Thank you and congrats for the great work with the library!

Originally created by @lmiguelside on GitHub (Jan 1, 2025). ### Is this suited for github? - [X] Yes, this is suited for github ### Is your feature request related to a problem? Please describe. When using the Email OTP plugin, the verification codes are currently stored in plain text in the database's verification table. While OTPs are temporary by nature, encrypting these codes would add an extra layer of security to prevent potential exposure of valid codes in case of a database breach during the short validity window. ### Describe the solution you'd like - Implement encryption for the OTP code value in the verification table - Use a secure encryption method to encrypt the OTP before storing it - Decrypt the value when validating the OTP - Maintain the current expiration mechanism - Keep the performance impact minimal since OTP validation needs to be quick ### Describe alternatives you've considered - Hashing the OTP instead of encryption (similar to password storage), but this would require storing the plain text OTP temporarily for email sending (I believe). - Keeping the current implementation, relying on the short expiration time as the main security measure ### Additional context This enhancement would be particularly valuable for applications that need to meet specific security compliance requirements or handle sensitive data. While OTPs are temporary by nature, encrypting them follows security best practices and provides an additional level of defense. Thank you and congrats for the great work with the library!
Author
Owner

@JosipPardon commented on GitHub (Jan 2, 2025):

@luismiguelamado This is extremely good idea. Actually, I don't know why this is not already implemented, it now seems to me that this is major security problem.

Hashing OTPs same as passwords is much better than encryption (more secure). It also makes more sense, because OTPs are one time PASSWORDS.

It's not true that this would require storing OTP temporarily, as you said. This should be algorithm:

  1. Create OTP
  2. Pass it to sendVerificationOTP({ email, otp, type }) and execute it.
  3. If sendVerificationOTP fails (throws error), stop here. If It is successful, create hash of that OTP and store it.

It is not necessary to store OTP as plain text somewhere for 1st and 2nd step.

This should be implemented for all things stored in verification table (like user deletion tokens). It's great that this can be done without introducing any breaking changes to apps already using BetterAuth, code stays the same.

@JosipPardon commented on GitHub (Jan 2, 2025): @luismiguelamado This is extremely good idea. Actually, I don't know why this is not already implemented, it now seems to me that this is major security problem. Hashing OTPs same as passwords is much better than encryption (more secure). It also makes more sense, because OTPs are _one time **PASSWORDS**_. It's not true that this would require storing OTP temporarily, as you said. This should be algorithm: 1. Create OTP 2. Pass it to `sendVerificationOTP({ email, otp, type })` and execute it. 3. If `sendVerificationOTP` fails (throws error), stop here. If It is successful, create hash of that OTP and store it. It is not necessary to store OTP as plain text somewhere for 1st and 2nd step. This should be implemented for all things stored in verification table (like user deletion tokens). It's great that this can be done without introducing any breaking changes to apps already using BetterAuth, code stays the same.
Author
Owner

@lmiguelside commented on GitHub (Jan 2, 2025):

@luismiguelamado This is extremely good idea.

Hashing OTPs same as passwords is much better than encryption (more secure). It also makes more sense, because OTPs are one time PASSWORDS.

It's not true that this would require storing OTP temporarily, as you said. This should be algorithm:

  1. Create OTP
  2. Pass it to sendVerificationOTP({ email, otp, type }) and execute it.
  3. If sendVerificationOTP fails (throws error), stop here. If It is successful, create hash of that OTP and store it.

It is not necessary to store OTP as plain text somewhere for 1st and 2nd step.

This should be implemented for all things stored in verification table (like user deletion tokens). It's great that this can be done without introducing any breaking changes to apps already using BetterAuth, code the same.

Hi there,
Thank you for the clarification and additional info added!

@lmiguelside commented on GitHub (Jan 2, 2025): > @luismiguelamado This is extremely good idea. > > Hashing OTPs same as passwords is much better than encryption (more secure). It also makes more sense, because OTPs are _one time **PASSWORDS**_. > > It's not true that this would require storing OTP temporarily, as you said. This should be algorithm: > > 1. Create OTP > 2. Pass it to `sendVerificationOTP({ email, otp, type })` and execute it. > 3. If `sendVerificationOTP` fails (throws error), stop here. If It is successful, create hash of that OTP and store it. > > It is not necessary to store OTP as plain text somewhere for 1st and 2nd step. > > This should be implemented for all things stored in verification table (like user deletion tokens). It's great that this can be done without introducing any breaking changes to apps already using BetterAuth, code the same. Hi there, Thank you for the clarification and additional info added!
Author
Owner

@dosubot[bot] commented on GitHub (Jun 13, 2025):

Hi, @luismiguelamado. I'm Dosu, and I'm helping the better-auth team manage their backlog. I'm marking this issue as stale.

Issue Summary

  • You suggested implementing encryption for OTP codes in the Email OTP plugin to enhance security.
  • @JosipPardon recommended hashing OTPs instead, providing a detailed algorithm for implementation.
  • You appreciated the clarification and agreed with the hashing approach.
  • The issue was resolved by adopting the hashing method for OTPs and other tokens in the verification table.

Next Steps

  • Please confirm if this issue is still relevant to the latest version of the better-auth repository by commenting on this issue.
  • If there is no further activity, the issue will be automatically closed in 7 days.

Thank you for your understanding and contribution!

@dosubot[bot] commented on GitHub (Jun 13, 2025): Hi, @luismiguelamado. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog. I'm marking this issue as stale. **Issue Summary** - You suggested implementing encryption for OTP codes in the Email OTP plugin to enhance security. - @JosipPardon recommended hashing OTPs instead, providing a detailed algorithm for implementation. - You appreciated the clarification and agreed with the hashing approach. - The issue was resolved by adopting the hashing method for OTPs and other tokens in the verification table. **Next Steps** - Please confirm if this issue is still relevant to the latest version of the better-auth repository by commenting on this issue. - If there is no further activity, the issue will be automatically closed in 7 days. Thank you for your understanding and contribution!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#507