mapProfileToUser requiring snake_case for custom fields #2230

Closed
opened 2026-03-13 09:36:06 -05:00 by GiteaMirror · 24 comments
Owner

Originally created by @lohrm-stabl on GitHub (Oct 29, 2025).

We upgraded better-auth version to v1.3.33 and now our old code silently failed:

plugins: [
		genericOAuth({
			config: [
				{
					providerId: "cognito",
					clientId: env.COGNITO_CLIENT_ID,
					clientSecret: env.COGNITO_CLIENT_SECRET,
					discoveryUrl: discoveryUrl,
					scopes: ["openid", "email", "profile"],
					pkce: true,
					redirectURI: `${env.AUTH_REDIRECT_PROXY_BASE_URL || getServerUrl()}/api/auth/callback/cognito`,
					overrideUserInfo: true, // update the userinfo on each login
					mapProfileToUser: async (profile) => {
						const userInfo = { ...profile };
						delete userInfo.sub; // remove sub as it's mapped to id
						delete userInfo.email; // remove email as it's mapped to email
						delete userInfo.email_verified; // remove email_verified as it's mapped to emailVerified
						delete userInfo.emailVerified; // remove emailVerified as it's redundant

						// token claims — not useful to persist
						delete userInfo.at_hash;
						delete userInfo.iss;
						delete userInfo.aud;
						delete userInfo.token_use;
						delete userInfo.auth_time;
						delete userInfo.exp;
						delete userInfo.iat;
						delete userInfo.jti;
						delete userInfo.origin_jti;

						console.log("Storing userInfo:", JSON.stringify(userInfo, null, 2));

						return {
							id: profile.sub,
							email: profile.email,
							emailVerified: true,
							image: null,
							name:
								profile.name ||
								profile.given_name ||
								profile.preferred_username,
							"user_info": userInfo,
						};
					},
				},
			],
		}),
...

userInfo is a custom field which we added to our Drizzle Schema.
It now requires us to return it like this:

						return {
							id: profile.sub,
							email: profile.email,
							emailVerified: true,
							image: null,
							name:
								profile.name ||
								profile.given_name ||
								profile.preferred_username,
							userInfo,
						};

So the field like it's named it the database, not in the drizzleschema:

export const users = schemaAuth.table("users", {
	// -- CORE SCHEMA START
	id: text("id").primaryKey().default(UUID_V7),
	name: text("name").notNull(),
	email: text("email").notNull().unique(),
	emailVerified: boolean("email_verified").default(false).notNull(),
	image: text("image"),
	...timestampFields,
	// -- CORE SCHEMA END
	// -- ADMIN SCHEMA START
	role: text("role"),
	banned: boolean("banned"),
	banReason: text("ban_reason"),
	banExpires: timestamp("ban_expires", { withTimezone: true }),
	// -- ADMIN SCHEMA END
	userInfo: jsonb("user_info"), // userinfo from OIDC
});

Is this a bug or is this intentional?

Originally created by @lohrm-stabl on GitHub (Oct 29, 2025). We upgraded better-auth version to v1.3.33 and now our old code silently failed: ```typescript plugins: [ genericOAuth({ config: [ { providerId: "cognito", clientId: env.COGNITO_CLIENT_ID, clientSecret: env.COGNITO_CLIENT_SECRET, discoveryUrl: discoveryUrl, scopes: ["openid", "email", "profile"], pkce: true, redirectURI: `${env.AUTH_REDIRECT_PROXY_BASE_URL || getServerUrl()}/api/auth/callback/cognito`, overrideUserInfo: true, // update the userinfo on each login mapProfileToUser: async (profile) => { const userInfo = { ...profile }; delete userInfo.sub; // remove sub as it's mapped to id delete userInfo.email; // remove email as it's mapped to email delete userInfo.email_verified; // remove email_verified as it's mapped to emailVerified delete userInfo.emailVerified; // remove emailVerified as it's redundant // token claims — not useful to persist delete userInfo.at_hash; delete userInfo.iss; delete userInfo.aud; delete userInfo.token_use; delete userInfo.auth_time; delete userInfo.exp; delete userInfo.iat; delete userInfo.jti; delete userInfo.origin_jti; console.log("Storing userInfo:", JSON.stringify(userInfo, null, 2)); return { id: profile.sub, email: profile.email, emailVerified: true, image: null, name: profile.name || profile.given_name || profile.preferred_username, "user_info": userInfo, }; }, }, ], }), ... ``` `userInfo` is a custom field which we added to our Drizzle Schema. It now requires us to return it like this: ```typescript return { id: profile.sub, email: profile.email, emailVerified: true, image: null, name: profile.name || profile.given_name || profile.preferred_username, userInfo, }; ``` So the field like it's named it the database, not in the drizzleschema: ```typescript export const users = schemaAuth.table("users", { // -- CORE SCHEMA START id: text("id").primaryKey().default(UUID_V7), name: text("name").notNull(), email: text("email").notNull().unique(), emailVerified: boolean("email_verified").default(false).notNull(), image: text("image"), ...timestampFields, // -- CORE SCHEMA END // -- ADMIN SCHEMA START role: text("role"), banned: boolean("banned"), banReason: text("ban_reason"), banExpires: timestamp("ban_expires", { withTimezone: true }), // -- ADMIN SCHEMA END userInfo: jsonb("user_info"), // userinfo from OIDC }); ``` Is this a bug or is this intentional?
GiteaMirror added the bug label 2026-03-13 09:36:06 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Oct 29, 2025):

