next-auth icon indicating copy to clipboard operation
next-auth copied to clipboard

Session is not updated after user email verifcation

Open Ali-Raza764 opened this issue 1 year ago • 6 comments

Environment

System: OS: Windows 10 10.0.19045 CPU: (4) x64 Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz Memory: 2.39 GB / 7.92 GB Binaries: Node: 21.5.0 - C:\Program Files\nodejs\node.EXE npm: 10.2.5 - C:\Program Files\nodejs\npm.CMD Browsers: Edge: Chromium (123.0.2420.97) Internet Explorer: 11.0.19041.4355 npmPackages: next: 14.2.3 => 14.2.3 react: ^18 => 18.3.1

Reproduction URL

https://github.com/Ali-Raza764/next-auth-tookit

Describe the issue

I have tried using the unstable_update and to customly rewrite the session after the email verification at the route but it does not seem to work `import { auth, unstable_update } from "@/auth"; // Correctly import unstable_update import User from "@/utils/db/models/user.model"; import { NextResponse } from "next/server"; import dbConnect from "@/utils/db/dbConnect";

export async function GET(request) { try { const { searchParams } = new URL(request.url); const token = searchParams.get('token'); console.log("token", token);

const session = await auth();
if (!session) {
  return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
}

await dbConnect();

const userId = session.user.id;
const user = await User.findById(userId);

if (!user) {
  return NextResponse.json({ message: "User not found" }, { status: 404 });
}

const currentTime = new Date().getTime();

if (token === user.verificationToken && currentTime < user.verificationTokenExpiry) {
  user.isVerified = true;
  await user.save();

  // Update the session and token with the new isVerified status
  await unstable_update({ ...session, user: { ...session.user, isVerified: true } });

  console.log("Route Session", session.user);

  return NextResponse.json({ message: "Verification successful" });
}

return NextResponse.json({ message: "Verification failed" }, { status: 400 });

} catch (error) { console.error("Error verifying token:", error); return NextResponse.json({ message: "Internal Server Error" }, { status: 500 }); } }`

here is my protected route `import { auth } from "@/auth"; import React from "react"; import SignOutButton from "../auth/components/SignOut"; import { redirect } from "next/navigation";

const Protected = async () => { const session = await auth(); console.log(session); if (!session.user.isVerified) { redirect("/auth/verify"); } return (

{JSON.stringify(session)} <SignOutButton />
); };

export default Protected; `

How to reproduce

  1. Clone the repo
  2. Install dependencies npm install
  3. npm run dev

Expected behavior

The session should get updated and I should be able to access the protected route.

Ali-Raza764 avatar Jun 02 '24 05:06 Ali-Raza764

await unstable_update({ user: { ...session.user, isVerified: true } }); Also tried this way but does not seem to work

Ali-Raza764 avatar Jun 02 '24 06:06 Ali-Raza764

i have the same problem, but when changing the user's avatar.

TauanAlmeida avatar Jul 31 '24 02:07 TauanAlmeida

Same problem here.

Did you find any solution @Ali-Raza764 ?

I think I'll move updating it in the client instead in the server, but I don't like this way.

suitux avatar Aug 17 '24 12:08 suitux

No I tried several ways before but was not successfull I will try again !

Ali-Raza764 avatar Aug 18 '24 05:08 Ali-Raza764

I have tried using the client useSession() hook to do this but it didnot work either To me it seems that the way my callbacks are working is causing the issues I will dig deeper

Ali-Raza764 avatar Aug 18 '24 05:08 Ali-Raza764

await unstable_update({ user: { ...session.user, isVerified: true } }); Also tried this way but does not seem to work

When I changed the user name through prisma, the jwt data violated the refresh

monsterooo avatar Aug 20 '24 11:08 monsterooo

hi guys I think I have solved it auth.js file:

import NextAuth, { CredentialsSignin } from "next-auth";
import Credentials from "next-auth/providers/credentials";

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    Credentials({
      credentials: {
        username: { label: "Username" },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials) {
        // Add database logic here to cofirm the password
        return {
          name: "Ali Raza Khalid",
          email: "[email protected]",
          avatar: "https://dummyimage.com/50X50",
        };
      },
    }),
  ],
  pages: {
    signIn: "/auth/signin",
  },
  callbacks: {
    async session({ session, token }) {
      session.user.id = token.id;
      session.user.name = token.name;
      session.user.email = token.email;
      session.user.avatar = token.avatar;
      return session;
    },
    async jwt({ token, user, trigger, session }) {
      if (user) {
        token.id = user.id;
        token.name = user.name;
        token.email = user.email;
        token.avatar = user.avatar;
      }

      // Handle updates
      if (trigger === "update" && session) {
        // Update the token with the new data
        token.name = session.user.name;
        token.email = session.user.email;
        token.avatar = session.user.avatar;
        console.log(session.user.name);
      }

      return token;
    },
  },
});

Client to update the session:

"use client";

