Is it possible to utilize various providers in Next-Auth while still maintaining the same email address?

I am currently developing a Next.js application using NextAuth. At the moment, I have implemented login functionality with Google, credentials, and GitHub. However, I encountered an issue where if a user logs in with Google using the email "[email protected]", then logs out and tries to log in with GitHub using an account that shares the same email "[email protected]", they receive an error: OAuthAccountNotLinked.

Within the database model provided by NextAuth, each User has a relation with Accounts which is an array. Therefore, I thought it would be possible to have the same user linked with two different accounts. Am I missing something or is this the default behavior?

Below is my code snippet from [...nextAuth].ts:

export const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  const data = requestWrapper(req, res);
  return await NextAuth(...data);
};

export default handler;

// Function to wrap the request parameters
export function requestWrapper(
  req: NextApiRequest,
  res: NextApiResponse
): [req: NextApiRequest, res: NextApiResponse, opts: NextAuthOptions] {
  // Define utility functions
  const generateSessionToken = () => randomUUID();

  const fromDate = (time: number, date = Date.now()) =>
    new Date(date + time * 1000);

  const adapter = PrismaAdapter(prisma);

  const opts: NextAuthOptions = {
    // Include user.id on session
    adapter: adapter,
    pages: {
      signIn: "/login",
    },
    callbacks: {
      session({ session, user }) {
        console.log("AAAA");
        if (session.user) {
          session.user = user;
        }
        return session;
      },
      async signIn({ user, account, profile, email, credentials }) {
        // Check if this sign in callback is being called in the credentials authentication flow
        if (
          req.query.nextauth?.includes("callback") &&
          req.query.nextauth?.includes("credentials") &&
          req.method === "POST"
        ) {
          if (user) {
            const sessionToken = generateSessionToken();
            const sessionMaxAge = 60 * 60 * 24 * 30; 
            const sessionExpiry = fromDate(sessionMaxAge);

            await adapter.createSession({
              sessionToken: sessionToken,
              userId: user.id,
              expires: sessionExpiry,
            });

            const cookies = new Cookies(req, res);

            cookies.set("next-auth.session-token", sessionToken, {
              expires: sessionExpiry,
            });
          }
        }

        return true;
      },
    },
    jwt: {
      encode: async ({ token, secret, maxAge }) => {
        if (
          req.query.nextauth?.includes("callback") &&
          req.query.nextauth.includes("credentials") &&
          req.method === "POST"
        ) {
          const cookies = new Cookies(req, res);
          const cookie = cookies.get("next-auth.session-token");
          if (cookie) return cookie;
          else return "";
        }
        // Revert to default behaviour when not in the credentials provider callback flow
        return encode({ token, secret, maxAge });
      },
      decode: async ({ token, secret }) => {
        if (
          req.query.nextauth?.includes("callback") &&
          req.query.nextauth.includes("credentials") &&
          req.method === "POST"
        ) {
          return null;
        }

        // Revert to default behaviour when not in the credentials provider callback flow
        return decode({ token, secret });
      },
    },
    // Configure authentication providers
    secret: process.env.NEXTAUTH_SECRET,
    providers: [
      GithubProvider({
        clientId: process.env.GITHUB_ID as string,
        clientSecret: process.env.GITHUB_SECRET as string,
        profile(profile, token) {
          return {
            id: profile.id.toString(),
            name: profile.name || profile.login,
            image: profile.avatar_url,
            email: profile.email,
            role: Role.USER,
          };
        },
      }),
      GoogleProvider({
        clientId: process.env.GOOGLE_ID as string,
        clientSecret: process.env.GOOGLE_SECRET as string,
        authorization: {
          params: {
            prompt: "consent",
            access_type: "offline",
            response_type: "code",
          },
        },
      }),
      CredentialProvider({
        name: "CredentialProvider",
        credentials: {
          email: { label: "Email", type: "text", placeholder: "" },
          password: { label: "Password", type: "password" },
        },
        async authorize(credentials: any, _req): Promise<any | null> {
          const userInputs = {
            email: credentials.email,
            password: credentials.password,
          };

          const { user } = await loginCredentials(userInputs);

          if (user) {
            return user;
          } else {
            return null;
          }
        },
      }),
    ],
  };

  return [req, res, opts];
}

Answer №1

To resolve the issue, I found success by including

allowDangerousEmailAccountLinking: true
in my code like this:

   providers: [
    GithubProvider({
      clientId: env.GITHUB_CLIENT_ID,
      clientSecret: env.GITHUB_CLIENT_SECRET,
      allowDangerousEmailAccountLinking: true,
    }),
    GoogleProvider({
      clientId: env.GOOGLE_CLIENT_ID,
      clientSecret: env.GOOGLE_CLIENT_SECRET,
      allowDangerousEmailAccountLinking: true,
    }),
   ]

Implementing this change helped alleviate many of the account linking errors. However, I am open to hearing about any alternative solutions that others may have discovered.

For further details, visit: https://next-auth.js.org/configuration/providers/oauth#allowdangerousemailaccountlinking-option

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Retrieve the file name from the URL while disregarding the query string