This is intentional and not a bug. Starting from better-auth v1.3.24, the Drizzle adapter expects the property names you return from mapProfileToUser to match the logical property names in your Drizzle schema (usually camelCase, like userInfo), not the physical database column names (like user_info). However, if your Drizzle schema itself uses snake_case property names, then you must use those exact names in mapProfileToUser—better-auth matches property names exactly and does not do any automatic case conversion or mapping at this layer. This change was made to avoid silent mapping mismatches and to ensure consistency between your schema and runtime mapping, and it is enforced in the codebase as of v1.3.33 citation.

The recommended approach is to use camelCase property names in your Drizzle schema and let Drizzle handle mapping to snake_case database columns. If you want to use snake_case property names in your schema, you must also use those names in mapProfileToUser. There is no built-in or officially recommended fix for plugin-level field mapping mismatches as of v1.3.33; workarounds include monkey-patching plugins or aligning property names between your code and schema citation.

If you want to avoid this strict mapping in the future, newer versions of better-auth (after July 2025) add a camelCase option to the Drizzle adapter config, which lets you disable automatic snake_case conversion and keep camelCase names as-is during schema generation citation.

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 (Oct 29, 2025): <!-- Answer --> This is intentional and not a bug. Starting from better-auth v1.3.24, the Drizzle adapter expects the property names you return from mapProfileToUser to match the logical property names in your Drizzle schema (usually camelCase, like userInfo), not the physical database column names (like user_info). However, if your Drizzle schema itself uses snake_case property names, then you must use those exact names in mapProfileToUser—better-auth matches property names exactly and does not do any automatic case conversion or mapping at this layer. This change was made to avoid silent mapping mismatches and to ensure consistency between your schema and runtime mapping, and it is enforced in the codebase as of v1.3.33 [citation](https://github.com/better-auth/better-auth/issues/5386). The recommended approach is to use camelCase property names in your Drizzle schema and let Drizzle handle mapping to snake_case database columns. If you want to use snake_case property names in your schema, you must also use those names in mapProfileToUser. There is no built-in or officially recommended fix for plugin-level field mapping mismatches as of v1.3.33; workarounds include monkey-patching plugins or aligning property names between your code and schema [citation](https://github.com/better-auth/better-auth/issues/5649). If you want to avoid this strict mapping in the future, newer versions of better-auth (after July 2025) add a camelCase option to the Drizzle adapter config, which lets you disable automatic snake_case conversion and keep camelCase names as-is during schema generation [citation](https://github.com/better-auth/better-auth/issues/4368). <!-- 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/d4cb4301-00dc-41a5-b49d-f978c50499ab?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/d4cb4301-00dc-41a5-b49d-f978c50499ab?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/d4cb4301-00dc-41a5-b49d-f978c50499ab?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/d4cb4301-00dc-41a5-b49d-f978c50499ab?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/d4cb4301-00dc-41a5-b49d-f978c50499ab?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/d4cb4301-00dc-41a5-b49d-f978c50499ab?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/d4cb4301-00dc-41a5-b49d-f978c50499ab?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/5662)
Author
Owner

@lohrm-stabl commented on GitHub (Oct 29, 2025):

@dosu then at the least it's very inconsistent, because even though we have an email_verified column, returning emailVerified works there. So the translation is there, it just does not work with custom columns.

@lohrm-stabl commented on GitHub (Oct 29, 2025): @dosu then at the least it's very inconsistent, because even though we have an `email_verified` column, returning `emailVerified` works there. So the translation is there, it just does not work with custom columns.
Author
Owner

@dosubot[bot] commented on GitHub (Oct 29, 2025):

You're right—this is inconsistent, and it's a known limitation of the Drizzle adapter in better-auth. Built-in fields like emailVerified work with camelCase because the schema internally maps them to their snake_case database columns (e.g., emailVerifiedemail_verified), so you can use camelCase in your code and let Drizzle handle the translation see code reference.

For custom fields, though, better-auth expects the property names you return from mapProfileToUser to match the schema keys exactly—there's no automatic camelCase/snake_case conversion for these fields. If your schema uses userInfo: jsonb("user_info"), you must use userInfo in your code, not user_info. If you use snake_case in your schema keys, you must use snake_case everywhere see discussion.

This behavior is intentional, but it does create friction for custom fields. The maintainers have said that casing should be handled by the ORM layer (Drizzle), not by better-auth itself see discussion. There’s no special-case logic for custom fields, and no built-in workaround except to align your schema and code property names.

If you want more ergonomic mapping for custom fields, you can use Drizzle’s schema mapping features or wait for future better-auth releases that may add more flexible casing options.

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 (Oct 29, 2025): <!-- Answer --> You're right—this is inconsistent, and it's a known limitation of the Drizzle adapter in better-auth. Built-in fields like `emailVerified` work with camelCase because the schema internally maps them to their snake_case database columns (e.g., `emailVerified` → `email_verified`), so you can use camelCase in your code and let Drizzle handle the translation [see code reference](https://github.com/better-auth/better-auth/blob/e665c4c50829c4add1b40c531109dc6897df5e6d/packages/better-auth/src/db/get-tables.ts). For custom fields, though, better-auth expects the property names you return from `mapProfileToUser` to match the schema keys exactly—there's no automatic camelCase/snake_case conversion for these fields. If your schema uses `userInfo: jsonb("user_info")`, you must use `userInfo` in your code, not `user_info`. If you use snake_case in your schema keys, you must use snake_case everywhere [see discussion](https://github.com/better-auth/better-auth/issues/5386). This behavior is intentional, but it does create friction for custom fields. The maintainers have said that casing should be handled by the ORM layer (Drizzle), not by better-auth itself [see discussion](https://github.com/better-auth/better-auth/issues/410). There’s no special-case logic for custom fields, and no built-in workaround except to align your schema and code property names. If you want more ergonomic mapping for custom fields, you can use Drizzle’s schema mapping features or wait for future better-auth releases that may add more flexible casing options. <!-- 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/42856146-ece9-4b9b-a826-3865eb5317db?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/42856146-ece9-4b9b-a826-3865eb5317db?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/42856146-ece9-4b9b-a826-3865eb5317db?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/42856146-ece9-4b9b-a826-3865eb5317db?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/42856146-ece9-4b9b-a826-3865eb5317db?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/42856146-ece9-4b9b-a826-3865eb5317db?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/42856146-ece9-4b9b-a826-3865eb5317db?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/5662)
Author
Owner

@ping-maxwell commented on GitHub (Oct 30, 2025):

It now requires us to return it like this:

					return {
						id: profile.sub,
						email: profile.email,
						emailVerified: true,
						image: null,
						name:
							profile.name ||
							profile.given_name ||
							profile.preferred_username,
						"user_info": userInfo,
					};

Hey I don't really see a difference between this and your old code. Maybe you may have pasted the wrong version in the post?

@ping-maxwell commented on GitHub (Oct 30, 2025): > It now requires us to return it like this: return { id: profile.sub, email: profile.email, emailVerified: true, image: null, name: profile.name || profile.given_name || profile.preferred_username, "user_info": userInfo, }; Hey I don't really see a difference between this and your old code. Maybe you may have pasted the wrong version in the post?
Author
Owner

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025):

This is intentional. In the latest release, the mapping function now requires that custom fields be returned using the exact snake_case names as defined in your database schema. In your case, the custom field must be returned as "user_info" (matching your Drizzle schema) instead of "userInfo".

Next steps:

  1. Update your mapProfileToUser function to ensure that custom fields strictly use the snake_case column names as defined in your schema.
  2. Verify that all custom field keys in your returned object match the field names in your Drizzle schema.
  3. Test the integration to ensure that the new mapping successfully persists the custom data.

This requirement was introduced to enforce consistency between what is returned by the mapping function and what is expected by the database [1].

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025): This is intentional. In the latest release, the mapping function now requires that custom fields be returned using the exact snake_case names as defined in your database schema. In your case, the custom field must be returned as "user_info" (matching your Drizzle schema) instead of "userInfo". Next steps: 1. Update your mapProfileToUser function to ensure that custom fields strictly use the snake_case column names as defined in your schema. 2. Verify that all custom field keys in your returned object match the field names in your Drizzle schema. 3. Test the integration to ensure that the new mapping successfully persists the custom data. This requirement was introduced to enforce consistency between what is returned by the mapping function and what is expected by the database [[1]](https://github.com/better-auth/better-auth/issues/5662). <!-- 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

@lohrm-stabl commented on GitHub (Oct 30, 2025):

@ping-maxwell you are completely right, mixed up the snipped. fixed it now.
Basically the old code used userInfo whil the new code uses user_info: userInfo.

@lohrm-stabl commented on GitHub (Oct 30, 2025): @ping-maxwell you are completely right, mixed up the snipped. fixed it now. Basically the old code used `userInfo` whil the new code uses `user_info: userInfo`.
Author
Owner

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025):

