Please refer to the documentation, the example project and existing issues before creating a new issue.
Your question
When I add a callback to the options object from next-auth it runs it 4 times. Why is this so?
What are you trying to do
Here is the little code I execute on session:
...
callbacks: {
session: async (session, user, sessionToken) => {
if (user.isNewUser) {
console.log("user is new user", user.isNewUser);
if (!client.isConnected()) await client.connect();
const db = client.db("copy-pasta");
const collection = await db.collection("users");
const defaultSnippet = {
id: new ObjectID(),
title: "this is a dummy Title",
body:
"Dummy body. Double click me to edit or add a new on on the top",
};
// only run that the first time when user.snippets does not exist yet. So after the first `updateOne` this altough called
severall times should not be true again. But it seems that it is executing 4 times, because I have the object 4 times in the
array.
if (user && !user.snippets) {
await collection.updateOne(
{ email: user.user.email },
{ $push: { snippets: defaultSnippet } }
);
}
user.isNewUser = false;
return Promise.resolve(session);
}
console.log("the session:", session);
return Promise.resolve(session);
},
}
This essentially is a code that should run only the first time a user logs in. But currently when I'm logged in I have this entry 4 times in my db. So it pushes the dummy object 4 times into my database. That means the code runs 4 times.
Why is this callback running 4 times? I also placed a console.log in if (user && !user.snippets) and it runs 4 times. The user.snippets is unfedfined 4 times. But then it has the object 4 times.
Context of what I want to achieve here. Imagine a trello board webapp. And you signup and you already have 1 "tutorial" card on it. The same thing I want to do here. On first sign up it should add a "card" or "snippet" to the user so he can immediately see it and can do things with it.
How can I either solve this issue, or is there another callback or hook in the package that is a better fit for what I want to achieve here?
Documentation feedback
Documentation refers to searching through online documentation, code comments and issue history. The example project refers to next-auth-example.
There are probably one or more reasons for the multiple calls.
npm run build & npm start).useSession() hook is present in more than one component.I appreciate the problem but I'm not sure if have a solution for it, but you could try this:
There is a signIn() event that gets fired exactly once after sign in that might be useful. IIRC it's also a blocking call, so you can execute code it in it after they have authenticated but before a response is returned to the user.
Note: There is also a signIn() callback but that probably isn't so useful as that is called before the user is created in the database (to allow you to reject the sign in request), so you probably want the event.
Hi @iaincollins and thx for the reply.
The signIn event sounds perfect, but unfortunately it doesn't run.
signIn: async (user, account, profile) => {
// doesnt get called, no console.log in the console
console.log("the best user,", user);
return Promise.resolve(true);
},
I use Google Login provider. Does signIn run when using a provider?
Oh! Hmm. I have not checked this, but the code for it looks good at first glance (I've just pulled it up) so I'm not sure why it wouldn't be. I'll take a look later!
Ok very appreciated! Here is a bit more context how I use signIn here is my whole options object:
const options = {
site: process.env.SITE,
providers: [
Providers.Google({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
Providers.Email({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM,
}),
],
database: process.env.DATABASE_URL,
secret: process.env.SECRET,
callbacks: {
signIn: async (user, account, profile) => {
// Doesnt appear in my console, by either signing in via Google or Email
console.log("the best user,", user);
return Promise.resolve(true);
},
},
session: {
jwt: true,
},
};
Also with email signin it doesn't run the event. Just checked. The console.log does not show in my console. I use next-auth v2 btw.
BIG EDIT: I think you meant event according to the doc here: https://next-auth.js.org/configuration/events
For some reason I tought you mean Callback like here: https://next-auth.js.org/configuration/callbacks.
When I use it as event, how do I get access to the user that logged in?
Ah thanks!
Try using this as the config:
const options = {
providers: [
Providers.Google({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
Providers.Email({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM,
}),
],
database: process.env.DATABASE_URL,
secret: process.env.SECRET,
events: {
signIn: async (message) => { /* on successful sign in */ },
console.log("Sign In Event,", message);
},
},
session: {
jwt: true,
},
};
Note: You will want to be running v3 (released today). You don't need to specify site parameter any more, that's now handled by the NEXTAUTH_URL environment variable (only required in production, defaults to http://localhost:3000 in development).
Hi,
I edited my post just figured I used callback instead of events. I tried it now, but console.log still doesn't run after login with google. What am I doing wrong?
Here is my options object:
const options = {
site: process.env.SITE,
providers: [
Providers.Google({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
Providers.Email({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM,
}),
],
database: process.env.DATABASE_URL,
secret: process.env.SECRET,
callbacks: {
signIn: async (user, account, profile) => {
console.log("the best user,", user);
// Return false to display a default error message
return Promise.resolve(true);
},
},
events: {
signIn: async (message) => {
console.log("the message", message);
},
},
session: {
jwt: true,
},
};
Awesome that v3 released today. I think I will check it out :)
Ah, the callback and events were called signin in v2 and signIn in v3 (i.e. was lowercase, now camelCase).
I appreciate this was an annoying change and is going to trip people up, but it was probably more annoying to leave the different methods inconsistent in the long run; everything is at least consistently camelCase in the API now!
@iaincollins you are a hero. That's it. And now everything works as expected.
Here my code if anyone needs it in the future (this is for v2, for v3 use camelCase signIn):
events: {
signin: async (message) => {
console.log("the message", message);
console.log("is new user?", message.isNewUser);
if (message.isNewUser) {
if (!client.isConnected()) await client.connect();
const db = client.db("copy-pasta");
const collection = await db.collection("users");
const defaultSnippet = {
id: new ObjectID(),
title: "this is a dummy Title",
body:
"Dummy body. Double click me to edit or add a new on on the top",
};
await collection.updateOne(
{ email: message.user.email },
{ $push: { snippets: defaultSnippet } }
);
}
},
},
Thank you very much! You can close the issue. I'm absolutely in love with this repo.
Oh that's great news, thank you for leaving feedback - it's really helpful to know if a suggestion works out in practice.
It sounds like this is a direction we should maybe point folks towards in a future tutorial, rather than advising they use the Session callback for database writes (due to the issues you've raised with that approach).
Hi there! It looks like this issue hasn't had any activity for a while. It will be closed if no further activity occurs. If you think your issue is still relevant, feel free to comment on it to keep ot open. Thanks!
Most helpful comment
Oh that's great news, thank you for leaving feedback - it's really helpful to know if a suggestion works out in practice.
It sounds like this is a direction we should maybe point folks towards in a future tutorial, rather than advising they use the Session callback for database writes (due to the issues you've raised with that approach).