Looking for a way to extract the test.jsp from this URL: http://www.xyz.com/a/test.jsp?a=b&c=d Can anyone provide guidance on how to accomplish this task? ...

What is the best way to transform this JSON data into an array of key-value pairs in JavaScript?

Dealing with nested JSON data can be challenging, especially when trying to extract key-value pairs efficiently. If anyone has suggestions on how to simplify this process and improve readability, please share your insights. The goal is to transform the ne ...

Differences between Typescript Import and JavaScript import

/module/c.js, attempting to export name and age. export const name = 'string1'; export const age = 43; In b.ts, I'm trying to import the variables name and age from this .ts file import { name, age } from "./module/c"; console.log(name, ...

While it is possible to filter by friend.brand.id within friends in Angular, unfortunately the || undefined trick cannot be used to reset the filter when it is NULL

In the futuristic world of 2075, immortal humans have subjugated mortal humans. There exists a unique store where mortal friends are sold, and efforts are being made to develop an app that allows customers to manage their friends based on their favorite br ...

Retrieving information from child components in Next.js

In my page.tsx component, I am trying to retrieve the current pathname as it is a client-side component. The issue arises when I have a child server component that needs to fetch data based on the received pathname prop. Unfortunately, attempts to fetch th ...

JSON is not being parsed in Next.js

Encountering an issue where Next.js parses JSON in development mode, but uses the json state in production mode. It's quite amazing! Check out the configuration file config.next.js below: const {webpack} = require("next/dist/compiled/webpack/webp ...

Learn how to iterate over an array and display items with a specific class when clicked using jQuery

I have an array with values that I want to display one by one on the screen when the background div is clicked. However, I also want each element to fade out when clicked and then a new element to appear. Currently, the elements are being produced but th ...

A guide on integrating a submission button that combines values from multiple dropdown forms and redirects to a specific URL

Is there a way to make the submit button on this form act based on the combined values of two different dropdown menus? For instance, if "west" is selected from the first dropdown and "winter" is selected from the second dropdown, I want it to navigate to ...

In the realm of Next.js 13.4 API streaming, tokens make their way to localhost in a staggered fashion, whereas in a production environment

Issue Encountered with API Streaming Behavior in Next.js 13.4 An unexpected issue has arisen with the API streaming functionality in Next.js 13.4 when using the app router, specifically related to the behavior of tokens being sent all at once instead of i ...

Enhancing productivity with tools for developers and effortless tab navigation

During my development process, I always keep the developer tools open on one or more of my tabs. However, I noticed that when I switch to a tab where the developer tools were not previously open, a resize event is triggered. Strangely, this event causes el ...

Safari is not properly rendering a React/Next.js website (showing a blank page)

Recently, I've been facing a frustrating bug on my Next.js website. When I try to open it in Safari, there's a 50/50 chance that it will either load correctly or show a blank page with faint outlines of components but no text. This issue occurs o ...

React Resize Detection: Handling Window Resize Events

Is there a more efficient way to listen for the window resize event in react.js without causing multiple callbacks? Perhaps a React-oriented approach (like using a hook) to achieve this? const resizeQuery = () => { console.log("check"); if ( ...

Utilize jQuery to manipulate a subset of an array, resulting in the creation of a new array through the use of

I have a string formatted like this: ItemName1:Rate1:Tax1_ItemName2:Rate2:Tax2:_ItemName3:Rate3:Tax3_ItemName4:Rate4:Tax4 (and so on, up to item 25). My task is to take an index provided by the user (for example, 2), retrieve the item at that index when ...

``The problem of cross-origin resource sharing (CORS)

Encountering a CORS error when sending the request, although it works fine in Postman Error Message: The fetch request to (cloud function url) from my web app origin is being blocked by CORS policy: No 'Access-Control-Allow-Origin' header is p ...

Using JavaScript to search for a specific string within a row and removing that row if the string is detected

I need help with a script that removes table rows containing the keyword STRING in a cell. However, my current script is deleting every other row when the keyword is found. I suspect this has to do with the way the rows are renumbered after deletion. How c ...

Angular mistakenly uses the incorrect router-outlet

Encountering an issue with Angular routing. The main app has its own routing module, and there is a sub module with its own routing module and router-outlet. However, the routes defined in the submodule are being displayed using the root router outlet inst ...

Is it necessary to include @types/ before each dependency in react native?

I am interested in converting my current react native application to use typescript. The instructions mention uninstalling existing dependencies and adding new ones, like so: yarn add --dev @types/jest @types/react @types/react-native @types/react-test- ...

Retrieve the location of the selected element

We are faced with the challenge of determining the position of the button clicked in Angular. Do you think this is achievable? Our current setup involves an ng-grid, where each row contains a button in column 1. When the button is clicked, we aim to displ ...

Storing the information received from an API as an HTML element

My HTML file contains JavaScript, along with a URL that displays data retrieved from an AWS lambda API call via AWS API Gateway. The page initially appears blank and the data is structured like this: [ {"user": "bob", "groups": ["bobsGroup"], "policies": ...

JavaScript Grouping Arrays

I am working with an array and need to filter it by both Country and Service. So far, I have successfully filtered the array by Country. Now, I want to achieve the same filtering process based on the Service as well. Here is a snippet of the array: [ ...