Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration with an existing project #296

Closed
1 of 5 tasks
Xetera opened this issue Jun 21, 2020 · 12 comments
Closed
1 of 5 tasks

Integration with an existing project #296

Xetera opened this issue Jun 21, 2020 · 12 comments
Labels
question Ask how to do something or how something works

Comments

@Xetera
Copy link

Xetera commented Jun 21, 2020

Your question
Is it possible to to integrate next-auth with a project that already has users? When trying to login with an oauth provider as a user who's already registered in the database I get

Sign in failed
An account associated with your email address already exists.

Sign in with the same account you used originally to confirm your identity.

which I assume is because there are no sessions in the database as that user but because I'm migrating an existing project, there are no existing sessions for any user. Is there a way to get this working or am I approaching it wrong?

I tweaked my existing users table to fit the expected model and added accounts and sessions tables, I'm not sure if that is the correct way to get it working for an existing project though.

Documentation feedback
Documentation refers to searching through online documentation, code comments and issue history. The example project refers to next-auth-example.

  • Found the documentation helpful
  • Found documentation but was incomplete
  • Could not find relevant documentation
  • Found the example project helpful
  • Did not find the example project helpful
@Xetera Xetera added the question Ask how to do something or how something works label Jun 21, 2020
@iaincollins
Copy link
Member

So I think I understand your situation, thanks for the explanation which seems clear.

This error message is triggered when someone has an account (e.g. via email sign in, or sign in with an OAuth provider) but then tries to sign in with a DIFFERENT provider where they have specified the same email address on their account.

It is quite common for sites to automatically link accounts in this scenario, but that is not secure and most authentication services don't do it - and NextAuth.js doesn't do it either. It is possible to link accounts securely, but you already need to be signed in to the account you want to link to.

As well as being secure, this helps reduce instances of people accidentally creating two different accounts with the same service.

So, this feature is working as intended, they should read the error message, and sign in with the same provider they used before (that is in the accounts table). If you don't have an accounts table then you do have a a bit of problem, but the good news is you have several options.

Solutions

These are the options that are probably most relevant:

  1. Configure the OAuth services you want to use, but don't configure a database (which will enable JSON Web Tokens) and use a custom signin() callback to authenticate users and/or insert them in your existing database.

    You will need to write some database code if you want to do this, but it's very quick and easy and you never have to worry about being tightly integrated into NextAuth.js models.

    If you didn't save OAuth provider details (e.g. Account ID) then I would be careful as usually you shouldn't trust the email address returned by OAuth providers, although some providers like Google included a flag like email_verified in the OAuth profile, which is helpful.

  2. Only enable email sign in or (better yet) create a custom error page that prompts the user to sign in via email first and then link their account (this will create the account table entry for them so it will work next time).

    This may be your only option if you didn't save OAuth account details for them.

    Unfortunately securely account linking isn't officially supported yet, but you can do it by sending users to /api/auth/signin if they are already logged in (you will see if you sign in with an account, then sign in with a different account, they are both associated with the same user and that either can then be used to sign in as that user).

  3. Create a new set of tables and import your existing data into them.

    This is a lot easier and more robust than trying to update an existing table, as if things like timestamp fields are declared slightly differently you will likely run into problems at some point. You can use the entityPrefix option if you want NextAuth.js to create the tables in the same database without the table names clashing with existing tables, the option is documented here.

    You only need to populate the users and accounts tables if you want to import users. You don't need to worry about the sessions table.

    For the accounts table you will need:

  • Provider Type (always just oauth currently)
  • Provider ID (e.g. twitter, google, etc)
  • Provider Account ID - the ID of the user with the OAuth Service (e.g. 12345)
  • User ID - the ID of the User in the users table the account is associated with
  • The compoundId - which is a simple hash of the Provider ID and Provider Account ID. See the model for the accounts table to see how this is generated. You don't don't worry about needing an accessToken or refreshToken for each account, those are not required.

There are no one answer if you have an existing database of users, you'll need to decide what works best for you.

NextAuth.js tries hard to to be opinionated about table structure, and provide a good base set of models that should work for a lot of people and try to stick to common conventions for each database (be that an SQL DB or a Document DB), so it's great if you are starting from scratch as it's sensible and easy to extend, but existing apps can be much more complex.

If you have an existing database then Option 1 is probably a good choice for now and avoids a lot of work. You can always try migrating users over later if you decide you like NextAuth.js and it's worth re-considering how you store your user data in future.

@Xetera
Copy link
Author

Xetera commented Jun 21, 2020

