account linking still requires email #998

Closed
opened 2026-03-13 08:16:27 -05:00 by GiteaMirror · 7 comments
Owner

Originally created by @maxwiseman on GitHub (Apr 8, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Create a config for a generic provider that does not provide the user email (similar to Twitter/X). The email_is_missing error is triggered.
  2. Try returning { email: null } as a workaround, like Twitter/X provider does.
  3. email_is_missing is still thrown.

Current vs. Expected behavior

#1479 mentions that some providers, in accordance with the oauth2 spec, do not provide an email during authentication.

#1481 fixed part of that, allowing the email to be different from existing emails in accordance with the allowDifferentEmails option.

However, it would make sense for emails to be optional when linking accounts. Currently, some dummy value must be provided (such as user id) to the email field, but as long as allowDifferentEmails is enabled, it is not used for anything on the backend.

Therefor, instead of requiring this workaround, it would make more sense to allow a blank email if you're trying to link accounts, and allowDifferentEmails is turned on.

What version of Better Auth are you using?

1.2.5

Provide environment information

N/A

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

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { genericOAuth } from "better-auth/plugins";
import { db } from "./db";
import { env } from "@/env";

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: "sqlite",
    usePlural: true,
  }),
  emailAndPassword: {
    enabled: true,
    requireEmailVerification: false,
  },
  account: {
    accountLinking: {
      enabled: true,
      allowDifferentEmails: true,
      trustedProviders: ["google", "github", "email-password", "strava"],
    },
  },
  socialProviders: {
    github:
      {
        clientId: env.GITHUB_CLIENT_ID,
        clientSecret: env.GITHUB_CLIENT_SECRET,
      },
  },
  plugins: [
    genericOAuth({
      config: [
        {
          providerId: "strava",
          responseMode: "query",
          clientId: env.STRAVA_CLIENT_ID,
          clientSecret: env.STRAVA_CLIENT_SECRET,
          redirectURI: `${env.AUTH_URL}/api/auth/oauth2/callback/strava`,
          authorizationUrl: "https://www.strava.com/oauth/authorize",
          tokenUrl: "https://www.strava.com/oauth/token",
          //   userInfoUrl: "https://www.strava.com/api/v3/athlete",
          scopes: ["activity:read_all,read_all"],

          //   @ts-expect-error -- this is a test
          async getUserInfo(tokens) {
            const data = await fetch("https://www.strava.com/api/v3/athlete", {
              headers: { Authorization: `Bearer ${tokens.accessToken}` },
            }).then(
              (data) =>
                data.json() as Promise<{
                  firstname: string;
                  lastname: string;
                  username: string;
                }>,
            );
            return {
              ...data,
              email: data.username, // This is required to fix the error, even though it makes no sense b/c it isn't an email
              emailVerified: true,
              name: `${data.firstname} ${data.lastname}`,
            };
          },
        },
      ],
    }),
  ],
  baseURL: env.AUTH_URL,
});

Additional context

No response