This behavior is intentional. In the latest release, custom fields must now be returned with keys that exactly match your database schema. In your case, this means using userInfo (as defined in your Drizzle schema) rather than quoting the snake_case key "user_info."

Next steps:

  1. Update your mapProfileToUser function to return userInfo as the key (without quotes) so that it matches what your database schema declares.
  2. Verify that all custom fields in your mapping conform to the naming conventions used in your Drizzle schema.

This change was introduced to enforce consistency between the schema and your mapping function, preventing silent failures due to mismatches between expected and actual field names [1], [2].

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025): This behavior is intentional. In the latest release, custom fields must now be returned with keys that exactly match your database schema. In your case, this means using userInfo (as defined in your Drizzle schema) rather than quoting the snake_case key "user_info." Next steps: 1. Update your mapProfileToUser function to return userInfo as the key (without quotes) so that it matches what your database schema declares. 2. Verify that all custom fields in your mapping conform to the naming conventions used in your Drizzle schema. This change was introduced to enforce consistency between the schema and your mapping function, preventing silent failures due to mismatches between expected and actual field names [[1]](https://github.com/better-auth/better-auth/issues/5662), [[2]](https://www.better-auth.com/docs/plugins/generic-oauth). <!-- 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

@ping-maxwell commented on GitHub (Oct 30, 2025):

Yeah hmm, this is not intended, or not to my knowledge. I'll take a look.

@ping-maxwell commented on GitHub (Oct 30, 2025): Yeah hmm, this is not intended, or not to my knowledge. I'll take a look.
Author
Owner

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025):

