feat: allow extending account table #1041

Open
opened 2026-03-13 08:20:03 -05:00 by GiteaMirror · 9 comments
Owner

Originally created by @armannaj on GitHub (Apr 14, 2025).

Is this suited for github?

  • Yes, this is suited for github

Currently in linked account database table, there is field for "AccountId" which contains that social account numeric unique id (twitter ID, github ID). However, in most cases we need to show our users what username they linked with.

Describe the solution you'd like

Therefore I would like Accounts database table to have a new column for "AccountName" and every social oauth implementation fills this column with AccountName (twitter handle, google email, github username, etc.)

Describe alternatives you've considered

The alternative could be to have "additionalField" for "Account" object in auth.ts config so that we can extend core schema for Account object as well. Currently it is only possible for "user" and "session" objects.

However, if this approach is chosen, there should be a mapProfileToAccount hook as well, so that we can fill the value of this additional new field we just added.

Additional context

No response

Originally created by @armannaj on GitHub (Apr 14, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### Is your feature request related to a problem? Please describe. Currently in linked account database table, there is field for "AccountId" which contains that social account numeric unique id (twitter ID, github ID). However, in most cases we need to show our users what username they linked with. ### Describe the solution you'd like Therefore I would like Accounts database table to have a new column for "AccountName" and every social oauth implementation fills this column with AccountName (twitter handle, google email, github username, etc.) ### Describe alternatives you've considered The alternative could be to have "_additionalField_" for "Account" object in auth.ts config so that we can extend core schema for Account object as well. Currently it is only possible for "user" and "session" objects. However, if this approach is chosen, there should be a _mapProfileToAccount_ hook as well, so that we can fill the value of this additional new field we just added. ### Additional context _No response_
GiteaMirror added the enhancement label 2026-03-13 08:20:03 -05:00
Author
Owner

@Kinfe123 commented on GitHub (Apr 14, 2025):

since they formed a link , username or handle is already there on the user side which you can still use additionalField feature on it ..so that you can rework on that - if you need additional pre and post processing regarding to that you can make sure to use hooks and get access to the account and user info with ctx.context.internalAdapter.*

@Kinfe123 commented on GitHub (Apr 14, 2025): since they formed a link , username or handle is already there on the user side which you can still use additionalField feature on it ..so that you can rework on that - if you need additional pre and post processing regarding to that you can make sure to use hooks and get access to the account and user info with `ctx.context.internalAdapter.*`
Author
Owner

@elwin212 commented on GitHub (Apr 18, 2025):

Did you manage to solve this? I have the same question regarding account linking.
When using authClient.listAccounts, I’d also like to display each account’s name and email.
Since I’ve set allowDifferentEmails: true, a user can log in with different Google accounts, and showing their associated info would be helpful.

@elwin212 commented on GitHub (Apr 18, 2025): Did you manage to solve this? I have the same question regarding account linking. When using `authClient.listAccounts`, I’d also like to display each account’s name and email. Since I’ve set `allowDifferentEmails: true`, a user can log in with different Google accounts, and showing their associated info would be helpful.
Author
Owner

@Bekacru commented on GitHub (May 6, 2025):

My recommendation is to store githubId and similar metadata in the user table in the mean time

@Bekacru commented on GitHub (May 6, 2025): My recommendation is to store `githubId` and similar metadata in the user table in the mean time
Author
Owner

@dtc20h4802010137 commented on GitHub (May 14, 2025):

Did you manage to solve this? I have the same question regarding account linking. When using authClient.listAccounts, I’d also like to display each account’s name and email. Since I’ve set allowDifferentEmails: true, a user can log in with different Google accounts, and showing their associated info would be helpful.

Yeah, i want to display username or email of account that linked to user too. But cant find the way to do that, im trying to use access_token to get linked account info through provider api

@dtc20h4802010137 commented on GitHub (May 14, 2025): > Did you manage to solve this? I have the same question regarding account linking. When using `authClient.listAccounts`, I’d also like to display each account’s name and email. Since I’ve set `allowDifferentEmails: true`, a user can log in with different Google accounts, and showing their associated info would be helpful. Yeah, i want to display username or email of account that linked to user too. But cant find the way to do that, im trying to use access_token to get linked account info through provider api
Author
Owner

@dosubot[bot] commented on GitHub (Aug 13, 2025):

Hi, @armannaj. I'm Dosu, and I'm helping the better-auth team manage their backlog and am marking this issue as stale.

Issue Summary:

  • You requested adding an "AccountName" column or schema extension to store usernames alongside AccountId in linked accounts.
  • I suggested using the existing additionalField feature and hooks to access and process account and user info for custom handling.
  • Other users expressed similar needs to display linked account names and emails, with some attempting to retrieve info via provider APIs.
  • A recommendation was made to temporarily store metadata like githubId in the user table.
  • The issue was resolved by encouraging custom solutions using existing features rather than adding a dedicated AccountName column.

Next Steps:

  • Please let me know if this issue is still relevant to the latest version of better-auth by commenting here.
  • Otherwise, this issue will be automatically closed in 7 days.

Thank you for your understanding and contribution!

@dosubot[bot] commented on GitHub (Aug 13, 2025): Hi, @armannaj. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog and am marking this issue as stale. **Issue Summary:** - You requested adding an "AccountName" column or schema extension to store usernames alongside AccountId in linked accounts. - I suggested using the existing additionalField feature and hooks to access and process account and user info for custom handling. - Other users expressed similar needs to display linked account names and emails, with some attempting to retrieve info via provider APIs. - A recommendation was made to temporarily store metadata like githubId in the user table. - The issue was resolved by encouraging custom solutions using existing features rather than adding a dedicated AccountName column. **Next Steps:** - Please let me know if this issue is still relevant to the latest version of better-auth by commenting here. - Otherwise, this issue will be automatically closed in 7 days. Thank you for your understanding and contribution!
Author
Owner

@armannaj commented on GitHub (Aug 18, 2025):

Hi, @armannaj. I'm Dosu, and I'm helping the better-auth team manage their backlog and am marking this issue as stale.

Issue Summary:

  • You requested adding an "AccountName" column or schema extension to store usernames alongside AccountId in linked accounts.
  • I suggested using the existing additionalField feature and hooks to access and process account and user info for custom handling.
  • Other users expressed similar needs to display linked account names and emails, with some attempting to retrieve info via provider APIs.
  • A recommendation was made to temporarily store metadata like githubId in the user table.
  • The issue was resolved by encouraging custom solutions using existing features rather than adding a dedicated AccountName column.

Next Steps:

  • Please let me know if this issue is still relevant to the latest version of better-auth by commenting here.
  • Otherwise, this issue will be automatically closed in 7 days.

Thank you for your understanding and contribution!

hi there,

Yes, this issue still exists and would be good to solve it.

@armannaj commented on GitHub (Aug 18, 2025): > Hi, [@armannaj](https://github.com/armannaj). I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog and am marking this issue as stale. > > **Issue Summary:** > > * You requested adding an "AccountName" column or schema extension to store usernames alongside AccountId in linked accounts. > * I suggested using the existing additionalField feature and hooks to access and process account and user info for custom handling. > * Other users expressed similar needs to display linked account names and emails, with some attempting to retrieve info via provider APIs. > * A recommendation was made to temporarily store metadata like githubId in the user table. > * The issue was resolved by encouraging custom solutions using existing features rather than adding a dedicated AccountName column. > > **Next Steps:** > > * Please let me know if this issue is still relevant to the latest version of better-auth by commenting here. > * Otherwise, this issue will be automatically closed in 7 days. > > Thank you for your understanding and contribution! hi there, Yes, this issue still exists and would be good to solve it.
Author
Owner

@ghyath5 commented on GitHub (Oct 25, 2025):

I'm facing what seems to be the same issue with additionalFields not being returned, even when marked as returned: true.

Context

I'm using Better Auth with the Drizzle adapter and account linking enabled for Instagram.
The Instagram account linking works perfectly — the username is fetched from the Graph API and saved correctly in the database.
However, the username field is not included in the response from:

const accounts = await authClient.listAccounts();

Relevant Configuration

account: {
  additionalFields: {
    username: {
      type: "string",
      fieldName: "username",
      input: true,
      returned: true, // ✅ explicitly marked as returned
    },
  },
  accountLinking: {
    enabled: true,
    trustedProviders: ["instagram"],
    allowDifferentEmails: true,
  },
},

Observed Behavior

authClient.listAccounts() only returns the default fields:

id, providerId, accountId, scopes, createdAt, updatedAt

but does not include username, even though it’s stored in the database and marked as returned: true.

Expected Behavior

Any field defined in additionalFields (and flagged with returned: true) should also be included in the serialized response of authClient.listAccounts().

Notes

  • I confirmed the column exists in the database and is being populated correctly.
  • This appears to match the previously reported behavior where custom additionalFields are saved but not returned in client responses.

Is this the intended behavior, or is there a current limitation preventing custom fields from being serialized in listAccounts() responses?

@ghyath5 commented on GitHub (Oct 25, 2025): I'm facing what seems to be the same issue with `additionalFields` not being returned, even when marked as `returned: true`. #### Context I'm using **Better Auth** with the **Drizzle adapter** and **account linking enabled** for Instagram. The Instagram account linking works perfectly — the `username` is fetched from the Graph API and saved correctly in the database. However, the `username` field is **not included** in the response from: ```ts const accounts = await authClient.listAccounts(); ``` #### Relevant Configuration ```ts account: { additionalFields: { username: { type: "string", fieldName: "username", input: true, returned: true, // ✅ explicitly marked as returned }, }, accountLinking: { enabled: true, trustedProviders: ["instagram"], allowDifferentEmails: true, }, }, ``` #### Observed Behavior `authClient.listAccounts()` only returns the default fields: ``` id, providerId, accountId, scopes, createdAt, updatedAt ``` but **does not include `username`**, even though it’s stored in the database and marked as `returned: true`. #### Expected Behavior Any field defined in `additionalFields` (and flagged with `returned: true`) should also be included in the serialized response of `authClient.listAccounts()`. #### Notes - I confirmed the column exists in the database and is being populated correctly. - This appears to match the previously reported behavior where custom `additionalFields` are saved but not returned in client responses. Is this the intended behavior, or is there a current limitation preventing custom fields from being serialized in `listAccounts()` responses?
Author
Owner

@cvienna commented on GitHub (Jan 1, 2026):

No Built-in Solution for Username/Email in Account Table

Better Auth doesn't natively support username or email fields in the Account table. Their source code literally says:

todo: we should use generics to extend this type with additional fields from plugins and options in the future.

Workaround

Create a temporary staging table:

export const pending_account = pgTable("pending_account", {
  id: text("id").primaryKey(),
  accountId: text("account_id").notNull(),
  username: text("username"),
});

Implementation

1. In getUserInfo (OAuth callback), stage the username:

getUserInfo: async (token) => {
  const response = await fetch(
    "https://www.googleapis.com/oauth2/v2/userinfo",
    { headers: { Authorization: `Bearer ${token.accessToken}` } }
  );
  const profile = await response.json();

  const result = await db
    .update(account)
    .set({ username: profile.username })
    .where(eq(account.accountId, profile.id));

  if (result.rowCount && result.rowCount > 0) {
    await db
      .delete(pending_account)
      .where(eq(pending_account.accountId, profile.id));
  } else {
    await db.insert(pending_account).values({
      id: generateId(32),
      accountId: profile.id,
      username: profile.username,
    });
  }

  return { user: { id: profile.id, name: profile.name, ... }, data: profile };
};

2. Use an after-hook to persist staged data:

hooks: {
  after: createAuthMiddleware(async (ctx) => {
    if (!ctx.path?.includes("/callback") || !ctx.headers) return;

    const userAccount = await auth.api.listUserAccounts({ headers: ctx.headers });
    const accountIds = userAccount.map((acc) => acc.accountId);

    const pendingAccounts = await db.query.pending_account.findMany({
      where: inArray(pending_account.accountId, accountIds),
    });

    if (pendingAccounts.length > 0) {
      await Promise.all(
        pendingAccounts.map((pending) =>
          db.update(account)
            .set({ username: pending.username })
            .where(eq(account.accountId, pending.accountId))
        )
      );

      await db.delete(pending_account)
        .where(inArray(pending_account.accountId, accountIds));
    }
  }),
}

Why This Works

OAuth callback triggers before account record exists. Staging table bridges the timing gap, then the hook syncs data once the account is created.

Simpler Alternative

Skip the after-hook and just put the update call directly in getUserInfo. Users need to re-authenticate once for the metadata to apply correctly, but it removes the staging complexity entirely.

@cvienna commented on GitHub (Jan 1, 2026): ## No Built-in Solution for Username/Email in Account Table Better Auth doesn't natively support username or email fields in the Account table. Their source code literally says: > todo: we should use generics to extend this type with additional fields from plugins and options in the future. ### Workaround **Create a temporary staging table:** ```typescript export const pending_account = pgTable("pending_account", { id: text("id").primaryKey(), accountId: text("account_id").notNull(), username: text("username"), }); ``` ### Implementation **1. In getUserInfo (OAuth callback), stage the username:** ```typescript getUserInfo: async (token) => { const response = await fetch( "https://www.googleapis.com/oauth2/v2/userinfo", { headers: { Authorization: `Bearer ${token.accessToken}` } } ); const profile = await response.json(); const result = await db .update(account) .set({ username: profile.username }) .where(eq(account.accountId, profile.id)); if (result.rowCount && result.rowCount > 0) { await db .delete(pending_account) .where(eq(pending_account.accountId, profile.id)); } else { await db.insert(pending_account).values({ id: generateId(32), accountId: profile.id, username: profile.username, }); } return { user: { id: profile.id, name: profile.name, ... }, data: profile }; }; ``` **2. Use an after-hook to persist staged data:** ```typescript hooks: { after: createAuthMiddleware(async (ctx) => { if (!ctx.path?.includes("/callback") || !ctx.headers) return; const userAccount = await auth.api.listUserAccounts({ headers: ctx.headers }); const accountIds = userAccount.map((acc) => acc.accountId); const pendingAccounts = await db.query.pending_account.findMany({ where: inArray(pending_account.accountId, accountIds), }); if (pendingAccounts.length > 0) { await Promise.all( pendingAccounts.map((pending) => db.update(account) .set({ username: pending.username }) .where(eq(account.accountId, pending.accountId)) ) ); await db.delete(pending_account) .where(inArray(pending_account.accountId, accountIds)); } }), } ``` ### Why This Works **OAuth callback triggers before account record exists. Staging table bridges the timing gap, then the hook syncs data once the account is created.** ### Simpler Alternative **Skip the after-hook and just put the update call directly in getUserInfo. Users need to re-authenticate once for the metadata to apply correctly, but it removes the staging complexity entirely.**
Author
Owner

@afn commented on GitHub (Mar 11, 2026):

Here's a PR that adds an optional getAccountFields to the provider config to allow adding providerEmail (and any other relevant attributes) when creating the account: https://github.com/better-auth/better-auth/pull/8552

@afn commented on GitHub (Mar 11, 2026): Here's a PR that adds an optional `getAccountFields` to the provider config to allow adding `providerEmail` (and any other relevant attributes) when creating the account: https://github.com/better-auth/better-auth/pull/8552
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1041