LastLoginMethod not being included when i call the api (saved to DB) #1937

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

Originally created by @Rayyan-Balami on GitHub (Sep 16, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

(this sends the userwithrole type) there is no lastMethodLogin,
banExpires? banReason? banned? createdAt email emailVerified id image? name role?
updatedAt
)
export const useGetUserById = (id: string) => {
return useQuery({
queryKey: ADMIN_KEYS.userById(id),
queryFn: () =>
authClient.admin.listUsers({
query: {
// use exact-match filter on the id field
filterField: "id",
filterValue: id,
limit: 1,
offset: 0,
},
}),
select: (data) => data.data?.users?.[0],
});
};

{this NEDDS TO BE FIXED, i wanted to get an user i cant get all detsils from this api, (property) data: {
id: string;
createdAt: Date;
updatedAt: Date;
email: string;
emailVerified: boolean;
name: string;
image?: string | null | undefined;
} | null }
export const useGetUserById = (id: string) => {
return useQuery({
queryKey: ADMIN_KEYS.userById(id),
queryFn: () =>
authClient.admin.getUser({
query: {
id: id,
},
}),
select: (data) => data.data,
});
};

Current vs. Expected behavior

need all user data from the user collection (using mongodb + mongoose)

What version of Better Auth are you using?

1.3.11

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 24.6.0: Mon Jul 14 11:30:34 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T8103",
    "release": "24.6.0",
    "cpuCount": 8,
    "cpuModel": "Apple M1",
    "totalMemory": "8.00 GB",
    "freeMemory": "0.10 GB"
  },
  "node": {
    "version": "v22.17.1",
    "env": "development"
  },
  "packageManager": {
    "name": "pnpm",
    "version": "10.16.1"
  },
  "frameworks": null,
  "databases": null,
  "betterAuth": {
    "version": "Unknown",
    "config": null
  }
}

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

Backend, Client, Documentation, Types

Auth config (if applicable)

import {
  BETTER_AUTH_SECRET,
  BETTER_AUTH_URL,
  CORS_ORIGIN,
  GOOGLE_CLIENT_ID,
  GOOGLE_CLIENT_SECRET,
  IS_PROD,
  MICROSOFT_CLIENT_ID,
  MICROSOFT_CLIENT_SECRET,
  REQUIRE_EMAIL_VERIFICATION,
} from "@/config/constants";
import {
  addProviderEmail,
  ProviderId,
} from "@/features/user/services/addProviderEmail.service";
import { client } from "@/lib/mongo";
import { userRoleEnum } from "@packages/shared/schemas/user";
import { betterAuth } from "better-auth";
import { mongodbAdapter } from "better-auth/adapters/mongodb";
import { admin, lastLoginMethod } from "better-auth/plugins";
import { sendResetPasswordEmail, sendVerificationEmail } from "./resend";

export const auth = betterAuth({
  database: mongodbAdapter(client),
  secret: BETTER_AUTH_SECRET,
  baseURL: BETTER_AUTH_URL,
  trustedOrigins: [CORS_ORIGIN],
  errorURL: `${CORS_ORIGIN}/auth/error`,
  account: {
    accountLinking: {
      enabled: true,
      allowDifferentEmails: true,
      updateUserInfoOnLink: true,
    },
  },
  // also tried this but it didn't work
  // user: {
  //   additionalFields: {
  //     lastLoginMethod: {
  //       type: "string",
  //       required: false,
  //     },
  //   },
  // },
  plugins: [
    admin({
      defaultRole: userRoleEnum.USER,
      adminRoles: [userRoleEnum.ADMIN],
    }),
    lastLoginMethod({
      storeInDatabase: true,
    }),
  ],
  emailAndPassword: {
    enabled: true,
    requireEmailVerification: REQUIRE_EMAIL_VERIFICATION,
    sendResetPassword: async ({ user, url, token }, request) => {
      await sendResetPasswordEmail(user, url);
    },
  },
  emailVerification: {
    sendOnSignUp: true,
    autoSignInAfterVerification: true,
    sendVerificationEmail: async ({ user, url, token }, request) => {
      await sendVerificationEmail(user, url);
    },
  },
  session: {
    expiresIn: 60 * 60 * 24 * 7, // 7 days
    updateAge: 60 * 60 * 24, // 1 day
  },
  advanced: {
    defaultCookieAttributes: {
      sameSite: "lax",
      secure: IS_PROD,
      httpOnly: true,
    },
  },
  socialProviders: {
    google: {
      prompt: "select_account",
      clientId: GOOGLE_CLIENT_ID,
      clientSecret: GOOGLE_CLIENT_SECRET,
      // optional: scope, accessType, etc.
    },
    microsoft: {
      clientId: MICROSOFT_CLIENT_ID,
      clientSecret: MICROSOFT_CLIENT_SECRET,
      tenantId: "common",
      prompt: "select_account",
    },
  },
  databaseHooks: {
    account: {
      create: {
        after: async (account) => {
          // skip if providerId is credentials
          if (account.providerId === "credentials") {
            return;
          }

          // Use service to add provider email
          await addProviderEmail(
            account.id,
            account.providerId as ProviderId,
            account.accessToken || undefined
          );
        },
      },
    },
  },
});