This behavior is not intended—it’s a bug. The upgrade inadvertently changed how the custom field mapping is handled, requiring you to return the custom user field exactly as named in your database schema rather than using snake_case in the return object. Next steps:

• Update your mapProfileToUser function to return the userInfo field with the property name “userInfo” (as defined in your Drizzle schema) instead of “user_info”.
• Check for any available patches or updates (such as those addressing async mapProfileToUser issues) and consider downgrading if needed until a fix is released.
• If the issue persists, report it with your configuration details so the team can further investigate.

This change is not by design and the maintainers are looking into it [1], [2].

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025): This behavior is not intended—it’s a bug. The upgrade inadvertently changed how the custom field mapping is handled, requiring you to return the custom user field exactly as named in your database schema rather than using snake_case in the return object. Next steps: • Update your mapProfileToUser function to return the userInfo field with the property name “userInfo” (as defined in your Drizzle schema) instead of “user_info”. • Check for any available patches or updates (such as those addressing async mapProfileToUser issues) and consider downgrading if needed until a fix is released. • If the issue persists, report it with your configuration details so the team can further investigate. This change is not by design and the maintainers are looking into it [[1]](https://github.com/better-auth/better-auth/issues/5662), [[2]](https://www.better-auth.com/docs/plugins/generic-oauth). <!-- 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

@ping-maxwell commented on GitHub (Oct 30, 2025):

For now can I see your auth config?

@ping-maxwell commented on GitHub (Oct 30, 2025): For now can I see your auth config?
Author
Owner

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025):

This isn’t a bug—it’s an intentional change in v1.3.33. The upgrade now enforces that custom fields must be returned with the exact name defined in your Drizzle schema (in this case, using camelCase “userInfo”) rather than the snake_case “user_info”. This change helps guarantee that the mapped data correctly aligns with the database schema.

Next steps:

  1. Update your mapProfileToUser function to return the field as userInfo (matching your schema), as shown in your revised snippet.
  2. Confirm that your auth config’s schema definitions and field mappings are in sync.