import { signOut, useSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import { useState } from "react";

const UpdateUserInfo = () => {
  const { data: session, update } = useSession();
  const [name, setName] = useState(session?.user?.name || "");
  const [message, setMessage] = useState("");
  // const router = useRouter();

  const handleSubmit = async (e) => {
    e.preventDefault();
    update({
      ...session,
      user: {
        ...session.user,
        name,
      },
    });
    // router.refresh();
  };

  return (
    <div className="flex items-center  flex-col justify-center h-screen  text-white">
      <form
        onSubmit={handleSubmit}
        className="bg-gray-800 p-6 rounded-lg shadow-md space-y-4"
      >
        <h2 className="text-2xl font-semibold mb-4">Update Your Information</h2>

        <div className="flex flex-col">
          <label htmlFor="name" className="mb-2">
            Name
          </label>
          <input
            id="name"
            type="text"
            value={name}
            onChange={(e) => setName(e.target.value)}
            className="p-2 bg-gray-700 text-white rounded focus:outline-none"
          />
        </div>

        <button
          type="submit"
          className="w-full py-2 px-4 bg-indigo-600 hover:bg-indigo-700 rounded-md text-white"
        >
          Update Name
        </button>

        {message && <p className="text-green-500 mt-3">{message}</p>}
      </form>
      {session?.user?.name}
      <button
        onClick={async () => {
          await signOut();
        }}
      >
        SignOut
      </button>
    </div>
  );
};

export default UpdateUserInfo;

Ali-Raza764 avatar Sep 22 '24 04:09 Ali-Raza764

Basically I had to play with the callbacks for correct handling unfortunately I did not found the correct docs for this but it works. I have created a repository https://github.com/Ali-Raza764/authv5 it is a simplest example using credentials sign-in. Note that the using this approach the useSession() will be updated instantly however for server components auth() you will have to reload the page. Instant router.refresh() also does not work as I think some time is required to change it but after a moment you can change route or refresh to get the new information.

Ali-Raza764 avatar Sep 22 '24 04:09 Ali-Raza764

@suitux @monsterooo @TauanAlmeida @linyiru check out this approach and tell me your results my dependencies are the following:

 "dependencies": {
    "next": "14.2.13",
    "next-auth": "^5.0.0-beta.21",
    "react": "^18",
    "react-dom": "^18"
  },

Ali-Raza764 avatar Sep 22 '24 04:09 Ali-Raza764

I'm basically doing the same as you @Ali-Raza764 .

I'm updating the session from the client, but instead of passing the arguments and updating the session with them, I'm doing a Database call and updating the session with the Database.

Working with "next-auth": "^5.0.0-beta.21"

suitux avatar Sep 22 '24 13:09 suitux

It works for me now. Can you share the repo url or code so I can take a look ?

Ali-Raza764 avatar Sep 22 '24 14:09 Ali-Raza764

sorry @Ali-Raza764 but I can't, it's private repo :(

But the auth callbacks looks lihe this:


const authCallbacks = (adapter?: Adapter): AuthConfig['callbacks'] => ({
  async jwt({ token, user, trigger }) {
    if (user) {
      token.userId = user.id!
      token.name = user.name
      token.username = user.username
      token.image = user.image
      token.email = user.email
      token.role = user.role
      token.isInitialized = user.isInitialized
      token.emailVerified = !!user.emailVerified
    }

    if (trigger === 'update') {
      const user = await adapter?.getUser!(token.userId)

      return {
        ...token,
        userId: user!.id,
        email: user!.email,
        role: user!.role,
        emailVerified: user!.emailVerified,
        isInitialized: user!.isInitialized,
        name: user!.name,
        username: user!.username,
        image: user!.image
      } as JWT
    }

    return token
  },
  session({ session, token }) {
    session.user.id = token.userId
    session.user.role = token.role
    session.user.username = token.username
    session.user.isInitialized = token.isInitialized
    session.user.emailVerified = !!token.emailVerified
    session.user.image = token.image

    return session
  }
})


suitux avatar Sep 22 '24 15:09 suitux

Try passing some arguments from the client side session update and recieve them inside the trigger. This will at least make sure if the trigger is working. Then try checking the token here at this part

const user = await adapter?.getUser!(token.userId)
if (trigger === 'update' && session){
   console.log(session)
}

You approach is a little different but I think above can help. I myself got a headache when working with adapters and fetching data inside the auth.js file.

Ali-Raza764 avatar Sep 22 '24 17:09 Ali-Raza764

I will close this issue for now. I don't see any progress here. If your issue doesn't resolve open a fresh one. Really nice to discuss the problem with you helped and learned a lot.

Ali-Raza764 avatar Sep 23 '24 14:09 Ali-Raza764

So unfortunate to see this worthy issue get closed without the devs getting involved. as the problem is currently present ("next-auth": "^5.0.0-beta.28") and the docs for this topic are still incomplete, and yet im unable to update the session server side. guess we have to investigate further ourselves.

aDevNamedShayan avatar May 25 '25 17:05 aDevNamedShayan

Yeah session refresh and updation is a headache in auth.js for now in my projects I try to just logout the user and ask them to sign in again. I did get it working but its not a clean solution random errors pop out sometimes....

Ali-Raza764 avatar May 26 '25 21:05 Ali-Raza764

Guess a simple straight forward example should be made with session updates etc... what are your thoughts?

Ali-Raza764 avatar May 26 '25 21:05 Ali-Raza764