#Unable to authenticate Convex with next auth when provider is Credentials.

1 messages · Page 1 of 1 (latest)

zenith iris
#

I have followed this post to setup the adapter https://stack.convex.dev/nextauth-adapter, and authenticate Convex with next auth, and it works with providers with github and google, but it does not with credentials.

I am able to authenticate in the client side still with credentials, but unable to authenticate the same with Convex. It is not returning any sessions with credentials provider.

Here is my auth.ts

export const {
  handlers: { GET, POST },
  auth,
  signIn,
  signOut,
} = NextAuth({
  pages: {
    signIn: "/auth/login",
    error: "/auth/error",
  },
  events: {
    async linkAccount({ user }) {
      await updateUserEmailVerification(user.id as Id<"users">);
    },
  },
  callbacks: {
    async signIn({ user, account }) {
      if (account?.provider !== "credentials") return true;

      if (!user.email) return false;

      const existingUser = await getUserByEmail(user.email);

      if (!existingUser?.emailVerified) return false;

      return true;
    },
    async session({ session }) {
      console.log(session);
      const privateKey = await importPKCS8(
        process.env.CONVEX_AUTH_PRIVATE_KEY!,
        "RS256"
      );
      const convexToken = await new SignJWT({
        sub: session.userId,
      })
        .setProtectedHeader({ alg: "RS256" })
        .setIssuedAt()
        .setIssuer(CONVEX_SITE_URL)
        .setAudience("convex")
        .setExpirationTime("1h")
        .sign(privateKey);
      return { ...session, convexToken };
    },
  },
  adapter: ConvexAdapter,
  ...authConfig,
});

declare module "next-auth" {
  interface Session {
    convexToken: string;
  }
}

I feel like I need to access the token callback to pass theJWT.
Have I missed something in the docs?

pine fern
#

What do your providers look like?

zenith iris
#
export default {
  providers: [
    Google({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
    Github({
      clientId: process.env.GITHUB_CLIENT_ID,
      clientSecret: process.env.GITHUB_CLIENT_SECRET,
    }),
    Credentials({
      async authorize(credentials) {
        const validatedFields = loginSchema.safeParse(credentials);

        if (validatedFields.success) {
          const { email, password } = validatedFields.data;

          const user = await getUserByEmail(email);

          if (!user || !user.password) return null;

          const passwordsMatch = await bcrypt.compare(password, user.password);

          if (passwordsMatch) return user;
        }
        return null;
      },
    }),
  ],
} satisfies NextAuthConfig;
#

Here you go.

pine fern
#

Doesn't look like this ever creates a user given credentials?

zenith iris
# pine fern Doesn't look like this ever creates a user given credentials?

It does, i guess. getUserByEmail is actually a wrapper of the query being fetched.

export const getUserByEmail = (email: string) => {
  return fetchQuery(api.users.getUserByEmail, {
    email,
  });
};

I have a register form which calls the register action on submit.

export const register = async (values: RegisterSchema) => {
  const validatedFields = registerSchema.safeParse(values);

  if (!validatedFields.success) {
    return { error: "Invdalid fields" };
  }

  const { email, password } = validatedFields.data;
  const hashedPassword = await bcrypt.hash(password, 10);

  try {
    const existingUser = await getUserByEmail(email);

    if (existingUser && existingUser.emailVerified) {
      return { error: "You already have an account with us, please login." };
    }

    if (existingUser && !existingUser.emailVerified) {
      await deleteUnverifiedUser(existingUser._id);
    }

    await createUser(email, hashedPassword);

    const verificationCode = await generateTwoFactorCode(email);

    const res = await sendVerificationEmail(
      verificationCode.email,
      verificationCode.code
    );

    if (res.error) {
      return { error: "Something went wrong!" };
    }

    return { success: "A code has been sent to your email" };
  } catch (error) {
    throw error;
  }
};

Each function is actually a wrapper of the query or mutation being called from convex.

My applicaiton registers the user just like it should, and I can see the data in the convex database, and I am able to login with the same too without any issues.

I just am not able to authenticate it with Convex, so that I can authenticate my Convex functions.

pine fern
#

Does the logic in signIn callback work?

What does the console.log(session); line print?

zenith iris
#

The signIn callback, logs the user and account

USER {
  _creationTime: 1716284082776.5615,
  _id: 'j572z018e0g9t800511wpmwzgh6sh56z',
  email: '...',
  emailVerified: 1716284125046,
  password: '$2a$10$/vA1Y1bDolexsMk0KJErR.o0tDDfOWKZjsnQIDU49vq6YT8CGpLy2',
  id: '53fea5c8-1574-4394-a812-ff86f944526e'
}
ACCOUNT {
  providerAccountId: '53fea5c8-1574-4394-a812-ff86f944526e',
  type: 'credentials',
  provider: 'credentials'
}

But the sessions callback logs null.

#

But it works when I set the session strategy as jwt, and in the jwt callback return the token. I feel like there is something I am misunderstanding here.

pine fern
#

Have you tried add debug: true to NextAuth and see if it prints anything useful?

zenith iris
#

This is what I get after adding debug as true

[auth][debug]: adapter_getSessionAndUser {
  "args": [
    "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwia2lkIjoiSDlkdS1xaE16RXBaUjBhSUhIYVpXUnN0bnBZcnVwY2V2SFN5RHhOc3pfeEgtV0RnNHJPZ2o4M1UzSTRlWU5fYjVHLXdNVHVOOU1zNWtLZFprRGJuV2cifQ..fAWh7EV-iq1MMjSEhPYGzg.qN_eqRC-aycxiHqX1DZ8fY-PAPuV6bXyu7UEKBVDzk1g6sv6OF_SZon6OWZN5pEM5IhKdQByTrx4LmUrWnfv50XHdY1QShL6yvVKjb8s5irWFaPQ_8AMz5kKJqqslIbnm-yn3VyCbBuCxnNsgO0C4Bb85Jv_KcYSGDOdD7w6TVc8I24DStk8bnDgx5_TAc7rZGygH6VF-tEpNNkb5-QapQ.6CK7XCb-jAXCkBCk5SVUFgAZrnZkB_y80xmg4BaZg-Q"
  ]
}
pine fern
#

Hmm, I'm honestly not sure. I'd have to try credentials and see if it works. You could ask on the Auth.js discord/github too.

zenith iris
#

Will be doing that. Thanks for the time tho.

zenith iris
pine fern
#

Ugh that's uber frustrating

#

You can use the JWT strategy with Convex if you need to though.

zenith iris
#

How should I implement that to my existing code? Can you help me with it.