// Use the shared types for consistency
export type Session = typeof auth.$Infer.Session.session;
export type User = typeof auth.$Infer.Session.user;

Additional context

No response

Originally created by @Rayyan-Balami on GitHub (Sep 16, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce (this sends the userwithrole type) there is no lastMethodLogin, banExpires? banReason? banned? createdAt email emailVerified id image? name role? updatedAt ) export const useGetUserById = (id: string) => { return useQuery({ queryKey: ADMIN_KEYS.userById(id), queryFn: () => authClient.admin.listUsers({ query: { // use exact-match filter on the id field filterField: "id", filterValue: id, limit: 1, offset: 0, }, }), select: (data) => data.data?.users?.[0], }); }; {this NEDDS TO BE FIXED, i wanted to get an user i cant get all detsils from this api, (property) data: { id: string; createdAt: Date; updatedAt: Date; email: string; emailVerified: boolean; name: string; image?: string | null | undefined; } | null } export const useGetUserById = (id: string) => { return useQuery({ queryKey: ADMIN_KEYS.userById(id), queryFn: () => authClient.admin.getUser({ query: { id: id, }, }), select: (data) => data.data, }); }; ### Current vs. Expected behavior need all user data from the user collection (using mongodb + mongoose) ### What version of Better Auth are you using? 1.3.11 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 24.6.0: Mon Jul 14 11:30:34 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T8103", "release": "24.6.0", "cpuCount": 8, "cpuModel": "Apple M1", "totalMemory": "8.00 GB", "freeMemory": "0.10 GB" }, "node": { "version": "v22.17.1", "env": "development" }, "packageManager": { "name": "pnpm", "version": "10.16.1" }, "frameworks": null, "databases": null, "betterAuth": { "version": "Unknown", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Backend, Client, Documentation, Types ### Auth config (if applicable) ```typescript import { BETTER_AUTH_SECRET, BETTER_AUTH_URL, CORS_ORIGIN, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, IS_PROD, MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRET, REQUIRE_EMAIL_VERIFICATION, } from "@/config/constants"; import { addProviderEmail, ProviderId, } from "@/features/user/services/addProviderEmail.service"; import { client } from "@/lib/mongo"; import { userRoleEnum } from "@packages/shared/schemas/user"; import { betterAuth } from "better-auth"; import { mongodbAdapter } from "better-auth/adapters/mongodb"; import { admin, lastLoginMethod } from "better-auth/plugins"; import { sendResetPasswordEmail, sendVerificationEmail } from "./resend"; export const auth = betterAuth({ database: mongodbAdapter(client), secret: BETTER_AUTH_SECRET, baseURL: BETTER_AUTH_URL, trustedOrigins: [CORS_ORIGIN], errorURL: `${CORS_ORIGIN}/auth/error`, account: { accountLinking: { enabled: true, allowDifferentEmails: true, updateUserInfoOnLink: true, }, }, // also tried this but it didn't work // user: { // additionalFields: { // lastLoginMethod: { // type: "string", // required: false, // }, // }, // }, plugins: [ admin({ defaultRole: userRoleEnum.USER, adminRoles: [userRoleEnum.ADMIN], }), lastLoginMethod({ storeInDatabase: true, }), ], emailAndPassword: { enabled: true, requireEmailVerification: REQUIRE_EMAIL_VERIFICATION, sendResetPassword: async ({ user, url, token }, request) => { await sendResetPasswordEmail(user, url); }, }, emailVerification: { sendOnSignUp: true, autoSignInAfterVerification: true, sendVerificationEmail: async ({ user, url, token }, request) => { await sendVerificationEmail(user, url); }, }, session: { expiresIn: 60 * 60 * 24 * 7, // 7 days updateAge: 60 * 60 * 24, // 1 day }, advanced: { defaultCookieAttributes: { sameSite: "lax", secure: IS_PROD, httpOnly: true, }, }, socialProviders: { google: { prompt: "select_account", clientId: GOOGLE_CLIENT_ID, clientSecret: GOOGLE_CLIENT_SECRET, // optional: scope, accessType, etc. }, microsoft: { clientId: MICROSOFT_CLIENT_ID, clientSecret: MICROSOFT_CLIENT_SECRET, tenantId: "common", prompt: "select_account", }, }, databaseHooks: { account: { create: { after: async (account) => { // skip if providerId is credentials if (account.providerId === "credentials") { return; } // Use service to add provider email await addProviderEmail( account.id, account.providerId as ProviderId, account.accessToken || undefined ); }, }, }, }, }); // Use the shared types for consistency export type Session = typeof auth.$Infer.Session.session; export type User = typeof auth.$Infer.Session.user; ``` ### Additional context _No response_
GiteaMirror added the plugin label 2026-03-13 09:13:37 -05:00
Author
Owner

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

The lastLoginMethod plugin with storeInDatabase: true is designed to automatically add lastLoginMethod to your user schema and persist it in the database—no need to add it manually to additionalFields. However, even if the field is present in the DB, the admin.listUsers and admin.getUser endpoints may not include it in their API responses unless the endpoint's serialization logic is updated to expose it. This means the field can exist in the DB but still not show up in your API responses if the admin endpoint is only returning a default set of fields (source).

If you need lastLoginMethod (or other custom/plugin fields) in your API responses, you may need to extend the user type on the client side and use type assertion to access the field, or customize the admin endpoint's serialization logic to include it (source). This is a known pattern with Better Auth: runtime fields can exist on user objects but may not be reflected in TypeScript types or API responses by default.

If you want all user fields from the DB to be returned, check if the admin endpoint can be configured or extended to include all fields, or consider opening a feature request if this flexibility is needed.

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 16, 2025): <!-- Answer --> The lastLoginMethod plugin with storeInDatabase: true is designed to automatically add lastLoginMethod to your user schema and persist it in the database—no need to add it manually to additionalFields. However, even if the field is present in the DB, the admin.listUsers and admin.getUser endpoints may not include it in their API responses unless the endpoint's serialization logic is updated to expose it. This means the field can exist in the DB but still not show up in your API responses if the admin endpoint is only returning a default set of fields ([source](https://github.com/better-auth/better-auth/blob/4df178463eeeced7972c9a59f902b4bbf55db30b/packages/better-auth/src/plugins/last-login-method/index.ts)). If you need lastLoginMethod (or other custom/plugin fields) in your API responses, you may need to extend the user type on the client side and use type assertion to access the field, or customize the admin endpoint's serialization logic to include it ([source](https://github.com/better-auth/better-auth/issues/3033)). This is a known pattern with Better Auth: runtime fields can exist on user objects but may not be reflected in TypeScript types or API responses by default. If you want all user fields from the DB to be returned, check if the admin endpoint can be configured or extended to include all fields, or consider opening a feature request if this flexibility is needed. <!-- 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/de94ad61-1a43-479f-997c-82ab862ad047?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/de94ad61-1a43-479f-997c-82ab862ad047?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/de94ad61-1a43-479f-997c-82ab862ad047?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/de94ad61-1a43-479f-997c-82ab862ad047?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/de94ad61-1a43-479f-997c-82ab862ad047?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/de94ad61-1a43-479f-997c-82ab862ad047?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/de94ad61-1a43-479f-997c-82ab862ad047?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/4696)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1937