Next.js Discord

Discord Forum

Middleware issue

Unanswered
Rough harvester ant posted this in #help-forum
Open in Discord
Rough harvester antOP
I'm setting up middleware for the first time. I'm using next-auth": "^5.0.0-beta.9". The middleware doesn't run when my middleware.ts file is inside the root directory, but when I put it in the /src folder, it runs (I see the console outputs from auth.config.ts) but none of my pages have styling and it always thinks I'm logged in. Very odd.
//middleware.ts
import NextAuth from "next-auth";
import { authConfig } from "./auth.config";

export default NextAuth(authConfig).auth;
export const config = {
    matcher: ["/((?!register|login|$).*)"]
}


Folder layout:
.
├── src/
│   └── ...
├── middleware.ts
├── auth.ts
└── auth.config.ts

30 Replies

Rough harvester antOP
Other files:
//auth.ts
export async function getUser(username: string): Promise<IUser | null> {
    try {
        const user = await prisma.user.findUnique({
            where: {
                username: username, // This matches the user with the provided email.
            },
        });
        return user as IUser;
    }
    catch (error) {
        console.error("Couldn't retrieve the user. ", error);
    }
    return null;
}
export const { auth, signIn, signOut } = NextAuth({
    ...authConfig,
    providers: [
        Credentials({
            async authorize(credentials) {
                console.log("authorize called with credentials: ", credentials)
                const parsedCredentials = z.object({ username: z.string(), password: z.string().min(6) })
                    .safeParse(credentials);
                console.log("parsedCredentials: ", parsedCredentials)
                if (parsedCredentials.success) {
                    const { username, password } = parsedCredentials.data;
                    const user = await getUser(username);
                    console.log("user: ", user)
                    if (!user) {
                        console.log("User not found.")
                        return null
                    };
                    const passwordsMatch = await bcrypt.compare(password, user.password);
                    if (passwordsMatch) {
                        console.log("Credentials are valid, returning user.", user)
                        return user;
                    } else {
                        console.log("Passwords didn't match.")
                    }
                }
                return null
            }
        })
    ]
})
//auth.config.ts
import type { NextAuthConfig } from "next-auth"

export const authConfig: NextAuthConfig = {
    pages: {
        signIn: "/auth/login",
        newUser: "/auth/register",
        error: "/auth/error",
    },
    callbacks: {
        authorized({ auth, request: { nextUrl } }) {
            const isLoggedIn = !!auth?.user;
            const isInUserArea = nextUrl.pathname.startsWith("/learn");
            if (isInUserArea) {
                if (isLoggedIn) {
                    console.log("Is logged in, and in user area. AUTHORISED")
                    return true
                };
                return false;
            } else if (isLoggedIn) {
                console.log("Is logged in, and not user area. REDIRECTING TO USER AREA")
                return Response.redirect(new URL("/learn", nextUrl))
            };
            console.log("Not logged in, and not user area. AUTHORISED")
            return true;
        }
    },
    providers: [

    ],
}
Rough harvester antOP
Okay, it's now working thanks but now I have another issue - my button type="submit" isn't doing anything (line 9, console.log("handleDispatch called with", { formData }), never appears in the console. I have properly passed down type as a prop to a button element in NewButton)
function SignUpOrInForm({ type }: { type: "signin" | "signup" }) {
    const usernameRef = useRef<HTMLInputElement>(null);
    const passwordRef = useRef<HTMLInputElement>(null);
    const [errorMessage, dispatch] = useFormState(authenticate, undefined);
    const { pending } = useFormStatus();
    console.log("SignUpOrInForm called");
    function handleDispatch(formData: FormData): void {
        console.log("handleDispatch called with", { formData })
        dispatch(formData)
    }
    useEffect(() => {
        usernameRef.current?.focus()
    }, [])
    return (
        <form
            action={handleDispatch}>
            <div className='flex flex-col' style={{ rowGap: spacing.gaps.separateElement }}>
                <h1 style={{ fontFamily: merriweather.style.fontFamily, fontSize: sizing.largestFontSize }}>{type == "signin" ? "Sign into your account" : "Create an account"}</h1>
                <fieldset className='flex flex-col' style={{ rowGap: spacing.gaps.separateElement }}>
                    <NewButton type='submit' aria-disabled={pending} className='w-full' style={{ paddingLeft: spacing.gaps.separateElement, paddingRight: spacing.gaps.separateElement, paddingTop: 14, paddingBottom: 14 }} buttonVariant="black" >
                        Sign In
                        {
                            pending ? <Loader2 className='animate-spin' color='white' size={24} /> : <ArrowRightIcon color='white' />
                        }
                    </NewButton>
                </fieldset>
            </div>
        </form>
    )
}

export default SignUpOrInForm
Siberian
try giving fieldset a name
and then try formData.get("name")
Rough harvester antOP
Still nothing logged in console.
Changes:
handleDispatch
    function handleDispatch(formData: FormData): void {
        console.log(formData.get('fs'))
        dispatch(formData)
    }


fieldset element
<fieldset name='fs'...
Siberian
well
use the actual name
Rough harvester antOP
mbmb
copied wrong
ahaha
All i'm seeing is this
Siberian
temporarly change NewButton to a standard
 <button type="submit">Send</button>
Rough harvester antOP
oh shit that works!!
ok the issue is my button
Siberian
Well I didnt saw the code, so that was the only thing that could be making error
Rough harvester antOP
also every time credentials are wrong I get this long error it's so annoying, if it's null I already put 'invalid credentials' idk what it wants me to do
Siberian
you might just not console log it
Siberian
but my guess its something with this
   const parsedCredentials = z.object({ username: z.string(), password: z.string().min(6) })
                    .safeParse(credentials);
is that something built in?
Rough harvester antOP
Yeah it is
Siberian
what should be in credentials?
callbackUrl only?
@Siberian what should be in `credentials`?
Rough harvester antOP
waittttt
It's now this
authorize called with credentials:  {
  '$ACTION_REF_1': '',
  '$ACTION_1:0': '{"id":"d8c614d32d4bd90ed73aed9370be3b13321d1dee","bound":"$@1"}',
  '$ACTION_1:1': '["$undefined"]',
  '$ACTION_KEY': 'k1806608105',
  username: 'testUser',
  password: 'agd',
  callbackUrl: 'http://localhost:3000/auth/login?callbackUrl=http%3A%2F%2Flocalhost%3A3000%2Flearn'
}
parsedCredentials:  { success: false, error: [Getter] }
[auth][error] CredentialsSignin: Read more at https://errors.authjs.dev#credentialssignin
Siberian
Ok, I think its because you are returning null
If you return an object it will be persisted to the JSON Web Token and the user will be signed in, unless a custom signIn() callback is configured that subsequently rejects it.

If you return null then an error will be displayed advising the user to check their details.

If you throw an Error, the user will be sent to the error page with the error message as a query parameter.
Siberian
So my reccomendation to test for you would be:
-return an object with error message in your Credentials
-try calling it with additional redirect:false parameter at client side