[GH-ISSUE #7796] [Admin] impersonatedBy field type mismatch with id #28231

Open
opened 2026-04-17 19:41:12 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @allanice001 on GitHub (Feb 4, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/7796

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Create Backend:

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: "pg",
    schema: authSchema,
  }),
  advanced: {
    database: {
      generateId: "uuid",
    },
  },
}

Generate Migrations:

@better-auth/cli@latest generate

in the resulting migration:

export const session = pgTable(
  "session",
  {
    id: uuid("id").defaultRandom().primaryKey(),
    expiresAt: timestamp("expires_at").notNull(),
    token: text("token").notNull().unique(),
    createdAt: timestamp("created_at").defaultNow().notNull(),
    updatedAt: timestamp("updated_at")
      .$onUpdate(() => /* @__PURE__ */ new Date())
      .notNull(),
    ipAddress: text("ip_address"),
    userAgent: text("user_agent"),
    userId: uuid("user_id")
      .notNull()
      .references(() => user.id, { onDelete: "cascade" }),
    impersonatedBy: text("impersonated_by"),
  },
  (table) => [index("session_userId_idx").on(table.userId)],
);

but because i specify generateId: "uuid", i believe
impersonatedBy: text("impersonated_by"),
should be
impersonatedBy: uuid("impersonated_by"),
at a minimum, optimally, maybe
impersonatedBy: uuid("impersonated_by").references(() => user.id, { onDelete: "set null" }),
because the generated user table looks like this:

export const user = pgTable("user", {
  id: uuid("id").defaultRandom().primaryKey(),
  name: text("name").notNull(),
  email: text("email").notNull().unique(),
  emailVerified: boolean("email_verified").default(false).notNull(),
  image: text("image"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at")
    .defaultNow()
    .$onUpdate(() => /* @__PURE__ */ new Date())
    .notNull(),
  role: text("role"),
  banned: boolean("banned").default(false),
  banReason: text("ban_reason"),
  banExpires: timestamp("ban_expires"),
});

(i updated the generated
id: uuid("id") .default(sqlpg_catalog.gen_random_uuid()) .primaryKey(),
to be
`
id: uuid("id").defaultRandom().primaryKey(),

`
additionally, you could also leverate the DB for updating timestams as follows:

updatedAt: timestamp("updated_at").defaultNow().$onUpdate(() => new Date()).notNull(),

Package versions:
"better-auth": "^1.4.18",
"drizzle-orm": "^0.45.1",
"drizzle-kit": "^0.31.8",

Current vs. Expected behavior

I expect impersonatedBy to be a UUID field, not text
(other improvements mentioned in the report above)

What version of Better Auth are you using?

1.4.18

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:41 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6031",
    "release": "25.2.0",
    "cpuCount": 14,
    "cpuModel": "Apple M3 Max",
    "totalMemory": "36.00 GB",
    "freeMemory": "0.18 GB"
  },
  "node": {
    "version": "v24.11.1",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "11.6.2"
  },
  "frameworks": [
    {
      "name": "next",
      "version": "16.1.6"
    },
    {
      "name": "react",
      "version": "19.2.3"
    }
  ],
  "databases": [
    {
      "name": "pg",
      "version": "^8.18.0"
    },
    {
      "name": "drizzle",
      "version": "^0.45.1"
    }
  ],
  "betterAuth": {
    "version": "^1.4.18",
    "config": {
      "advanced": {
        "database": {
          "generateId": "uuid"
        }
      },
      "emailAndPassword": {
        "enabled": true,
        "autoSignIn": false
      },
      "plugins": [
        {
          "name": "open-api",
          "config": {
            "id": "open-api",
            "endpoints": {}
          }
        },
        {
          "name": "admin",
          "config": {
            "id": "admin",
            "hooks": {
              "after": [
                {}
              ]
            },
            "endpoints": {},
            "$ERROR_CODES": {
              "FAILED_TO_CREATE_USER": "Failed to create user",
              "USER_ALREADY_EXISTS": "User already exists.",
              "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL": "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",
              "YOU_ARE_NOT_ALLOWED_TO_SET_NON_EXISTENT_VALUE": "You are not allowed to set a non-existent role value",
              "YOU_CANNOT_IMPERSONATE_ADMINS": "You cannot impersonate admins",
              "INVALID_ROLE_TYPE": "Invalid role type"
            },
            "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": {
              "ac": {
                "statements": {
                  "user": [
                    "create",
                    "list",
                    "set-role",
                    "ban",
                    "impersonate",
                    "delete",
                    "set-password",
                    "get",
                    "update"
                  ],
                  "session": [
                    "list",
                    "revoke",
                    "delete"
                  ]
                }
              },
              "roles": {
                "admin": {
                  "statements": {
                    "user": [
                      "create",
                      "list",
                      "set-role",
                      "ban",
                      "impersonate",
                      "delete",
                      "set-password",
                      "get",
                      "update"
                    ],
                    "session": [
                      "list",
                      "revoke",
                      "delete"
                    ]
                  }
                }
              }
            }
          }
        },
        {
          "name": "next-cookies",
          "config": {
            "id": "next-cookies",
            "hooks": {
              "after": [
                {}
              ]
            }
          }
        }
      ]
    }
  }
}

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

