[PR #6071] [CLOSED] [WIP] Add support for delegating OTP verification to user-provided objects #6423

Closed
opened 2026-03-13 12:58:54 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/6071
Author: @Copilot
Created: 11/18/2025
Status: Closed

Base: canaryHead: copilot/add-otp-verification-support


📝 Commits (1)

📄 Description

Thanks for asking me to work on this. I will get started on it and keep this PR's description up to date as I form a plan and make progress.

Original prompt

Summary

Add support for delegating OTP verification to a user-provided object with a verify() method when passed as opts.storeOTP. This enables consumers to use stronger, salted, or external verification schemes (bcrypt/argon2, remote APIs) instead of the current default SHA-1 hashed approach.

Goals

  • Detect when opts.storeOTP is an object with a verify() function and call it to validate OTPs.
  • Maintain the existing behavior as a fallback for existing usage (plain / encrypted / hashed).
  • Update TypeScript types to allow storeOTP to be an object with an optional verify({ password, hash }): Promise method.
  • Add unit tests for the new behavior and ensure existing tests still pass.
  • Update documentation to describe the new customization point and security guidance (discourage SHA-1 default).

Files & Changes (actionable details for the agent)

  1. Find all places that implement OTP verification and storage (likely under packages/plugins such as email-otp, two-factor/otp, or a shared storeOTP helper). In each place where storedOtp is compared/verified against an incoming otp, insert a branch before the existing verification logic:
if (typeof opts.storeOTP === 'object' && 'verify' in opts.storeOTP && typeof opts.storeOTP.verify === 'function') {
  // delegate verification to the provided verifier
  return await opts.storeOTP.verify({ password: otp, hash: storedOtp });
}

Make sure the function handles both sync and async returns (i.e., await the result) and that a boolean is returned to the caller.

  1. TypeScript types
  • Locate the core types/interfaces for options (e.g., src/types.ts, index.d.ts, or packages/*/types).
  • Add/update a type for StoreOTPObject:
type StoreOtpVerifier = {
  verify: (input: { password: string; hash: string }) => boolean | Promise<boolean>;
};
  • Update existing storeOTP union type (which probably is string | boolean | 'hashed' | etc.) to include StoreOtpVerifier.
  1. Tests
  • Add tests to cover:
    • When opts.storeOTP is an object with verify(), ensure verify() is called with the correct args and its return value is respected (true -> success, false -> failure).
    • Ensure existing hashed comparison behavior continues to work as before when storeOTP is not an object with verify().
    • Edge cases: verify() throws an error -> surface as verification failure (or rethrow depending on current patterns); ensure errors are handled consistently.
  1. Docs
  • Update README or relevant plugin READMEs to document that storeOTP may be an object with a verify() method that receives { password, hash }. Mention the security rationale and recommend libraries (argon2/bcrypt) over SHA-1.
  1. Lint/format
  • Run the repo's linting/formatting (prettier/eslint) and ensure the changes pass CI.

Acceptance criteria

  • A new pull request in better-auth/better-auth that implements the changes above.
  • Types updated and exported so consumers get correct typings.
  • Tests added and passing.
  • Documentation updated to mention the new API.

Notes and assumptions for the implementer

  • The repo's OTP handling files may be in more than one plugin; the implementer should search for occurrences of storeOTP, verify, otp, or the repo's OTP-related plugin folders and update all verification code paths.
  • Keep backward compatibility: if opts.storeOTP is not an object with verify(), the existing verification should remain unchanged.
  • Do not change the default behavior of existing code paths beyond adding the new branch.
  • If there are multiple TypeScript declaration files, ensure all relevant ones are updated.

If you need me to provide specific file paths or example code from the repository before making changes, tell me and I will fetch them.

This pull request was created as a result of the following prompt from Copilot chat.

Summary

Add support for delegating OTP verification to a user-provided object with a verify() method when passed as opts.storeOTP. This enables consumers to use stronger, salted, or external verification schemes (bcrypt/argon2, remote APIs) instead of the current default SHA-1 hashed approach.

Goals

  • Detect when opts.storeOTP is an object with a verify() function and call it to validate OTPs.
  • Maintain the existing behavior as a fallback for existing usage (plain / encrypted / hashed).
  • Update TypeScript types to allow storeOTP to be an object with an optional verify({ password, hash }): Promise method.
  • Add unit tests for the new behavior and ensure existing tests still pass.
  • Update documentation to describe the new customization point and security guidance (discourage SHA-1 default).

Files & Changes (actionable details for the agent)

  1. Find all places that implement OTP verification and storage (likely under packages/plugins such as email-otp, two-factor/otp, or a shared storeOTP helper). In each place where storedOtp is compared/verified against an incoming otp, insert a branch before the existing verification logic:
if (typeof opts.storeOTP === 'object' && 'verify' in opts.storeOTP && typeof opts.storeOTP.verify === 'function') {
  // delegate verification to the provided verifier
  return await opts.storeOTP.verify({ password: otp, hash: storedOtp });
}

Make sure the function handles both sync and async returns (i.e., await the result) and that a boolean is returned to the caller.

  1. TypeScript types
  • Locate the core types/interfaces for options (e.g., src/types.ts, index.d.ts, or packages/*/types).
  • Add/update a type for StoreOTPObject:
type StoreOtpVerifier = {
  verify: (input: { password: string; hash: string }) => boolean | Promise<boolean>;
};
  • Update existing storeOTP union type (which probably is string | boolean | 'hashed' | etc.) to include StoreOtpVerifier.
  1. Tests
  • Add tests to cover:
    • When opts.storeOTP is an object with verify(), ensure verify() is called with the correct args and its return value is respected (true -> success, false -> failure).
    • Ensure existing hashed comparison behavior continues to work as before when storeOTP is not an object with verify().
    • Edge cases: verify() throws an error -> surface as verification failure (or rethrow depending on current patterns); ensure errors are handled consistently.
  1. Docs
  • Update README or relevant plugin READMEs to document that storeOTP may be an object with a verify() method that receives { password, hash }. Mention the security rationale and recommend libraries (argon2/bcrypt) over SHA-1.
  1. Lint/format
  • Run the repo's linting/formatting (prettier/eslint) and ensure the changes pass CI.

Acceptance criteria

  • A new pull request in better-auth/better-auth that implements the changes above.
  • Types updated and exported so consumers get correct typings.
  • Tests added and passing.
  • Documentation updated to mention the new API.

Notes and assumptions for the implementer

  • The repo's OTP handling files may be in more than one plugin; the implementer should search for occurrences of storeOTP, verify, otp, or the repo's OTP-related plugin folders and update all verification code paths.
  • Keep backward compatibility: if opts.storeOTP is not an object with verify(), the existing verification should remain unchanged.
  • Do not change the default behavior of existing code paths beyond adding the new branch.
  • If there are multiple TypeScript declaration files, ensure all relevant ones are updated.

If you need me to provide specific file paths or example code from the repository before making changes, tell me and I will fetch them.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.


Summary by cubic

Adds support for delegating OTP verification to a user-provided verifier via opts.storeOTP.verify(), enabling stronger hashing or external checks while keeping current behavior as a fallback.

  • New Features
    • Detect a verifier object on opts.storeOTP and call verify({ password, hash }); supports sync/async.
    • Preserve existing plain/encrypted/hashed verification when no verifier is provided.
    • Extend TypeScript types so storeOTP can be a verifier object with verify returning boolean or Promise.
    • Add tests for delegation, fallback, and error handling.
    • Update docs with guidance and examples (e.g., bcrypt/argon2, remote APIs).

Written for commit 1e3925e4e1. Summary will update automatically 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/6071 **Author:** [@Copilot](https://github.com/apps/copilot-swe-agent) **Created:** 11/18/2025 **Status:** ❌ Closed **Base:** `canary` ← **Head:** `copilot/add-otp-verification-support` --- ### 📝 Commits (1) - [`1e3925e`](https://github.com/better-auth/better-auth/commit/1e3925e4e1307124bfc971da7d1ff66387ecba93) Initial plan ### 📄 Description Thanks for asking me to work on this. I will get started on it and keep this PR's description up to date as I form a plan and make progress. <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > Summary > > Add support for delegating OTP verification to a user-provided object with a verify() method when passed as opts.storeOTP. This enables consumers to use stronger, salted, or external verification schemes (bcrypt/argon2, remote APIs) instead of the current default SHA-1 hashed approach. > > Goals > > - Detect when opts.storeOTP is an object with a verify() function and call it to validate OTPs. > - Maintain the existing behavior as a fallback for existing usage (plain / encrypted / hashed). > - Update TypeScript types to allow storeOTP to be an object with an optional verify({ password, hash }): Promise<boolean> method. > - Add unit tests for the new behavior and ensure existing tests still pass. > - Update documentation to describe the new customization point and security guidance (discourage SHA-1 default). > > Files & Changes (actionable details for the agent) > > 1) Find all places that implement OTP verification and storage (likely under packages/plugins such as email-otp, two-factor/otp, or a shared storeOTP helper). In each place where storedOtp is compared/verified against an incoming otp, insert a branch before the existing verification logic: > > ```ts > if (typeof opts.storeOTP === 'object' && 'verify' in opts.storeOTP && typeof opts.storeOTP.verify === 'function') { > // delegate verification to the provided verifier > return await opts.storeOTP.verify({ password: otp, hash: storedOtp }); > } > ``` > > Make sure the function handles both sync and async returns (i.e., await the result) and that a boolean is returned to the caller. > > 2) TypeScript types > > - Locate the core types/interfaces for options (e.g., src/types.ts, index.d.ts, or packages/*/types). > - Add/update a type for StoreOTPObject: > > ```ts > type StoreOtpVerifier = { > verify: (input: { password: string; hash: string }) => boolean | Promise<boolean>; > }; > ``` > > - Update existing storeOTP union type (which probably is string | boolean | 'hashed' | etc.) to include StoreOtpVerifier. > > 3) Tests > > - Add tests to cover: > - When opts.storeOTP is an object with verify(), ensure verify() is called with the correct args and its return value is respected (true -> success, false -> failure). > - Ensure existing hashed comparison behavior continues to work as before when storeOTP is not an object with verify(). > - Edge cases: verify() throws an error -> surface as verification failure (or rethrow depending on current patterns); ensure errors are handled consistently. > > 4) Docs > > - Update README or relevant plugin READMEs to document that storeOTP may be an object with a verify() method that receives { password, hash }. Mention the security rationale and recommend libraries (argon2/bcrypt) over SHA-1. > > 5) Lint/format > > - Run the repo's linting/formatting (prettier/eslint) and ensure the changes pass CI. > > Acceptance criteria > > - A new pull request in better-auth/better-auth that implements the changes above. > - Types updated and exported so consumers get correct typings. > - Tests added and passing. > - Documentation updated to mention the new API. > > Notes and assumptions for the implementer > > - The repo's OTP handling files may be in more than one plugin; the implementer should search for occurrences of `storeOTP`, `verify`, `otp`, or the repo's OTP-related plugin folders and update all verification code paths. > - Keep backward compatibility: if opts.storeOTP is not an object with verify(), the existing verification should remain unchanged. > - Do not change the default behavior of existing code paths beyond adding the new branch. > - If there are multiple TypeScript declaration files, ensure all relevant ones are updated. > > If you need me to provide specific file paths or example code from the repository before making changes, tell me and I will fetch them. > </details> *This pull request was created as a result of the following prompt from Copilot chat.* > Summary > > Add support for delegating OTP verification to a user-provided object with a verify() method when passed as opts.storeOTP. This enables consumers to use stronger, salted, or external verification schemes (bcrypt/argon2, remote APIs) instead of the current default SHA-1 hashed approach. > > Goals > > - Detect when opts.storeOTP is an object with a verify() function and call it to validate OTPs. > - Maintain the existing behavior as a fallback for existing usage (plain / encrypted / hashed). > - Update TypeScript types to allow storeOTP to be an object with an optional verify({ password, hash }): Promise<boolean> method. > - Add unit tests for the new behavior and ensure existing tests still pass. > - Update documentation to describe the new customization point and security guidance (discourage SHA-1 default). > > Files & Changes (actionable details for the agent) > > 1) Find all places that implement OTP verification and storage (likely under packages/plugins such as email-otp, two-factor/otp, or a shared storeOTP helper). In each place where storedOtp is compared/verified against an incoming otp, insert a branch before the existing verification logic: > > ```ts > if (typeof opts.storeOTP === 'object' && 'verify' in opts.storeOTP && typeof opts.storeOTP.verify === 'function') { > // delegate verification to the provided verifier > return await opts.storeOTP.verify({ password: otp, hash: storedOtp }); > } > ``` > > Make sure the function handles both sync and async returns (i.e., await the result) and that a boolean is returned to the caller. > > 2) TypeScript types > > - Locate the core types/interfaces for options (e.g., src/types.ts, index.d.ts, or packages/*/types). > - Add/update a type for StoreOTPObject: > > ```ts > type StoreOtpVerifier = { > verify: (input: { password: string; hash: string }) => boolean | Promise<boolean>; > }; > ``` > > - Update existing storeOTP union type (which probably is string | boolean | 'hashed' | etc.) to include StoreOtpVerifier. > > 3) Tests > > - Add tests to cover: > - When opts.storeOTP is an object with verify(), ensure verify() is called with the correct args and its return value is respected (true -> success, false -> failure). > - Ensure existing hashed comparison behavior continues to work as before when storeOTP is not an object with verify(). > - Edge cases: verify() throws an error -> surface as verification failure (or rethrow depending on current patterns); ensure errors are handled consistently. > > 4) Docs > > - Update README or relevant plugin READMEs to document that storeOTP may be an object with a verify() method that receives { password, hash }. Mention the security rationale and recommend libraries (argon2/bcrypt) over SHA-1. > > 5) Lint/format > > - Run the repo's linting/formatting (prettier/eslint) and ensure the changes pass CI. > > Acceptance criteria > > - A new pull request in better-auth/better-auth that implements the changes above. > - Types updated and exported so consumers get correct typings. > - Tests added and passing. > - Documentation updated to mention the new API. > > Notes and assumptions for the implementer > > - The repo's OTP handling files may be in more than one plugin; the implementer should search for occurrences of `storeOTP`, `verify`, `otp`, or the repo's OTP-related plugin folders and update all verification code paths. > - Keep backward compatibility: if opts.storeOTP is not an object with verify(), the existing verification should remain unchanged. > - Do not change the default behavior of existing code paths beyond adding the new branch. > - If there are multiple TypeScript declaration files, ensure all relevant ones are updated. > > If you need me to provide specific file paths or example code from the repository before making changes, tell me and I will fetch them. > <!-- START COPILOT CODING AGENT TIPS --> --- 💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey). <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Adds support for delegating OTP verification to a user-provided verifier via opts.storeOTP.verify(), enabling stronger hashing or external checks while keeping current behavior as a fallback. - **New Features** - Detect a verifier object on opts.storeOTP and call verify({ password, hash }); supports sync/async. - Preserve existing plain/encrypted/hashed verification when no verifier is provided. - Extend TypeScript types so storeOTP can be a verifier object with verify returning boolean or Promise<boolean>. - Add tests for delegation, fallback, and error handling. - Update docs with guidance and examples (e.g., bcrypt/argon2, remote APIs). <sup>Written for commit 1e3925e4e1307124bfc971da7d1ff66387ecba93. Summary will update automatically 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-03-13 12:58:54 -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#6423