Thank you so much for the detailed response, you've given me a lot to think about in terms of how I've previously implemented OAuth as well as my database structure and security going forward. I think I'm going to try solution 1 and see how that works out for me, much appreciated!

@iaincollins
Copy link
Member

I'm glad I could help! We don't have any tutorials that show ways to use the callbacks yet - if you have any questions as to how best to approach this please do reach out.

@IRediTOTO
Copy link

Hi, did you solve this? Can you show me how ? Thank you

@liydaco
Copy link

liydaco commented Jun 14, 2022

Anyone facing this issue and using mongodb to store users - check your 'user' and 'accounts' collections have the same number of fields. This is a bug caused when nextauth creates a new account field for an existing user. To fix it, find the account ID in 'accounts' collection that has no corresponding document in the 'users' collection and delete it. You can do this with an aggregate function in atlas for simplicity.

@drewgainey
Copy link

I'm getting the same error message but it's a completely new project. I have no data in the database

@karan0805
Copy link

I'm glad I could help! We don't have any tutorials that show ways to use the callbacks yet - if you have any questions as to how best to approach this please do reach out.

Did you guys have the implementation ?

@CRYBOII
Copy link

CRYBOII commented Aug 11, 2023

Problem happened after i delete some users from table
i use MongoDBAdapter

Here how i fix

async signIn({
   user,
   account,
   profile,
   email,
   credentials
}) {
  // Check for signup this also condition if Data in Accounts or Users  removed 
  // When login this conditon wil be skip
   if (!user?.init) {
       dbConnect();
       const isAccExist = await Account.findOne({
           providerAccountId: user.id,
       });
       if (isAccExist) {
           await Account.deleteMany({
               providerAccountId: user.id
           });
       }
       const isEmailExist = await User.findOne({
           email: user.email
       });
       try {

       if (isEmailExist) {
           const isExistAccount = await Account.findOne({
             userId: new ObjectId(isEmailExist._id.toString()),
           });
           if (!isExistAccount) {
             await Account.create({
               userId: new ObjectId(isEmailExist._id.toString()),
               expires_at: account?.expires_at,
               scope: account?.scope,
               token_type: account?.token_type,
               type: account?.type,
               providerAccountId: account?.providerAccountId,
               provider: account?.provider,
             });
           }
         }

       } catch (error) {}
       user.init = true;
   } else {}

   return true;
}

Account.tsx



import mongoose from "mongoose";

const accountSchema = new mongoose.Schema({
  userId: {
    type: mongoose.SchemaTypes.ObjectId,
    ref: "User",
    required: true,
  },
  provider: {
    type: String,
    default: "google",
  },
  providerAccountId: {
    type: String,
  },

  token_type: {
    type: String,
    default: "Bearer",
  },
  type: {
    default: "oauth",
    type: String,
  },
  access_token: {
    type: String,
  },
  id_token: {
    type: String,
  },
  scope: {
    type: String,
    default:
      "openid https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile",
  },
  expires_at: {
    type: Number,
  },
});

export default mongoose.models.Account ||
  mongoose.model("Account", accountSchema);

@yalegria
Copy link

yalegria commented Oct 24, 2023

I sort of experienced the same issue. I had two authentication providers set on NextAuth, one with Github and another a custom CAS provider. On my local development I created a new user on the CAS server in order to test things out. Mistakenly or not I used the name as the user of my Github account. Next time I tried to login, I got a message "To confirm your identity, sign in with the same account you used originally."

Just to verify the same name was the issue,, I deleted the records on the User, Account, and Session tables pertaining to the new CAS user. Then I was able to login.

Note: Deleting the records does not solve the problem, but I wanted to confirm that was the issue.

@ShivamMetricoid
Copy link

I am facing similar issue except mine in not existing project it is a new project. I get error when the user who first signed in with email provider next time tries to signin with Google OAuth.
image

or (better yet) create a custom error page that prompts the user to sign in via email first and then link their account (this will create the account table entry for them so it will work next time).

Can anyone eloborate how to implement this? How would i link their account when the user signs in with email?

@karan0805
Copy link

I am facing similar issue except mine in not existing project it is a new project. I get error when the user who first signed in with email provider next time tries to signin with Google OAuth. image

or (better yet) create a custom error page that prompts the user to sign in via email first and then link their account (this will create the account table entry for them so it will work next time).

Can anyone eloborate how to implement this? How would i link their account when the user signs in with email?

Hi @ShivamMetricoid ,

image

Try to use allowDangerousEmailAccountLinking: true flag in Google Provider.

@ShivamMetricoid
Copy link

Thanks @karan0805 for your prompt assistance, it works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Ask how to do something or how something works
Projects
None yet
Development

No branches or pull requests

9 participants