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

What is the best way to integrate an authentication system into Next.js?

With minimal exposure to Next.js, I find myself stuck in the process of creating a user profile for my website. Despite researching ways to incorporate authentication systems into Next.js, I'm struggling to grasp the concept. Essentially, I aim to cr ...

"The variables in the .env file are not reflecting the latest updates. What is the best way

Currently, I am in the process of developing an application using Quasar (Vue) where I have stored my database keys in a .env file. Recently, I encountered an issue when attempting to switch to another instance and updating the keys in the env file. Despit ...

What are some strategies for troubleshooting asynchronous errors in JavaScript function calls?

I'm currently working on an asynchronous JavaScript code that utilizes the async method findDevices from the class Devices, which is located in a separate file. This method involves performing a mongo find operation within the IDevices collection. Her ...

Connecting a segment on a different page in Next.js

Is it feasible to create a link that directs to a specific section on another page in Next.js? When clicking the link, I would like to be taken directly to the designated section. While this is achievable in standard HTML, can it also be implemented in N ...

Inject a heavy dose of Vue into your project

**I'm trying to implement the provide/inject logic in Vue. In my 'App.vue' component, I have defined the 'firstName' input as a string "John", and I want to display this value when the child component 'Step1' is created. ...

WebDriverIO effortlessly converts the text extracted using the getText() command

One of my webpage elements contains the following text: <span class="mat-button-wrapper">Sicherheitsfrage ändern</span> However, when I attempt to verify this text using webdriver, it indicates that it is incorrect assert.strictEqual($(.mat ...

Can anyone suggest a method to block the execution of Javascript commands in the console?

Regarding the inquiry mentioned in the question title. Is it possible to prevent individuals from executing functions on my webpage using the console, even though it is not necessary? I have experimented with various methods, and the code below represent ...

The revalidation feature in Next.js' getStaticProps function does not seem to be

https://i.stack.imgur.com/vnNMQ.png I have a question regarding my use of the getStaticProps function in index.js. I am trying to ensure that my API call runs every 60 seconds when a user visits my page, but I am experiencing issues with revalidate not wo ...

Next.js Issue: Invariant error - page not correctly generated

I encountered a recurring error while attempting to build my project. Strangely, everything runs smoothly during development, but as soon as the build process is initiated, the following error presents itself: next build ▲ Next.js 14.1.0 - Environm ...

Is it acceptable to halt the execution of an async route handler in an Express application using a return statement?

My async route handler is set up like this: router.post('/', async function (req, res, next) { try { const { username, email, password } = req.body const { errors, userData } = validateRegistrationInput(username, email, password) if ...

Encountering issues with uploading Cloudinary images through Nodejs

I'm having trouble uploading a base64encoded image to Cloudinary using the code snippet below. It's not working as expected and I keep getting a 500 error when making the post request. Can anyone provide me with a solution or suggest what might b ...

Alter the font color upon clicking the menu using jQuery

When I click on the menu and sub-menu items, I want to change their colors along with their parent. You can see an example of how I want it to work here. However, currently, when I click on a sub-menu item, the color of the parent menu item gets removed. ...

Next.js image component suffers from poor quality when viewed on Google Chrome

I am currently working on a website using the next.js framework. I have encountered an issue with the Image component specifically in Google Chrome. The image loads with poor quality on Chrome, however, when downloaded directly from the browser it appears ...

The storage capacity of localStorage is insufficient for retaining data

While I was trying to troubleshoot an error, I encountered another issue. var save_button = document.getElementById('overlayBtn'); if(save_button){ save_button.addEventListener('click', updateOutput);} This led to the following ...

Having trouble retrieving the default selected value using the index in Angular Material's mat-button-toggle functionality

I'm encountering an issue with setting the default value for a toggle button group. The code is simple and the toggle buttons are correctly fetching values from the index, but I can't seem to get one of them to be default selected. I tried settin ...

Troubleshooting issue with Angular's ng-class functionality malfunctioning

I am currently working on displaying a class using ng-class in the box element of my HTML code. However, it does not seem to be functioning as expected. Can someone please help me identify what I might be doing wrong? Check out this link for reference. ...

Adjust website content depending on user's authentication status

My goal is to display a logout button when the user is logged in and a login button if they are not. I am using JSON tokens to determine if a user is logged in or not, by checking if the token is null. However, this approach does not seem to be working. Ca ...

Add a Page to Your Domain Name using the Text Input Box

I'm looking to create an input field that allows users to enter a text string, which will be added to a domain name when submitted, redirecting the user to a specific page. Here's how the process works: The user enters 'foo' into the ...

Tips for swapping out a specific string in an HTML webpage with a different string using JavaScript

For my HTML website, I am trying to replace one string with another using JavaScript. Specifically, within the nodeList "AuthorList," there is a string called "Test String" that needs to be changed to "new test." I have attempted various modifications of ...

Mapping an array using getServerSideProps in NextJS - the ultimate guide!

I'm facing an issue while trying to utilize data fetched from the Twitch API in order to generate a list of streamers. However, when attempting to map the props obtained from getServerSideProps, I end up with a blank page. Interestingly, upon using co ...