Create User from Admin doesn't respect plugins #1924

Closed
opened 2026-03-13 09:12:53 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @anmol-fzr on GitHub (Sep 14, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Setup a Better Auth
  2. Add phoneNumber and admin Plugin on both client and server
  3. use create-user endpoint via admin plugin's authClient.admin.createUser

Now Even if you pass the phoneNumber to createUser it won't get added to the newly created user,

Current vs. Expected behavior

create-user endpoint in admin plugin's authClient.admin.createUser must add phoneNumber if i'm using phoneNumber plugin

What version of Better Auth are you using?

1.3.4

System info

{
  "system": {
    "platform": "linux",
    "arch": "x64",
    "version": "#1 SMP PREEMPT_DYNAMIC Sun, 06 Jul 2025 11:14:36 +0000",
    "release": "6.15.5-arch1-1",
    "cpuCount": 16,
    "cpuModel": "12th Gen Intel(R) Core(TM) i5-1240P",
    "totalMemory": "23.17 GB",
    "freeMemory": "18.53 GB"
  },
  "node": {
    "version": "v20.19.3",
    "env": "development"
  },
  "packageManager": {
    "name": "bun",
    "version": "1.2.21"
  },
  "frameworks": null,
  "databases": null,
  "betterAuth": {
    "version": "0.0.0",
    "config": {
      "appName": "safe-fin-api",
      "session": {
        "cookieCache": {
          "enabled": true,
          "maxAge": 216000
        }
      },
      "plugins": [
        {
          "name": "expo",
          "config": {
            "id": "expo",
            "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",
              "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",
              "NO_DATA_TO_UPDATE": "No data to update",
              "YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS": "You are not allowed to update users"
            },
            "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": {
              "adminUserIds": [
                "F7EOrNtgbhOUA4FYvj0r7bN4eJykYGMb"
              ]
            }
          }
        },
        {
          "name": "open-api",
          "config": {
            "id": "open-api",
            "endpoints": {}
          }
        },
        {
          "name": "multi-session",
          "config": {
            "id": "multi-session",
            "endpoints": {},
            "hooks": {
              "after": [
                {},
                {}
              ]
            },
            "$ERROR_CODES": {
              "INVALID_SESSION_TOKEN": "[REDACTED]"
            }
          }
        },
        {
          "name": "phone-number",
          "config": {
            "id": "phone-number",
            "endpoints": {},
            "schema": {
              "user": {
                "fields": {
                  "phoneNumber": {
                    "type": "string",
                    "required": false,
                    "unique": true,
                    "sortable": true,
                    "returned": true
                  },
                  "phoneNumberVerified": {
                    "type": "boolean",
                    "required": false,
                    "returned": true,
                    "input": false
                  }
                }
              }
            },
            "rateLimit": [
              {
                "window": 60000,
                "max": 10
              }
            ],
            "$ERROR_CODES": {
              "INVALID_PHONE_NUMBER": "Invalid phone number",
              "PHONE_NUMBER_EXIST": "Phone number already exists",
              "INVALID_PHONE_NUMBER_OR_PASSWORD": "[REDACTED]",
              "UNEXPECTED_ERROR": "Unexpected error",
              "OTP_NOT_FOUND": "OTP not found",
              "OTP_EXPIRED": "OTP expired",
              "INVALID_OTP": "Invalid OTP",
              "PHONE_NUMBER_NOT_VERIFIED": "Phone number not verified"
            }
          }
        }
      ],
      "baseURL": "http://localhost:8787",
      "secret": "[REDACTED]",
      "trustedOrigins": [
        "http://localhost:5173",
        "http://192.168.29.57:5173"
      ]
    }
  }
}

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

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  emailAndPassword: {  
    enabled: true
  },
	plugins: [
		expo(),
		admin({
			adminUserIds: ["F7EOrNtgbhOUA4FYvj0r7bN4eJykYGMb"],
		}),
		openAPI(),
		multiSession(),
		phoneNumber({
			allowedAttempts: 3,
			sendOTP: ({ phoneNumber, code }) => {
				console.info({ phoneNumber, code });
			},
			signUpOnVerification: {
				getTempEmail: (phoneNumber) => phoneNumber,
				getTempName: (phoneNumber) => phoneNumber,
			},
		}),
	],

});

Additional context

No response

