Add twoFactorPage option to twoFactorClient plugin #2129

Closed
opened 2026-03-13 09:29:13 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @wuzgood98 on GitHub (Oct 15, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

The docs for basic usage of Two Factor client plugin includes twoFactorPage option, a page to redirect if a user needs to verify their two factor.
It does not exist as an option currently so I figured why not add its implementation rather than remove it from the docs.

Image

Current vs. Expected behavior

Following the previous section, I expected twoFactorClient to have twoFactorPage as an option, but it did not.

What version of Better Auth are you using?

1.3.27

System info

{
  "system": {
    "platform": "win32",
    "arch": "x64",
    "version": "Windows 11 Pro",
    "release": "10.0.26120",
    "cpuCount": 4,
    "cpuModel": "Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz",
    "totalMemory": "19.86 GB",
    "freeMemory": "4.34 GB"
  },
  "node": {
    "version": "v22.18.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "11.6.1"
  },
  "frameworks": [
    {
      "name": "next",
      "version": "15.5.4"
    },
    {
      "name": "react",
      "version": "^19.2.0"
    }
  ],
  "databases": [
    {
      "name": "@prisma/client",
      "version": "^6.16.2"
    }
  ],
  "betterAuth": {
    "version": "^1.3.27",
    "config": {
      "appName": "GRANTED",
      "user": {
        "changeEmail": {
          "enabled": true
        },
        "additionalFields": {
          "onboardingStartedAt": {
            "type": "string",
            "input": false
          }
        }
      },
      "emailAndPassword": {
        "enabled": true,
        "requireEmailVerification": true,
        "password": {}
      },
      "emailVerification": {
        "sendOnSignUp": true,
        "autoSignInAfterVerification": true
      },
      "socialProviders": {
        "google": {
          "clientId": "[REDACTED]",
          "clientSecret": "[REDACTED]"
        }
      },
      "session": {
        "fields": {
          "expiresAt": "expires",
          "token": "[REDACTED]"
        }
      },
      "account": {
        "fields": {
          "accountId": "providerAccountId",
          "refreshToken": "[REDACTED]",
          "accessToken": "[REDACTED]",
          "accessTokenExpiresAt": "expires_at",
          "idToken": "[REDACTED]"
        },
        "accountLinking": {
          "enable": true,
          "trustedProviders": [
            "google",
            "github",
            "email-password"
          ],
          "allowDifferentEmails": false
        }
      },
      "advanced": {
        "database": {
          "generateId": false
        }
      },
      "plugins": [
        {
          "name": "magic-link",
          "config": {
            "id": "magic-link",
            "endpoints": {},
            "rateLimit": [
              {
                "window": 60,
                "max": 5
              }
            ]
          }
        },
        {
          "name": "two-factor",
          "config": {
            "id": "two-factor",
            "endpoints": {},
            "hooks": {
              "after": [
                {}
              ]
            },
            "schema": {
              "user": {
                "fields": {
                  "twoFactorEnabled": {
                    "type": "boolean",
                    "required": false,
                    "defaultValue": false,
                    "input": false
                  }
                }
              },
              "twoFactor": {
                "fields": {
                  "secret": {
                    "type": "string",
                    "required": true,
                    "returned": false
                  },
                  "backupCodes": {
                    "type": "string",
                    "required": true,
                    "returned": false
                  },
                  "userId": {
                    "type": "string",
                    "required": true,
                    "returned": false,
                    "references": {
                      "model": "user",
                      "field": "id"
                    }
                  }
                }
              }
            },
            "rateLimit": [
              {
                "window": 10,
                "max": 3
              }
            ],
            "$ERROR_CODES": {
              "OTP_NOT_ENABLED": "OTP not enabled",
              "OTP_HAS_EXPIRED": "OTP has expired",
              "TOTP_NOT_ENABLED": "TOTP not enabled",
              "TWO_FACTOR_NOT_ENABLED": "Two factor isn't enabled",
              "BACKUP_CODES_NOT_ENABLED": "Backup codes aren't enabled",
              "INVALID_BACKUP_CODE": "Invalid backup code",
              "INVALID_CODE": "Invalid code",
              "TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE": "Too many attempts. Please request a new code.",
              "INVALID_TWO_FACTOR_COOKIE": "Invalid two factor cookie"
            }
          }
        },
        {
          "name": "last-login-method",
          "config": {
            "id": "last-login-method",
            "hooks": {
              "after": [
                {}
              ]
            }
          }
        },
        {
          "name": "custom-session",
          "config": {
            "id": "custom-session",
            "hooks": {
              "after": [
                {}
              ]
            },
            "endpoints": {}
          }
        }
      ]
    }
  }
}

Which area(s) are affected? (Select all that apply)

Client, Types, Documentation

Auth config (if applicable)


Additional context

I tested my reproduction against the latest release.

Originally created by @wuzgood98 on GitHub (Oct 15, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce The docs for basic usage of Two Factor client plugin includes `twoFactorPage` option, a page to redirect if a user needs to verify their two factor. It does not exist as an option currently so I figured why not add its implementation rather than remove it from the docs. <img width="3680" height="1540" alt="Image" src="https://github.com/user-attachments/assets/fe44c5b3-b3d0-4992-9d7a-8383378a33bf" /> ### Current vs. Expected behavior Following the previous section, I expected `twoFactorClient` to have `twoFactorPage` as an option, but it did not. ### What version of Better Auth are you using? 1.3.27 ### System info ```bash { "system": { "platform": "win32", "arch": "x64", "version": "Windows 11 Pro", "release": "10.0.26120", "cpuCount": 4, "cpuModel": "Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz", "totalMemory": "19.86 GB", "freeMemory": "4.34 GB" }, "node": { "version": "v22.18.0", "env": "development" }, "packageManager": { "name": "npm", "version": "11.6.1" }, "frameworks": [ { "name": "next", "version": "15.5.4" }, { "name": "react", "version": "^19.2.0" } ], "databases": [ { "name": "@prisma/client", "version": "^6.16.2" } ], "betterAuth": { "version": "^1.3.27", "config": { "appName": "GRANTED", "user": { "changeEmail": { "enabled": true }, "additionalFields": { "onboardingStartedAt": { "type": "string", "input": false } } }, "emailAndPassword": { "enabled": true, "requireEmailVerification": true, "password": {} }, "emailVerification": { "sendOnSignUp": true, "autoSignInAfterVerification": true }, "socialProviders": { "google": { "clientId": "[REDACTED]", "clientSecret": "[REDACTED]" } }, "session": { "fields": { "expiresAt": "expires", "token": "[REDACTED]" } }, "account": { "fields": { "accountId": "providerAccountId", "refreshToken": "[REDACTED]", "accessToken": "[REDACTED]", "accessTokenExpiresAt": "expires_at", "idToken": "[REDACTED]" }, "accountLinking": { "enable": true, "trustedProviders": [ "google", "github", "email-password" ], "allowDifferentEmails": false } }, "advanced": { "database": { "generateId": false } }, "plugins": [ { "name": "magic-link", "config": { "id": "magic-link", "endpoints": {}, "rateLimit": [ { "window": 60, "max": 5 } ] } }, { "name": "two-factor", "config": { "id": "two-factor", "endpoints": {}, "hooks": { "after": [ {} ] }, "schema": { "user": { "fields": { "twoFactorEnabled": { "type": "boolean", "required": false, "defaultValue": false, "input": false } } }, "twoFactor": { "fields": { "secret": { "type": "string", "required": true, "returned": false }, "backupCodes": { "type": "string", "required": true, "returned": false }, "userId": { "type": "string", "required": true, "returned": false, "references": { "model": "user", "field": "id" } } } } }, "rateLimit": [ { "window": 10, "max": 3 } ], "$ERROR_CODES": { "OTP_NOT_ENABLED": "OTP not enabled", "OTP_HAS_EXPIRED": "OTP has expired", "TOTP_NOT_ENABLED": "TOTP not enabled", "TWO_FACTOR_NOT_ENABLED": "Two factor isn't enabled", "BACKUP_CODES_NOT_ENABLED": "Backup codes aren't enabled", "INVALID_BACKUP_CODE": "Invalid backup code", "INVALID_CODE": "Invalid code", "TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE": "Too many attempts. Please request a new code.", "INVALID_TWO_FACTOR_COOKIE": "Invalid two factor cookie" } } }, { "name": "last-login-method", "config": { "id": "last-login-method", "hooks": { "after": [ {} ] } } }, { "name": "custom-session", "config": { "id": "custom-session", "hooks": { "after": [ {} ] }, "endpoints": {} } } ] } } } ``` ### Which area(s) are affected? (Select all that apply) Client, Types, Documentation ### Auth config (if applicable) ```typescript ``` ### Additional context I tested my reproduction against the latest release.
GiteaMirror added the enhancement label 2026-03-13 09:29:13 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Oct 15, 2025):

Hi @wuzgood98! I'm Dosu and I’m helping the better-auth team.

The twoFactorPage option is referenced in the documentation and usage examples for the twoFactorClient plugin, but it is not actually implemented or typed in the plugin itself, which leads to TypeScript errors and confusion for users who try to use it (source). There are no existing pull requests that add this feature; the documentation currently shows the option, but the codebase does not support it (source).

To implement twoFactorPage, you would need to update the twoFactorClient plugin's TypeScript options interface to accept this property, and add logic to handle redirecting users to the specified page when 2FA verification is required. This would likely involve updating the plugin's runtime logic where redirects or hooks are processed. You should also update the documentation and usage examples to match the new implementation.

Until this is added, users have been working around the limitation by manually handling redirects in hooks or middleware after authentication events (source). If you want to contribute this feature, focus on updating the client plugin's options, redirect logic, and documentation for consistency.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Oct 15, 2025): <!-- Greeting --> Hi @wuzgood98! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> The twoFactorPage option is referenced in the documentation and usage examples for the twoFactorClient plugin, but it is not actually implemented or typed in the plugin itself, which leads to TypeScript errors and confusion for users who try to use it [(source)](https://github.com/better-auth/better-auth/issues/313). There are no existing pull requests that add this feature; the documentation currently shows the option, but the codebase does not support it [(source)](https://github.com/better-auth/better-auth/pull/4519). To implement twoFactorPage, you would need to update the twoFactorClient plugin's TypeScript options interface to accept this property, and add logic to handle redirecting users to the specified page when 2FA verification is required. This would likely involve updating the plugin's runtime logic where redirects or hooks are processed. You should also update the documentation and usage examples to match the new implementation. Until this is added, users have been working around the limitation by manually handling redirects in hooks or middleware after authentication events [(source)](https://github.com/better-auth/better-auth/issues/1279#issuecomment-2668210943). If you want to contribute this feature, focus on updating the client plugin's options, redirect logic, and documentation for consistency. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/9bdf1e98-ec80-4667-a952-57e8bdba713e?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/9bdf1e98-ec80-4667-a952-57e8bdba713e?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/9bdf1e98-ec80-4667-a952-57e8bdba713e?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/9bdf1e98-ec80-4667-a952-57e8bdba713e?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/9bdf1e98-ec80-4667-a952-57e8bdba713e?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/9bdf1e98-ec80-4667-a952-57e8bdba713e?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/9bdf1e98-ec80-4667-a952-57e8bdba713e?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/5326)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2129