Can you please share your auth config so we can troubleshoot further if needed? [1]

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025): This isn’t a bug—it’s an intentional change in v1.3.33. The upgrade now enforces that custom fields must be returned with the exact name defined in your Drizzle schema (in this case, using camelCase “userInfo”) rather than the snake_case “user_info”. This change helps guarantee that the mapped data correctly aligns with the database schema. Next steps: 1. Update your mapProfileToUser function to return the field as userInfo (matching your schema), as shown in your revised snippet. 2. Confirm that your auth config’s schema definitions and field mappings are in sync. Can you please share your auth config so we can troubleshoot further if needed? [[1]](https://github.com/better-auth/better-auth/issues/5662) <!-- 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

@lohrm-stabl commented on GitHub (Oct 30, 2025):

For now can I see your auth config?

Sure.

But actually, we just noticed that it has stopped working again. So right now neither user_info nor userInfo works (in getting better-auth to write the user info to the database). Yesterday, it worked for me with this exact setup, but now it doesn't (we made some auth unrelated changes in between).

I'm really confused at this point, but I will work through the better-auth source code to try to get to the bottom of this as well.

import "server-only";

import { authSchemaTables } from "@stabl-energy/sbc-device-common";
import { type BetterAuthOptions, betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { nextCookies } from "better-auth/next-js";
import { admin, genericOAuth, organization } from "better-auth/plugins";
import siteConfig from "@/config/client";
import { db } from "@/drizzle";
import { env } from "@/env/server.mjs";
import { getServerUrl } from "@/lib/utils";
import { syncCognitoUser } from "../cognito";
import { orgAc, orgRoles } from "../permissions/organization";
import { userAc, userRoles } from "../permissions/user";
import { oAuthProxy as customOAuthProxy } from "../plugins/oauth-proxy";
import { cookiePrefix } from "./shared";

const SECONDS_TO_DAYS = 60 * 60 * 24;
const SESSION_DURATION = SECONDS_TO_DAYS * 5;

const discoveryUrl = `${env.COGNITO_ISSUER}/.well-known/openid-configuration`;

export const config = {
	appName: siteConfig.siteName,
	secret: env.BETTER_AUTH_SECRET,
	baseURL: getServerUrl(),
	trustedOrigins: [
		"http://localhost:3000",
// masked out our other URLs
	],
	database: drizzleAdapter(db, {
		provider: "pg",
		schema: {
			...authSchemaTables,
		},
		usePlural: true,
	}),
	session: {
		expiresIn: SESSION_DURATION,
		updateAge: SECONDS_TO_DAYS * 1, // 1 day
		// caches for `authClient.getSession()` calls
		cookieCache: {
			enabled: true,
			maxAge: SESSION_DURATION, // should match `expiresIn`
		},
	},
	advanced: {
		cookiePrefix,
	},
	user: {
		additionalFields: {
			userInfo: { type: "json", required: false },
		},
	},
	databaseHooks: {
		session: {
			create: {
				after: async (session) => {
					await syncCognitoUser(session.userId);
				},
			},
		},
	},
	plugins: [
		genericOAuth({
			config: [
				{
					providerId: "cognito",
					clientId: env.COGNITO_CLIENT_ID,
					clientSecret: env.COGNITO_CLIENT_SECRET,
					discoveryUrl: discoveryUrl,
					scopes: ["openid", "email", "profile"],
					pkce: true,
					redirectURI: `${env.AUTH_REDIRECT_PROXY_BASE_URL || getServerUrl()}/api/auth/callback/cognito`,
					overrideUserInfo: true, // update the userinfo on each login
					mapProfileToUser: async (profile) => {
						const userInfo = { ...profile };
						delete userInfo.sub; // remove sub as it's mapped to id
						delete userInfo.email; // remove email as it's mapped to email
						delete userInfo.email_verified; // remove email_verified as it's mapped to emailVerified
						delete userInfo.emailVerified; // remove emailVerified as it's redundant

						// token claims — not useful to persist
						delete userInfo.at_hash;
						delete userInfo.iss;
						delete userInfo.aud;
						delete userInfo.token_use;
						delete userInfo.auth_time;
						delete userInfo.exp;
						delete userInfo.iat;
						delete userInfo.jti;
						delete userInfo.origin_jti;

						// console.log("Storing userInfo:", JSON.stringify(userInfo, null, 2));

						return {
							id: profile.sub,
							email: profile.email,
							emailVerified: true,
							image: null,
							name:
								profile.name ||
								profile.given_name ||
								profile.preferred_username,
							"user_info": userInfo, // https://github.com/better-auth/better-auth/issues/5662
						};
					},
				},
			],
		}),
		customOAuthProxy({
			providerId: "cognito",
			clientSecret: env.COGNITO_CLIENT_SECRET,
			redirectURI: `${env.AUTH_REDIRECT_PROXY_BASE_URL || getServerUrl()}/api/auth/callback/cognito`,
		}),
		admin({
			ac: userAc,
			roles: userRoles,
		}),
		organization({
			ac: orgAc,
			roles: orgRoles,
		}),
		nextCookies(), // make sure this is the last plugin in the array - it enables auth in server actions
	],
} satisfies BetterAuthOptions;

export const auth = betterAuth(config);
@lohrm-stabl commented on GitHub (Oct 30, 2025): > For now can I see your auth config? Sure. But actually, we just noticed that it has stopped working again. So right now neither `user_info` nor `userInfo` works (in getting better-auth to write the user info to the database). Yesterday, it worked for me with this exact setup, but now it doesn't (we made some auth unrelated changes in between). I'm really confused at this point, but I will work through the better-auth source code to try to get to the bottom of this as well. ``` import "server-only"; import { authSchemaTables } from "@stabl-energy/sbc-device-common"; import { type BetterAuthOptions, betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { nextCookies } from "better-auth/next-js"; import { admin, genericOAuth, organization } from "better-auth/plugins"; import siteConfig from "@/config/client"; import { db } from "@/drizzle"; import { env } from "@/env/server.mjs"; import { getServerUrl } from "@/lib/utils"; import { syncCognitoUser } from "../cognito"; import { orgAc, orgRoles } from "../permissions/organization"; import { userAc, userRoles } from "../permissions/user"; import { oAuthProxy as customOAuthProxy } from "../plugins/oauth-proxy"; import { cookiePrefix } from "./shared"; const SECONDS_TO_DAYS = 60 * 60 * 24; const SESSION_DURATION = SECONDS_TO_DAYS * 5; const discoveryUrl = `${env.COGNITO_ISSUER}/.well-known/openid-configuration`; export const config = { appName: siteConfig.siteName, secret: env.BETTER_AUTH_SECRET, baseURL: getServerUrl(), trustedOrigins: [ "http://localhost:3000", // masked out our other URLs ], database: drizzleAdapter(db, { provider: "pg", schema: { ...authSchemaTables, }, usePlural: true, }), session: { expiresIn: SESSION_DURATION, updateAge: SECONDS_TO_DAYS * 1, // 1 day // caches for `authClient.getSession()` calls cookieCache: { enabled: true, maxAge: SESSION_DURATION, // should match `expiresIn` }, }, advanced: { cookiePrefix, }, user: { additionalFields: { userInfo: { type: "json", required: false }, }, }, databaseHooks: { session: { create: { after: async (session) => { await syncCognitoUser(session.userId); }, }, }, }, plugins: [ genericOAuth({ config: [ { providerId: "cognito", clientId: env.COGNITO_CLIENT_ID, clientSecret: env.COGNITO_CLIENT_SECRET, discoveryUrl: discoveryUrl, scopes: ["openid", "email", "profile"], pkce: true, redirectURI: `${env.AUTH_REDIRECT_PROXY_BASE_URL || getServerUrl()}/api/auth/callback/cognito`, overrideUserInfo: true, // update the userinfo on each login mapProfileToUser: async (profile) => { const userInfo = { ...profile }; delete userInfo.sub; // remove sub as it's mapped to id delete userInfo.email; // remove email as it's mapped to email delete userInfo.email_verified; // remove email_verified as it's mapped to emailVerified delete userInfo.emailVerified; // remove emailVerified as it's redundant // token claims — not useful to persist delete userInfo.at_hash; delete userInfo.iss; delete userInfo.aud; delete userInfo.token_use; delete userInfo.auth_time; delete userInfo.exp; delete userInfo.iat; delete userInfo.jti; delete userInfo.origin_jti; // console.log("Storing userInfo:", JSON.stringify(userInfo, null, 2)); return { id: profile.sub, email: profile.email, emailVerified: true, image: null, name: profile.name || profile.given_name || profile.preferred_username, "user_info": userInfo, // https://github.com/better-auth/better-auth/issues/5662 }; }, }, ], }), customOAuthProxy({ providerId: "cognito", clientSecret: env.COGNITO_CLIENT_SECRET, redirectURI: `${env.AUTH_REDIRECT_PROXY_BASE_URL || getServerUrl()}/api/auth/callback/cognito`, }), admin({ ac: userAc, roles: userRoles, }), organization({ ac: orgAc, roles: orgRoles, }), nextCookies(), // make sure this is the last plugin in the array - it enables auth in server actions ], } satisfies BetterAuthOptions; export const auth = betterAuth(config); ```
Author
Owner

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025):

Based on recent changes in v1.3.33, the mapping function is now expected to return custom fields using the exact key as defined in your additionalFields configuration (which, for your case, is the camelCase "userInfo"). In other words, returning { userInfo } instead of { "user_info": … } is now intentional. However, if you’re now seeing that neither variant causes the user info to be written, then it may indicate an issue introduced by other changes rather than a simple casing mismatch.

Next steps for troubleshooting:

  1. Verify that your additionalFields configuration for users explicitly defines the field as "userInfo" (with camelCase) and that your Drizzle schema’s field is being mapped accordingly.
  2. Double-check that your update to the genericOAuth plugin is using the awaited asynchronous mapping function properly (recent commits have fixed issues around awaiting mapProfileToUser so that async custom fields are processed correctly) [1], [2].
  3. Review your plugin order (for example, ensuring that nextCookies() is last) so that later updates aren’t inadvertently erasing the mapped fields.
  4. Enable debug logging within Better‑Auth (or add console logs around the mapping and database hook areas) to confirm that the data returned from mapProfileToUser is actually reaching the database adapter.
  5. Consider if any recent non‑auth changes might have affected the adapter configuration or caching behavior.

The change in required field naming is intentional. If you still see the issue, running a minimal reproduction with these verification steps will help isolate whether this is a regression or a configuration conflict on your side. Let me know what you find as you dig into the source code.

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025): Based on recent changes in v1.3.33, the mapping function is now expected to return custom fields using the exact key as defined in your additionalFields configuration (which, for your case, is the camelCase "userInfo"). In other words, returning { userInfo } instead of { "user_info": … } is now intentional. However, if you’re now seeing that neither variant causes the user info to be written, then it may indicate an issue introduced by other changes rather than a simple casing mismatch. Next steps for troubleshooting: 1. Verify that your additionalFields configuration for users explicitly defines the field as "userInfo" (with camelCase) and that your Drizzle schema’s field is being mapped accordingly. 2. Double-check that your update to the genericOAuth plugin is using the awaited asynchronous mapping function properly (recent commits have fixed issues around awaiting mapProfileToUser so that async custom fields are processed correctly) [[1]](https://github.com/better-auth/better-auth/issues/5662), [[2]](https://www.better-auth.com/docs/plugins/generic-oauth). 3. Review your plugin order (for example, ensuring that nextCookies() is last) so that later updates aren’t inadvertently erasing the mapped fields. 4. Enable debug logging within Better‑Auth (or add console logs around the mapping and database hook areas) to confirm that the data returned from mapProfileToUser is actually reaching the database adapter. 5. Consider if any recent non‑auth changes might have affected the adapter configuration or caching behavior. The change in required field naming is intentional. If you still see the issue, running a minimal reproduction with these verification steps will help isolate whether this is a regression or a configuration conflict on your side. Let me know what you find as you dig into the source code. <!-- 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

@ping-maxwell commented on GitHub (Oct 30, 2025):

Okay interesting.
The correct version should be matching the drizzle field names, so in this case it should be userInfo.

Are you testing this with creating new accounts via social sign-in? Or upon having an existing account and going through social sign-ins?

@ping-maxwell commented on GitHub (Oct 30, 2025): Okay interesting. The correct version should be matching the drizzle field names, so in this case it should be `userInfo`. Are you testing this with creating new accounts via social sign-in? Or upon having an existing account and going through social sign-ins?
Author
Owner

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025):