Other

Auth config (if applicable)

import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "@/db";
import { admin, openAPI } from "better-auth/plugins";
import { nextCookies } from "better-auth/next-js";
import { ac, allRoles } from "@/lib/permissions";
import { authSchema } from "@/db/schema";

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: "pg",
    schema: authSchema,
  }),
  advanced: {
    database: {
      generateId: "uuid",
    },
  },
  emailAndPassword: {
    enabled: true,
    autoSignIn: false,
  },
  plugins: [
    openAPI(),
    admin({
      ac,
      roles: allRoles,
    }),
    nextCookies(),
  ],
});

Additional context

No response

Originally created by @allanice001 on GitHub (Feb 4, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/7796 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce Create Backend: ``` export const auth = betterAuth({ database: drizzleAdapter(db, { provider: "pg", schema: authSchema, }), advanced: { database: { generateId: "uuid", }, }, } ``` Generate Migrations: ``` @better-auth/cli@latest generate ``` in the resulting migration: ``` export const session = pgTable( "session", { id: uuid("id").defaultRandom().primaryKey(), expiresAt: timestamp("expires_at").notNull(), token: text("token").notNull().unique(), createdAt: timestamp("created_at").defaultNow().notNull(), updatedAt: timestamp("updated_at") .$onUpdate(() => /* @__PURE__ */ new Date()) .notNull(), ipAddress: text("ip_address"), userAgent: text("user_agent"), userId: uuid("user_id") .notNull() .references(() => user.id, { onDelete: "cascade" }), impersonatedBy: text("impersonated_by"), }, (table) => [index("session_userId_idx").on(table.userId)], ); ``` but because i specify `generateId: "uuid",` i believe ` impersonatedBy: text("impersonated_by"), ` should be ` impersonatedBy: uuid("impersonated_by"),` at a minimum, optimally, maybe ` impersonatedBy: uuid("impersonated_by").references(() => user.id, { onDelete: "set null" }), ` because the generated user table looks like this: ``` export const user = pgTable("user", { id: uuid("id").defaultRandom().primaryKey(), name: text("name").notNull(), email: text("email").notNull().unique(), emailVerified: boolean("email_verified").default(false).notNull(), image: text("image"), createdAt: timestamp("created_at").defaultNow().notNull(), updatedAt: timestamp("updated_at") .defaultNow() .$onUpdate(() => /* @__PURE__ */ new Date()) .notNull(), role: text("role"), banned: boolean("banned").default(false), banReason: text("ban_reason"), banExpires: timestamp("ban_expires"), }); ``` (i updated the generated ` id: uuid("id") .default(sql`pg_catalog.gen_random_uuid()`) .primaryKey(), ` to be ` id: uuid("id").defaultRandom().primaryKey(), ` additionally, you could also leverate the DB for updating timestams as follows: ``` updatedAt: timestamp("updated_at").defaultNow().$onUpdate(() => new Date()).notNull(), ``` Package versions: "better-auth": "^1.4.18", "drizzle-orm": "^0.45.1", "drizzle-kit": "^0.31.8", ### Current vs. Expected behavior I expect impersonatedBy to be a UUID field, not text (other improvements mentioned in the report above) ### What version of Better Auth are you using? 1.4.18 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:41 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6031", "release": "25.2.0", "cpuCount": 14, "cpuModel": "Apple M3 Max", "totalMemory": "36.00 GB", "freeMemory": "0.18 GB" }, "node": { "version": "v24.11.1", "env": "development" }, "packageManager": { "name": "npm", "version": "11.6.2" }, "frameworks": [ { "name": "next", "version": "16.1.6" }, { "name": "react", "version": "19.2.3" } ], "databases": [ { "name": "pg", "version": "^8.18.0" }, { "name": "drizzle", "version": "^0.45.1" } ], "betterAuth": { "version": "^1.4.18", "config": { "advanced": { "database": { "generateId": "uuid" } }, "emailAndPassword": { "enabled": true, "autoSignIn": false }, "plugins": [ { "name": "open-api", "config": { "id": "open-api", "endpoints": {} } }, { "name": "admin", "config": { "id": "admin", "hooks": { "after": [ {} ] }, "endpoints": {}, "$ERROR_CODES": { "FAILED_TO_CREATE_USER": "Failed to create user", "USER_ALREADY_EXISTS": "User already exists.", "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL": "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", "YOU_ARE_NOT_ALLOWED_TO_SET_NON_EXISTENT_VALUE": "You are not allowed to set a non-existent role value", "YOU_CANNOT_IMPERSONATE_ADMINS": "You cannot impersonate admins", "INVALID_ROLE_TYPE": "Invalid role type" }, "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": { "ac": { "statements": { "user": [ "create", "list", "set-role", "ban", "impersonate", "delete", "set-password", "get", "update" ], "session": [ "list", "revoke", "delete" ] } }, "roles": { "admin": { "statements": { "user": [ "create", "list", "set-role", "ban", "impersonate", "delete", "set-password", "get", "update" ], "session": [ "list", "revoke", "delete" ] } } } } } }, { "name": "next-cookies", "config": { "id": "next-cookies", "hooks": { "after": [ {} ] } } } ] } } } ``` ### Which area(s) are affected? (Select all that apply) Other ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { db } from "@/db"; import { admin, openAPI } from "better-auth/plugins"; import { nextCookies } from "better-auth/next-js"; import { ac, allRoles } from "@/lib/permissions"; import { authSchema } from "@/db/schema"; export const auth = betterAuth({ database: drizzleAdapter(db, { provider: "pg", schema: authSchema, }), advanced: { database: { generateId: "uuid", }, }, emailAndPassword: { enabled: true, autoSignIn: false, }, plugins: [ openAPI(), admin({ ac, roles: allRoles, }), nextCookies(), ], }); ``` ### Additional context _No response_
GiteaMirror added the databasebugorganization labels 2026-04-17 19:41:13 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Feb 4, 2026):

It looks like you are raising a valid concern regarding the impersonatedBy field in the generated migration schema. Since you specified generateId: "uuid", it indeed would make sense for impersonatedBy to be a UUID type instead of text.

Customizing field types is consistent with Better Auth’s capabilities, as it allows for extensions of the core schema by adding additional fields with custom types. You can change the field type of impersonatedBy to UUID as you suggested, which aligns with best practices for maintaining consistent data types within your database.

If you believe this is an oversight in the migration generation, you might want to open an issue on GitHub or tag the Better Auth team members, Beka (@bekacru) and Max (@ping__), for further assistance.

How else can I assist you with your Better Auth integration?

If you need more help, tag @better-auth-agent in a comment so I can respond.

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3849623914 --> @better-auth-agent[bot] commented on GitHub (Feb 4, 2026): It looks like you are raising a valid concern regarding the `impersonatedBy` field in the generated migration schema. Since you specified `generateId: "uuid"`, it indeed would make sense for `impersonatedBy` to be a UUID type instead of text. Customizing field types is consistent with Better Auth’s capabilities, as it allows for extensions of the core schema by adding additional fields with custom types. You can change the field type of `impersonatedBy` to UUID as you suggested, which aligns with best practices for maintaining consistent data types within your database. If you believe this is an oversight in the migration generation, you might want to open an issue on GitHub or tag the Better Auth team members, Beka (@bekacru) and Max (@ping__), for further assistance. How else can I assist you with your Better Auth integration? _If you need more help, tag @better-auth-agent in a comment so I can respond._ <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/better-auth) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/better-auth/better-auth)
Author
Owner

@bytaesu commented on GitHub (Feb 4, 2026):

Hi @allanice001, I'm checking 🧐

<!-- gh-comment-id:3850184678 --> @bytaesu commented on GitHub (Feb 4, 2026): Hi @allanice001, I'm checking 🧐
Author
Owner

@bytaesu commented on GitHub (Feb 5, 2026):

Hi @allanice001,

This is expected since a FK constraint cannot be enforced on this field. Because it is stored in the session table, referencing it via a FK in secondary-storage is not possible. When using secondary storage, the data could be stored in a KV store.

<!-- gh-comment-id:3854263034 --> @bytaesu commented on GitHub (Feb 5, 2026): Hi @allanice001, This is expected since a FK constraint cannot be enforced on this field. Because it is stored in the session table, referencing it via a FK in secondary-storage is not possible. When using secondary storage, the data could be stored in a KV store.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#28231