Originally created by @anmol-fzr on GitHub (Sep 14, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Setup a Better Auth 2. Add `phoneNumber` and `admin` Plugin on both `client` and `server` 3. use `create-user` endpoint via admin plugin's `authClient.admin.createUser` Now Even if you pass the phoneNumber to createUser it won't get added to the newly created user, ### Current vs. Expected behavior `create-user` endpoint in admin plugin's `authClient.admin.createUser` must add phoneNumber if i'm using `phoneNumber` plugin ### What version of Better Auth are you using? 1.3.4 ### System info ```bash { "system": { "platform": "linux", "arch": "x64", "version": "#1 SMP PREEMPT_DYNAMIC Sun, 06 Jul 2025 11:14:36 +0000", "release": "6.15.5-arch1-1", "cpuCount": 16, "cpuModel": "12th Gen Intel(R) Core(TM) i5-1240P", "totalMemory": "23.17 GB", "freeMemory": "18.53 GB" }, "node": { "version": "v20.19.3", "env": "development" }, "packageManager": { "name": "bun", "version": "1.2.21" }, "frameworks": null, "databases": null, "betterAuth": { "version": "0.0.0", "config": { "appName": "safe-fin-api", "session": { "cookieCache": { "enabled": true, "maxAge": 216000 } }, "plugins": [ { "name": "expo", "config": { "id": "expo", "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", "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", "NO_DATA_TO_UPDATE": "No data to update", "YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS": "You are not allowed to update users" }, "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": { "adminUserIds": [ "F7EOrNtgbhOUA4FYvj0r7bN4eJykYGMb" ] } } }, { "name": "open-api", "config": { "id": "open-api", "endpoints": {} } }, { "name": "multi-session", "config": { "id": "multi-session", "endpoints": {}, "hooks": { "after": [ {}, {} ] }, "$ERROR_CODES": { "INVALID_SESSION_TOKEN": "[REDACTED]" } } }, { "name": "phone-number", "config": { "id": "phone-number", "endpoints": {}, "schema": { "user": { "fields": { "phoneNumber": { "type": "string", "required": false, "unique": true, "sortable": true, "returned": true }, "phoneNumberVerified": { "type": "boolean", "required": false, "returned": true, "input": false } } } }, "rateLimit": [ { "window": 60000, "max": 10 } ], "$ERROR_CODES": { "INVALID_PHONE_NUMBER": "Invalid phone number", "PHONE_NUMBER_EXIST": "Phone number already exists", "INVALID_PHONE_NUMBER_OR_PASSWORD": "[REDACTED]", "UNEXPECTED_ERROR": "Unexpected error", "OTP_NOT_FOUND": "OTP not found", "OTP_EXPIRED": "OTP expired", "INVALID_OTP": "Invalid OTP", "PHONE_NUMBER_NOT_VERIFIED": "Phone number not verified" } } } ], "baseURL": "http://localhost:8787", "secret": "[REDACTED]", "trustedOrigins": [ "http://localhost:5173", "http://192.168.29.57:5173" ] } } } ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ emailAndPassword: { enabled: true }, plugins: [ expo(), admin({ adminUserIds: ["F7EOrNtgbhOUA4FYvj0r7bN4eJykYGMb"], }), openAPI(), multiSession(), phoneNumber({ allowedAttempts: 3, sendOTP: ({ phoneNumber, code }) => { console.info({ phoneNumber, code }); }, signUpOnVerification: { getTempEmail: (phoneNumber) => phoneNumber, getTempName: (phoneNumber) => phoneNumber, }, }), ], }); ``` ### Additional context _No response_
GiteaMirror added the plugin label 2026-03-13 09:12:53 -05:00
Author
Owner

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

The admin plugin's createUser endpoint does not automatically merge or validate fields from other plugins like phoneNumber. Instead, it only includes its own fields and a generic data object for extra fields. If you want to add fields from plugins (such as phoneNumber), you need to pass them inside the data property when calling createUser. However, even when you do this, those fields may not be validated or persisted unless the central schema validation (parseUserInput) is invoked before user creation. This means plugin fields can be ignored or dropped if the admin endpoint doesn't explicitly handle them, which matches what you're seeing reference.

This is a limitation of the current implementation. As a workaround, ensure you pass plugin fields in the data object, and consider using user lifecycle hooks (like before user creation) to merge or validate these fields if your setup allows it. There are no plugin registration order requirements for schema merging; all user fields are aggregated centrally, but only if the relevant validation step is used reference.

If you need strict enforcement of plugin fields during admin user creation, you may need to customize the admin endpoint or add a validation step that uses the merged schema from all plugins.

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 (Sep 14, 2025): <!-- Answer --> The admin plugin's `createUser` endpoint does not automatically merge or validate fields from other plugins like `phoneNumber`. Instead, it only includes its own fields and a generic `data` object for extra fields. If you want to add fields from plugins (such as `phoneNumber`), you need to pass them inside the `data` property when calling `createUser`. However, even when you do this, those fields may not be validated or persisted unless the central schema validation (`parseUserInput`) is invoked before user creation. This means plugin fields can be ignored or dropped if the admin endpoint doesn't explicitly handle them, which matches what you're seeing [reference](https://github.com/better-auth/better-auth/blob/5b06218d7fd6673df2fdfbade1640d9bed0f2906/packages/better-auth/src/plugins/admin/admin.ts). This is a limitation of the current implementation. As a workaround, ensure you pass plugin fields in the `data` object, and consider using user lifecycle hooks (like `before` user creation) to merge or validate these fields if your setup allows it. There are no plugin registration order requirements for schema merging; all user fields are aggregated centrally, but only if the relevant validation step is used [reference](https://github.com/better-auth/better-auth/blob/5b06218d7fd6673df2fdfbade1640d9bed0f2906/packages/better-auth/src/db/schema.ts). If you need strict enforcement of plugin fields during admin user creation, you may need to customize the admin endpoint or add a validation step that uses the merged schema from all plugins. <!-- 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/f2e479ce-4004-47c6-a4db-bf97998feea5?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/f2e479ce-4004-47c6-a4db-bf97998feea5?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/f2e479ce-4004-47c6-a4db-bf97998feea5?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/f2e479ce-4004-47c6-a4db-bf97998feea5?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/f2e479ce-4004-47c6-a4db-bf97998feea5?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/f2e479ce-4004-47c6-a4db-bf97998feea5?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/f2e479ce-4004-47c6-a4db-bf97998feea5?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/4651)
Author
Owner

@Bekacru commented on GitHub (Sep 30, 2025):

you need to use the generic data field to add those data

@Bekacru commented on GitHub (Sep 30, 2025): you need to use the generic `data` field to add those data
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1924