Next.js Discord

Discord Forum

Next-Auth: Credentials Provider failing unexpectetly with custom login page.

Answered
Egyptian Mau posted this in #help-forum
Open in Discord
Egyptian MauOP
Next 14, App Router

I am new to next auth and next.js itself, I am trying to implement a credentials provider that sends creds to a node server and gets a JWT token in return.

My implementation works when using the standard login page next auth generates, but when I try to make a custom one nothing happens when I click log in and I get the error you can see on the screenshot.
Answered by Ray
'use server';

import { signIn } from "../auth";

export async function authenticate(
  prveState: string | undefined,
  formData: FormData
) {
  try {
    const email = formData.get("email");
    const password = formData.get("password");
    await signIn("credentials", { email, password });
  } catch (error) {
    if (error) {
      switch (error.type) {
        case "CredentialsSignin":
          return "Invalid credentials";
        default:
          return "Something went wrong";
      }
    }
  }
}

I think you are missing 'use server' in action.ts
View full answer

9 Replies

Egyptian MauOP
/login page:

"use client";

import React from "react";
import { useFormState, useFormStatus } from "react-dom";
import { authenticate } from "@/actions";

const LoginForm = () => {
  const [errorMessage, dispatch] = useFormState(authenticate, undefined);

  return (
    <>

        <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
          <form className="space-y-6" action={dispatch}>
            <div>
              <label
                htmlFor="email"
                className="block text-sm font-medium leading-6 text-gray-900"
              >
                Email address
              </label>
              <div className="mt-2">
                <input
                  id="email"
                  name="email"
                  type="email"
                  autoComplete="email"
                  required
                />
              </div>
            </div>

            <div>
              <div>
                <label
                  htmlFor="password"
                >
                  Password
                </label>
                <div className="text-sm">
                  <a
                    href="#"
                    className="font-semibold text-indigo-600 hover:text-indigo-500"
                  >
                    Forgot password?
                  </a>
                </div>
              </div>
              <div className="mt-2">
                <input
                  id="password"
                  name="password"
                  type="password"
                  autoComplete="current-password"
                  required/>
              </div>
            </div>
            <div>
              <LoginButton />
            </div>
          </form>
        </div>
      </div>
    </>
  );
};

export default LoginForm;

function LoginButton() {
  const { pending } = useFormStatus();
  return (
    <button
      aria-disabled={pending}
      type="submit"
    >
      Sign in
    </button>
  );
}
auth.ts:
import NextAuth from "next-auth";

import GitHub from "next-auth/providers/github";
import CredentialsProvider from "next-auth/providers/credentials";

import type { NextAuthConfig } from "next-auth";

export const config = {
  theme: {
    logo: "https://next-auth.js.org/img/logo/logo-sm.png",
  },
  pages: {
    signIn: "/login",
  },
  providers: [
    GitHub,
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        email: {
          label: "Email",
          type: "email",
          placeholder: "email@example.com",
        },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials, req) {
        const { email, password } = credentials as any;
        const res = await fetch("http://localhost:5000/api/user/login", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            email,
            password,
          }),
        });
        const data = await res.json();

        if (res.ok && data) {
          return data.user;
        }

        return null;
      },
    }),
  ],
  callbacks: {
    authorized({ request, auth }) {
      const { pathname } = request.nextUrl;
      if (pathname === "/login") return !!auth;
      return true;
    },
  },
} satisfies NextAuthConfig;

export const { handlers, auth, signIn, signOut } = NextAuth(config); 
actions.ts:

import { signIn } from "../auth";

export async function authenticate(
  prveState: string | undefined,
  formData: FormData
) {
  try {
    const email = formData.get("email");
    const password = formData.get("password");
    await signIn("credentials", { email, password });
  } catch (error) {
    if (error) {
      switch (error.type) {
        case "CredentialsSignin":
          return "Invalid credentials";
        default:
          return "Something went wrong";
      }
    }
  }
}
@Egyptian Mau Next 14, App Router I am new to next auth and next.js itself, I am trying to implement a credentials provider that sends creds to a node server and gets a JWT token in return. My implementation works when using the standard login page next auth generates, but when I try to make a custom one nothing happens when I click log in and I get the error you can see on the screenshot.
'use server';

import { signIn } from "../auth";

export async function authenticate(
  prveState: string | undefined,
  formData: FormData
) {
  try {
    const email = formData.get("email");
    const password = formData.get("password");
    await signIn("credentials", { email, password });
  } catch (error) {
    if (error) {
      switch (error.type) {
        case "CredentialsSignin":
          return "Invalid credentials";
        default:
          return "Something went wrong";
      }
    }
  }
}

I think you are missing 'use server' in action.ts
Answer
Egyptian MauOP
That is it, thanks a lot! Can you explain to me, I thought next.js defaults to use server, why do I need to specify it?
because the server action invoke on the client
Egyptian MauOP
Thats fair, thanks a bunch.