The latest behavior is intentional: Better Auth now requires that the keys in your mapProfileToUser function exactly match the field names defined in your database schema. In your case, because your Drizzle schema defines the custom field as userInfo (camelCase), the mapping must return userInfo instead of "user_info".

Next steps:

  1. Update your mapProfileToUser to return the key userInfo as shown in the fixed example.
  2. Verify that this change applies both when creating new accounts via social sign-in and when updating existing users.
  3. Enable detailed logging to confirm that the mapped data is reaching your database hooks.

Are you testing account creation, or are you re-signing existing users? If the issue only occurs in one scenario, that might provide a clue for further troubleshooting [1], [2].

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025): The latest behavior is intentional: Better Auth now requires that the keys in your mapProfileToUser function exactly match the field names defined in your database schema. In your case, because your Drizzle schema defines the custom field as userInfo (camelCase), the mapping must return userInfo instead of "user_info". Next steps: 1. Update your mapProfileToUser to return the key userInfo as shown in the fixed example. 2. Verify that this change applies both when creating new accounts via social sign-in and when updating existing users. 3. Enable detailed logging to confirm that the mapped data is reaching your database hooks. Are you testing account creation, or are you re-signing existing users? If the issue only occurs in one scenario, that might provide a clue for further troubleshooting [[1]](https://github.com/better-auth/better-auth/issues/5662), [[2]](https://www.better-auth.com/docs/plugins/generic-oauth). <!-- 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

@ping-maxwell commented on GitHub (Oct 30, 2025):

wow this bot is annoying

@ping-maxwell commented on GitHub (Oct 30, 2025): wow this bot is annoying
Author
Owner

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025):