Originally created by @maxwiseman on GitHub (Apr 8, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Create a config for a generic provider that does not provide the user email (similar to Twitter/X). The `email_is_missing` error is triggered. 3. Try returning { email: null } as a workaround, like Twitter/X provider does. 4. `email_is_missing` is still thrown. ### Current vs. Expected behavior #1479 mentions that some providers, in accordance with the oauth2 spec, do not provide an email during authentication. #1481 fixed part of that, allowing the email to be different from existing emails in accordance with the `allowDifferentEmails` option. However, it would make sense for emails to be _optional_ when linking accounts. Currently, some dummy value must be provided (such as user id) to the email field, but as long as `allowDifferentEmails` is enabled, it is not used for _anything_ on the backend. Therefor, instead of requiring this workaround, it would make more sense to allow a blank email if you're trying to link accounts, and `allowDifferentEmails` is turned on. ### What version of Better Auth are you using? 1.2.5 ### Provide environment information ```bash N/A ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { genericOAuth } from "better-auth/plugins"; import { db } from "./db"; import { env } from "@/env"; export const auth = betterAuth({ database: drizzleAdapter(db, { provider: "sqlite", usePlural: true, }), emailAndPassword: { enabled: true, requireEmailVerification: false, }, account: { accountLinking: { enabled: true, allowDifferentEmails: true, trustedProviders: ["google", "github", "email-password", "strava"], }, }, socialProviders: { github: { clientId: env.GITHUB_CLIENT_ID, clientSecret: env.GITHUB_CLIENT_SECRET, }, }, plugins: [ genericOAuth({ config: [ { providerId: "strava", responseMode: "query", clientId: env.STRAVA_CLIENT_ID, clientSecret: env.STRAVA_CLIENT_SECRET, redirectURI: `${env.AUTH_URL}/api/auth/oauth2/callback/strava`, authorizationUrl: "https://www.strava.com/oauth/authorize", tokenUrl: "https://www.strava.com/oauth/token", // userInfoUrl: "https://www.strava.com/api/v3/athlete", scopes: ["activity:read_all,read_all"], // @ts-expect-error -- this is a test async getUserInfo(tokens) { const data = await fetch("https://www.strava.com/api/v3/athlete", { headers: { Authorization: `Bearer ${tokens.accessToken}` }, }).then( (data) => data.json() as Promise<{ firstname: string; lastname: string; username: string; }>, ); return { ...data, email: data.username, // This is required to fix the error, even though it makes no sense b/c it isn't an email emailVerified: true, name: `${data.firstname} ${data.lastname}`, }; }, }, ], }), ], baseURL: env.AUTH_URL, }); ``` ### Additional context _No response_
GiteaMirror added the enhancement label 2026-03-13 08:16:27 -05:00
Author
Owner

@maxwiseman commented on GitHub (Apr 8, 2025):

I think the fix is as simple as changing this line as follows:

if (!link && ctx.context.options.account?.accountLinking?.allowDifferentEmails !== true && !mapUser?.email) {
    ctx.context.logger.error("Unable to get user info", userInfo);
    throw redirectOnError("email_is_missing");
}

72631554e5/packages/better-auth/src/plugins/generic-oauth/index.ts (L611-L614)

I don't have a great way to test this, but I'm pretty confident that would fix it.

@maxwiseman commented on GitHub (Apr 8, 2025): I think the fix is as simple as changing this line as follows: ```tsx if (!link && ctx.context.options.account?.accountLinking?.allowDifferentEmails !== true && !mapUser?.email) { ctx.context.logger.error("Unable to get user info", userInfo); throw redirectOnError("email_is_missing"); } ``` https://github.com/better-auth/better-auth/blob/72631554e576dfd5e0521b1f2774bcaf19c7241a/packages/better-auth/src/plugins/generic-oauth/index.ts#L611-L614 I don't have a great way to test this, but I'm pretty confident that would fix it.
Author
Owner

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

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

Issue Summary:

  • You reported that Better Auth's account linking requires an email, which is problematic for OAuth providers like Twitter/X that do not provide user emails.
  • You suggested allowing a blank email when the allowDifferentEmails option is enabled, instead of requiring a dummy email, to better comply with OAuth2 specifications.
  • You proposed a simple code change to address this but mentioned lacking a good way to test it.
  • There have been no further comments or maintainer responses on this issue.

Next Steps:

  • Please let me know if this issue is still relevant with the latest version of better-auth by commenting here to keep the discussion open.
  • If I do not hear back within 7 days, this issue will be automatically closed.

Thank you for your understanding and contribution!

@dosubot[bot] commented on GitHub (Aug 4, 2025): Hi, @maxwiseman. 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 reported that Better Auth's account linking requires an email, which is problematic for OAuth providers like Twitter/X that do not provide user emails. - You suggested allowing a blank email when the `allowDifferentEmails` option is enabled, instead of requiring a dummy email, to better comply with OAuth2 specifications. - You proposed a simple code change to address this but mentioned lacking a good way to test it. - There have been no further comments or maintainer responses on this issue. **Next Steps:** - Please let me know if this issue is still relevant with the latest version of better-auth by commenting here to keep the discussion open. - If I do not hear back within 7 days, this issue will be automatically closed. Thank you for your understanding and contribution!
Author
Owner

@beeman commented on GitHub (Aug 11, 2025):

Opening this again as I'd love to see email being optional altogether. Imo, the ideal situation is that Better Auth supports either username or email as the primary unique identifier. Usernames are way better to make it client-facing without revealing a users' email. In addition, a user commonly has multiple emails, and not multiple usernames so from a data modeling point of view it's a more obvious choice.

Lastly, you can always use the email address (value) in the username field if you don't care about making it client-facing, the other way around is weird.

@beeman commented on GitHub (Aug 11, 2025): Opening this again as I'd love to see `email` being optional altogether. Imo, the ideal situation is that Better Auth supports either `username` or `email` as the primary unique identifier. Usernames are way better to make it client-facing without revealing a users' email. In addition, a user commonly has multiple emails, and not multiple usernames so from a data modeling point of view it's a more obvious choice. Lastly, you can always use the email address (value) in the username field if you don't care about making it client-facing, the other way around is weird.
Author
Owner

@hburn7 commented on GitHub (Sep 3, 2025):

Should this be closed? Would also appreciate seeing this implemented - osu! is a provider which does not return user emails. Lots of unnecessary head scratching over something that is intended by the provider.

@hburn7 commented on GitHub (Sep 3, 2025): Should this be closed? Would also appreciate seeing this implemented - [osu!](https://osu.ppy.sh/docs/index.html#authentication) is a provider which does not return user emails. Lots of unnecessary head scratching over something that is intended by the provider.
Author
Owner

@dantman commented on GitHub (Sep 7, 2025):

Another provider that does not return user emails is AniList.

I thought I got around this by using {anilist_id}@https://anilist.co as the email address, to follow the pattern that was established by the Anonymous auth plugin. But instead when trying to sign back in after my first login I get the account_not_linked error.

@dantman commented on GitHub (Sep 7, 2025): Another provider that does not return user emails is [AniList](https://docs.anilist.co/guide/auth/). I thought I got around this by using `{anilist_id}@https://anilist.co` as the email address, to follow the pattern that was established by the Anonymous auth plugin. But instead when trying to sign back in after my first login I get the account_not_linked error.
Author
Owner

@VIEWVIEWVIEW commented on GitHub (Oct 13, 2025):

This issue is still labelled as "not planned" by the bot, although it's still assigned to the 1.3.x milestone. It looks like it was simply closed + not planned by the bot due to inactivity, and then forgotten. @Bekacru are there still plans for this?

This issue (https://github.com/better-auth/better-auth/issues/4757) by @himself65 says that no new features/breaking changes are planned for 1.3.x, so my question is whether the plan to "make fields more dynamic" (as mentioned here https://github.com/better-auth/better-auth/issues/2402#issuecomment-2824147883) is still planned and simply moved into 1.4, or if it's fully dead? I haven't seen any recent issues or PRs tracking and being updated this. :(

It would be great, if we could finally allow users to sign up without an email, and just with username+pw. (our use case; we currently add a filler random email under our own control to all newly created users, but it's an ugly hack)

@VIEWVIEWVIEW commented on GitHub (Oct 13, 2025): This issue is still labelled as "not planned" by the bot, although it's still assigned to the 1.3.x milestone. It looks like it was simply closed + not planned by the bot due to inactivity, and then forgotten. @Bekacru are there still plans for this? This issue (https://github.com/better-auth/better-auth/issues/4757) by @himself65 says that no new features/breaking changes are planned for 1.3.x, so my question is whether the plan to "make fields more dynamic" (as mentioned here https://github.com/better-auth/better-auth/issues/2402#issuecomment-2824147883) is still planned and simply moved into 1.4, or if it's fully dead? I haven't seen any recent issues or PRs tracking and being updated this. :( It would be great, if we could finally allow users to sign up without an email, and just with username+pw. (our use case; we currently add a filler random email under our own control to all newly created users, but it's an ugly hack)
Author
Owner

@kupppo commented on GitHub (Dec 27, 2025):

I'd also love to see email be entirely optional. In my application, Discord is the primary social login mechanism and email is completely useless. I want my users to be able to link their Twitch accounts to my platform, but this makes it have significantly more friction without resorting to the generic OAuth plugin mentioned above. Since Twitch is an actually first-party offering from the library, I'd much rather use that than have my own hand-rolled version.

@kupppo commented on GitHub (Dec 27, 2025): I'd also love to see email be entirely optional. In my application, Discord is the primary social login mechanism and email is completely useless. I want my users to be able to link their Twitch accounts to my platform, but this makes it have significantly more friction without resorting to the generic OAuth plugin mentioned above. Since Twitch is an actually first-party offering from the library, I'd much rather use that than have my own hand-rolled version.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#998