[GH-ISSUE #4808] Username sign in does always sent email (does not care about sendOnSignIn) #27402

Closed
opened 2026-04-17 18:24:30 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @QuintenStr on GitHub (Sep 22, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/4808

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Add username plugin, set emailAndPassword.requireEmailVerification to true.
Set sendOnSignIn to false. Log in with unverified, username login. Each sign in sent email verification mail.

import { betterAuth } from "better-auth";
import { username } from "better-auth/plugins";

export const auth = betterAuth({
	emailAndPassword: {
		enabled: true,
		requireEmailVerification: true,
	},

	emailVerification: {
		sendOnSignUp: true,
		**sendOnSignIn: false,**
		autoSignInAfterVerification: true,
		expiresIn: 60 * 60,
	},

	plugins: [
		username(),
	],
});

Current vs. Expected behavior

Signing in with the username plugin does not care about sendOnSignIn: bool. Each sign in attempt for a unverified email user sents a email.

What version of Better Auth are you using?

1.3.13

System info

{
  "system": {
    "platform": "win32",
    "arch": "x64",
    "version": "Windows 11 Home",
    "release": "10.0.26100",
    "cpuCount": 16,
    "cpuModel": "Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz",
    "totalMemory": "31.90 GB",
    "freeMemory": "17.35 GB"
  },
  "node": {
    "version": "v22.14.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "11.4.2"
  },
  "frameworks": [
    {
      "name": "next",
      "version": "15.5.0"
    },
    {
      "name": "react",
      "version": "19.1.0"
    }
  ],
  "databases": [
    {
      "name": "postgres",
      "version": "^3.4.7"
    },
    {
      "name": "drizzle",
      "version": "^0.44.5"
    }
  ],
  "betterAuth": {
    "version": "^1.3.9",
    "config": {
      "logger": {
        "level": "debug",
        "disabled": false
      },
      "session": {
        "expiresIn": 604800,
        "updateAge": 86400,
        "cookieCache": {
          "enabled": true,
          "maxAge": 300
        }
      },
      "user": {
        "changeEmail": {
          "enabled": true
        },
        "deleteUser": {
          "enabled": true,
          "deleteTokenExpiresIn": 3600
        }
      },
      "emailAndPassword": {
        "enabled": true,
        "requireEmailVerification": true,
        "resetPasswordTokenExpiresIn": 3600
      },
      "emailVerification": {
        "sendOnSignUp": true,
        "sendOnSignIn": false,
        "autoSignInAfterVerification": true,
        "expiresIn": 3600
      },
      "plugins": [
        {
          "name": "username",
          "config": {
            "id": "username",
            "endpoints": {},
            "schema": {
              "user": {
                "fields": {
                  "username": {
                    "type": "string",
                    "required": false,
                    "sortable": true,
                    "unique": true,
                    "returned": true,
                    "transform": {}
                  },
                  "displayUsername": {
                    "type": "string",
                    "required": false,
                    "transform": {}
                  }
                }
              }
            },
            "hooks": {
              "before": [
                {},
                {}
              ]
            },
            "$ERROR_CODES": {
              "INVALID_USERNAME_OR_PASSWORD": "[REDACTED]",
              "EMAIL_NOT_VERIFIED": "Email not verified",
              "UNEXPECTED_ERROR": "Unexpected error",
              "USERNAME_IS_ALREADY_TAKEN": "Username is already taken. Please try another.",
              "USERNAME_TOO_SHORT": "Username is too short",
              "USERNAME_TOO_LONG": "Username is too long",
              "INVALID_USERNAME": "Username is invalid",
              "INVALID_DISPLAY_USERNAME": "Display username is invalid"
            }
          }
        },
        {
          "name": "last-login-method",
          "config": {
            "id": "last-login-method",
            "hooks": {
              "after": [
                {}
              ]
            }
          }
        },
        {
          "name": "admin",
          "config": {
            "id": "admin",
            "hooks": {
              "after": [
                {}
              ]
            },
            "endpoints": {},
            "$ERROR_CODES": {
              "FAILED_TO_CREATE_USER": "Failed to create user",
              "USER_ALREADY_EXISTS": "User already exists. Use another email.",
              "YOU_CANNOT_BAN_YOURSELF": "You cannot ban yourself",
              "YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE": "You are not allowed to change users role",
              "YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS": "You are not allowed to create users",
              "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS": "You are not allowed to list users",
              "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS": "You are not allowed to list users sessions",
              "YOU_ARE_NOT_ALLOWED_TO_BAN_USERS": "You are not allowed to ban users",
              "YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS": "You are not allowed to impersonate users",
              "YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS": "You are not allowed to revoke users sessions",
              "YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS": "You are not allowed to delete users",
              "YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD": "[REDACTED]",
              "BANNED_USER": "You have been banned from this application",
              "YOU_ARE_NOT_ALLOWED_TO_GET_USER": "You are not allowed to get user",
              "NO_DATA_TO_UPDATE": "No data to update",
              "YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS": "You are not allowed to update users",
              "YOU_CANNOT_REMOVE_YOURSELF": "You cannot remove yourself"
            },
            "schema": {
              "user": {
                "fields": {
                  "role": {
                    "type": "string",
                    "required": false,
                    "input": false
                  },
                  "banned": {
                    "type": "boolean",
                    "defaultValue": false,
                    "required": false,
                    "input": false
                  },
                  "banReason": {
                    "type": "string",
                    "required": false,
                    "input": false
                  },
                  "banExpires": {
                    "type": "date",
                    "required": false,
                    "input": false
                  }
                }
              },
              "session": {
                "fields": {
                  "impersonatedBy": {
                    "type": "string",
                    "required": false
                  }
                }
              }
            },
            "options": {
              "defaultRole": "user",
              "adminRoles": [
                "admin"
              ]
            }
          }
        },
        {
          "name": "next-cookies",
          "config": {
            "id": "next-cookies",
            "hooks": {
              "after": [
                {}
              ]
            }
          }
        }
      ]
    }
  }
}

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

Package

Auth config (if applicable)

import { betterAuth } from "better-auth";
import { username } from "better-auth/plugins";

export const auth = betterAuth({
	emailAndPassword: {
		enabled: true,
		requireEmailVerification: true,
	},

	emailVerification: {
		sendOnSignUp: true,
		**sendOnSignIn: false,**
		autoSignInAfterVerification: true,
		expiresIn: 60 * 60,
	},

	plugins: [
		username(),
	],
});

Additional context

No response

Originally created by @QuintenStr on GitHub (Sep 22, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/4808 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce Add username plugin, set emailAndPassword.requireEmailVerification to true. Set sendOnSignIn to false. Log in with unverified, username login. Each sign in sent email verification mail. ``` import { betterAuth } from "better-auth"; import { username } from "better-auth/plugins"; export const auth = betterAuth({ emailAndPassword: { enabled: true, requireEmailVerification: true, }, emailVerification: { sendOnSignUp: true, **sendOnSignIn: false,** autoSignInAfterVerification: true, expiresIn: 60 * 60, }, plugins: [ username(), ], }); ``` ### Current vs. Expected behavior Signing in with the username plugin does not care about sendOnSignIn: bool. Each sign in attempt for a unverified email user sents a email. ### What version of Better Auth are you using? 1.3.13 ### System info ```bash { "system": { "platform": "win32", "arch": "x64", "version": "Windows 11 Home", "release": "10.0.26100", "cpuCount": 16, "cpuModel": "Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz", "totalMemory": "31.90 GB", "freeMemory": "17.35 GB" }, "node": { "version": "v22.14.0", "env": "development" }, "packageManager": { "name": "npm", "version": "11.4.2" }, "frameworks": [ { "name": "next", "version": "15.5.0" }, { "name": "react", "version": "19.1.0" } ], "databases": [ { "name": "postgres", "version": "^3.4.7" }, { "name": "drizzle", "version": "^0.44.5" } ], "betterAuth": { "version": "^1.3.9", "config": { "logger": { "level": "debug", "disabled": false }, "session": { "expiresIn": 604800, "updateAge": 86400, "cookieCache": { "enabled": true, "maxAge": 300 } }, "user": { "changeEmail": { "enabled": true }, "deleteUser": { "enabled": true, "deleteTokenExpiresIn": 3600 } }, "emailAndPassword": { "enabled": true, "requireEmailVerification": true, "resetPasswordTokenExpiresIn": 3600 }, "emailVerification": { "sendOnSignUp": true, "sendOnSignIn": false, "autoSignInAfterVerification": true, "expiresIn": 3600 }, "plugins": [ { "name": "username", "config": { "id": "username", "endpoints": {}, "schema": { "user": { "fields": { "username": { "type": "string", "required": false, "sortable": true, "unique": true, "returned": true, "transform": {} }, "displayUsername": { "type": "string", "required": false, "transform": {} } } } }, "hooks": { "before": [ {}, {} ] }, "$ERROR_CODES": { "INVALID_USERNAME_OR_PASSWORD": "[REDACTED]", "EMAIL_NOT_VERIFIED": "Email not verified", "UNEXPECTED_ERROR": "Unexpected error", "USERNAME_IS_ALREADY_TAKEN": "Username is already taken. Please try another.", "USERNAME_TOO_SHORT": "Username is too short", "USERNAME_TOO_LONG": "Username is too long", "INVALID_USERNAME": "Username is invalid", "INVALID_DISPLAY_USERNAME": "Display username is invalid" } } }, { "name": "last-login-method", "config": { "id": "last-login-method", "hooks": { "after": [ {} ] } } }, { "name": "admin", "config": { "id": "admin", "hooks": { "after": [ {} ] }, "endpoints": {}, "$ERROR_CODES": { "FAILED_TO_CREATE_USER": "Failed to create user", "USER_ALREADY_EXISTS": "User already exists. Use another email.", "YOU_CANNOT_BAN_YOURSELF": "You cannot ban yourself", "YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE": "You are not allowed to change users role", "YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS": "You are not allowed to create users", "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS": "You are not allowed to list users", "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS": "You are not allowed to list users sessions", "YOU_ARE_NOT_ALLOWED_TO_BAN_USERS": "You are not allowed to ban users", "YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS": "You are not allowed to impersonate users", "YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS": "You are not allowed to revoke users sessions", "YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS": "You are not allowed to delete users", "YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD": "[REDACTED]", "BANNED_USER": "You have been banned from this application", "YOU_ARE_NOT_ALLOWED_TO_GET_USER": "You are not allowed to get user", "NO_DATA_TO_UPDATE": "No data to update", "YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS": "You are not allowed to update users", "YOU_CANNOT_REMOVE_YOURSELF": "You cannot remove yourself" }, "schema": { "user": { "fields": { "role": { "type": "string", "required": false, "input": false }, "banned": { "type": "boolean", "defaultValue": false, "required": false, "input": false }, "banReason": { "type": "string", "required": false, "input": false }, "banExpires": { "type": "date", "required": false, "input": false } } }, "session": { "fields": { "impersonatedBy": { "type": "string", "required": false } } } }, "options": { "defaultRole": "user", "adminRoles": [ "admin" ] } } }, { "name": "next-cookies", "config": { "id": "next-cookies", "hooks": { "after": [ {} ] } } } ] } } } ``` ### Which area(s) are affected? (Select all that apply) Package ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth"; import { username } from "better-auth/plugins"; export const auth = betterAuth({ emailAndPassword: { enabled: true, requireEmailVerification: true, }, emailVerification: { sendOnSignUp: true, **sendOnSignIn: false,** autoSignInAfterVerification: true, expiresIn: 60 * 60, }, plugins: [ username(), ], }); ``` ### Additional context _No response_
GiteaMirror added the lockedbug labels 2026-04-17 18:24:30 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Sep 22, 2025):

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

This is a known issue: the username plugin's sign-in flow does not respect the sendOnSignIn flag, so a verification email is sent on every sign-in attempt for unverified users, regardless of your config. This is inconsistent with the main email/password flow, which does respect the flag. The fix is to add a conditional check for sendOnSignIn before sending the verification email in the username plugin's sign-in logic, matching the main flow's behavior. There is currently no official workaround or patch in recent releases—if you need to resolve this immediately, you would need to patch the plugin locally by adding this check yourself. For reference, see the related discussion and code details here and here.

To reply, just mention @dosu.


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

<!-- gh-comment-id:3317105271 --> @dosubot[bot] commented on GitHub (Sep 22, 2025): <!-- Greeting --> Hi @QuintenStr! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This is a known issue: the username plugin's sign-in flow does not respect the `sendOnSignIn` flag, so a verification email is sent on every sign-in attempt for unverified users, regardless of your config. This is inconsistent with the main email/password flow, which does respect the flag. The fix is to add a conditional check for `sendOnSignIn` before sending the verification email in the username plugin's sign-in logic, matching the main flow's behavior. There is currently no official workaround or patch in recent releases—if you need to resolve this immediately, you would need to patch the plugin locally by adding this check yourself. For reference, see the related discussion and code details [here](https://github.com/better-auth/better-auth/blob/1baaccbf52803d27046dce3da57c5c7201a020fb/packages/better-auth/src/plugins/username/index.ts) and [here](https://github.com/better-auth/better-auth/issues/3494). <!-- 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/0b21f3d7-5836-4edf-b8a0-811dcf8c7cc7?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/0b21f3d7-5836-4edf-b8a0-811dcf8c7cc7?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/0b21f3d7-5836-4edf-b8a0-811dcf8c7cc7?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/0b21f3d7-5836-4edf-b8a0-811dcf8c7cc7?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/0b21f3d7-5836-4edf-b8a0-811dcf8c7cc7?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/0b21f3d7-5836-4edf-b8a0-811dcf8c7cc7?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/0b21f3d7-5836-4edf-b8a0-811dcf8c7cc7?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/4808)
Author
Owner

@QuintenStr commented on GitHub (Sep 22, 2025):

PR: https://github.com/better-auth/better-auth/pull/4799

<!-- gh-comment-id:3317116346 --> @QuintenStr commented on GitHub (Sep 22, 2025): PR: https://github.com/better-auth/better-auth/pull/4799
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#27402