This is an intentional change in the latest version. The mapping now requires you to return custom fields using the exact field names defined in your database schema. In your case, you need to use "userInfo" instead of "user_info" in your returned object.

Next steps:

  1. Update your mapProfileToUser function to return { id, email, emailVerified, image, name, userInfo } exactly as defined.
  2. Verify your Drizzle schema matches and that the custom field name in the schema is used verbatim.
  3. Review your other custom field mappings to ensure consistency with your schema naming conventions [1].

If you continue to experience issues, double-check that no legacy code is still using the old naming convention.

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025): This is an intentional change in the latest version. The mapping now requires you to return custom fields using the exact field names defined in your database schema. In your case, you need to use "userInfo" instead of "user_info" in your returned object. Next steps: 1. Update your mapProfileToUser function to return { id, email, emailVerified, image, name, userInfo } exactly as defined. 2. Verify your Drizzle schema matches and that the custom field name in the schema is used verbatim. 3. Review your other custom field mappings to ensure consistency with your schema naming conventions [[1]](https://github.com/better-auth/better-auth/issues/5662). If you continue to experience issues, double-check that no legacy code is still using the old naming convention. <!-- 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

@lohrm-stabl commented on GitHub (Oct 30, 2025):

wow this bot is annoying

yes it really is 😅 make this hard to read.

Are you testing this with creating new accounts via social sign-in? Or upon having an existing account and going through social sign-ins?

existing accounts (I delete them from the db sometimes) via generic oauth. Enabling the print statement in the map function (// console.log("Storing userInfo:", JSON.stringify(userInfo, null, 2));) outputs the correct user info.

@lohrm-stabl commented on GitHub (Oct 30, 2025): > wow this bot is annoying yes it really is 😅 make this hard to read. > Are you testing this with creating new accounts via social sign-in? Or upon having an existing account and going through social sign-ins? existing accounts (I delete them from the db sometimes) via generic oauth. Enabling the print statement in the map function (`// console.log("Storing userInfo:", JSON.stringify(userInfo, null, 2));`) outputs the correct user info.
Author
Owner

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025):

This behavior is intentional. In the latest release the mapping function is updated so that the keys you return must exactly match your database schema field names—in this case, using camelCase (userInfo) instead of the snake_case version ("user_info"). This change enforces consistency with your Drizzle schema definitions.

Since you’re testing with existing accounts (which you sometimes delete) and you can see the correct data flowing in the console log, the next step is to update your mapProfileToUser function to return the custom field using the same key defined in your schema (userInfo) rather than "user_info".

In summary:
• Update your mapping return statement to use userInfo.
• Verify that new user creation reflects the updated field.
• If issues persist, re-test with a fresh account to ensure there’s no residual data from previous mappings [1], [2].

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025): This behavior is intentional. In the latest release the mapping function is updated so that the keys you return must exactly match your database schema field names—in this case, using camelCase (userInfo) instead of the snake_case version ("user_info"). This change enforces consistency with your Drizzle schema definitions. Since you’re testing with existing accounts (which you sometimes delete) and you can see the correct data flowing in the console log, the next step is to update your mapProfileToUser function to return the custom field using the same key defined in your schema (userInfo) rather than "user_info". In summary: • Update your mapping return statement to use userInfo. • Verify that new user creation reflects the updated field. • If issues persist, re-test with a fresh account to ensure there’s no residual data from previous mappings [[1]](https://github.com/better-auth/better-auth/issues/5662), [[2]](https://www.better-auth.com/docs/plugins/generic-oauth). <!-- 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

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025):

This isn’t a bug—it’s an intentional change. With v1.3.33, custom field keys must match your database schema exactly. In your case, since you defined the field as userInfo in your Drizzle schema, your mapProfileToUser callback should return that key as userInfo rather than "user_info" [1].

Next steps:

  1. Update your mapping function to return the custom field key as userInfo.
  2. Since you’re testing with existing accounts, try deleting and re-creating them to ensure the new mapping takes effect.
  3. Verify that your async mapProfileToUser function is awaited properly (which has been fixed in recent updates) to prevent data loss [2].

These changes should resolve the issue with your custom field not being saved correctly.

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025): This isn’t a bug—it’s an intentional change. With v1.3.33, custom field keys must match your database schema exactly. In your case, since you defined the field as userInfo in your Drizzle schema, your mapProfileToUser callback should return that key as userInfo rather than "user_info" [[1]](https://github.com/better-auth/better-auth/issues/5662). Next steps: 1. Update your mapping function to return the custom field key as userInfo. 2. Since you’re testing with existing accounts, try deleting and re-creating them to ensure the new mapping takes effect. 3. Verify that your async mapProfileToUser function is awaited properly (which has been fixed in recent updates) to prevent data loss [[2]](https://www.better-auth.com/docs/plugins/generic-oauth). These changes should resolve the issue with your custom field not being saved correctly. <!-- 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

@ping-maxwell commented on GitHub (Oct 30, 2025):

Can you test adding debugLogs: true in your drizzle adapter config, then running through the flow to see what gets printed?
Maybe that could provide some insight..

@ping-maxwell commented on GitHub (Oct 30, 2025): Can you test adding `debugLogs: true` in your drizzle adapter config, then running through the flow to see what gets printed? Maybe that could provide some insight..
Author
Owner

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025):

This behavior is intentional. In the latest update, Better Auth now expects the custom field names to match exactly what’s defined in your database schema (i.e. snake_case isn’t auto‐applied inside mapped objects). That’s why using "userInfo" (matching your schema) works while "user_info" does not.

Next steps for troubleshooting:

  1. Update your mapProfileToUser function to return the field using the schema’s exact name (userInfo).
  2. Add debugLogs: true in your Drizzle adapter configuration. Run through your OAuth flow and carefully examine the logs to verify the data that’s being logged and passed into the createUser process.
  3. Ensure that the returned object’s keys strictly match your Drizzle schema definitions.

This debugging should clarify if the mapping reflects the expected behavior and if any other discrepancies occur during the flow [1], [2].

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025): This behavior is intentional. In the latest update, Better Auth now expects the custom field names to match exactly what’s defined in your database schema (i.e. snake_case isn’t auto‐applied inside mapped objects). That’s why using "userInfo" (matching your schema) works while "user_info" does not. Next steps for troubleshooting: 1. Update your mapProfileToUser function to return the field using the schema’s exact name (userInfo). 2. Add debugLogs: true in your Drizzle adapter configuration. Run through your OAuth flow and carefully examine the logs to verify the data that’s being logged and passed into the createUser process. 3. Ensure that the returned object’s keys strictly match your Drizzle schema definitions. This debugging should clarify if the mapping reflects the expected behavior and if any other discrepancies occur during the flow [[1]](https://github.com/better-auth/better-auth/issues/5662), [[2]](https://www.better-auth.com/docs/plugins/generic-oauth). <!-- 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

@lohrm-stabl commented on GitHub (Oct 30, 2025):

So, turns out it hadn't anything to do with snake case.
Snake case is actually wrong. The fields need to match the drizzle name.

https://github.com/better-auth/better-auth/pull/5681 will fix this issue.
Thanks a lot to Maxwell for helping figuring this out and fixing it!

@lohrm-stabl commented on GitHub (Oct 30, 2025): So, turns out it hadn't anything to do with snake case. Snake case is actually wrong. The fields need to match the drizzle name. https://github.com/better-auth/better-auth/pull/5681 will fix this issue. Thanks a lot to Maxwell for helping figuring this out and fixing it!
Author
Owner

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025):

The problem wasn’t with using snake_case per se—it turns out that the keys you return from mapProfileToUser need to exactly match the field names defined in your Drizzle schema. In your case, instead of returning a key named "user_info" (snake_case), you must return "userInfo" (camelCase) so the field maps correctly.

Next steps:

  1. Update your mapProfileToUser function to return custom fields with the same names as defined in your Drizzle schema (i.e., "userInfo" instead of "user_info").
  2. Upgrade your better‑auth package to a version that includes the fix (see the PR from Maxwell) so that this mapping behavior is officially supported.

This should resolve the silent failure of your custom field mapping [1], [2].

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Oct 30, 2025): The problem wasn’t with using snake_case per se—it turns out that the keys you return from mapProfileToUser need to exactly match the field names defined in your Drizzle schema. In your case, instead of returning a key named "user_info" (snake_case), you must return "userInfo" (camelCase) so the field maps correctly. Next steps: 1. Update your mapProfileToUser function to return custom fields with the same names as defined in your Drizzle schema (i.e., "userInfo" instead of "user_info"). 2. Upgrade your better‑auth package to a version that includes the fix (see the PR from Maxwell) so that this mapping behavior is officially supported. This should resolve the silent failure of your custom field mapping [[1]](https://github.com/better-auth/better-auth/issues/5662), [[2]](https://www.better-auth.com/docs/plugins/generic-oauth). <!-- 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)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2230