Reputation: 51
Turns out using the beta turbopack causes this bug to happen. I have opened an issue we'll see when they resolve this.
I recently started working on a project in NextJS 13 with the new app directory. I implemented Prisma and connected my MySQL DB then I installed next-auth. The user creation and JWT security all works great but when log in and go to a route that is protected by my middleware exported from the next-auth directory, the browser starts looping an infinite amount of GET requests (see image below) and makes the site irresponsive. It will also return the following error back to me:
"Failed to fetch RSC payload. Falling back to browser navigation. TypeError: NetworkError when attempting to fetch resource."
Removing the middleware.ts file obviously removes the route protection but it also eliminates the GET loop.
If there is anymore data I can supply to assist please let me know.
// Imports
import NextAuth from "next-auth/next";
import prisma from "@/lib/prisma";
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import CredentialsProvider from "next-auth/providers/credentials";
import { type NextAuthOptions } from "next-auth";
// NextAuth handler.
export const authOptions: NextAuthOptions = {
// Set adapter.
adapter: PrismaAdapter(prisma),
// Set secret.
secret: process.env.NEXTAUTH_SECRET,
// Set session strategy.
session: {
strategy: 'jwt'
},
// Set different login providers.
providers:[
CredentialsProvider({
// The name to display on the sign in form (e.g. "Sign in with...")
name: "Credentials",
// `credentials` is used to generate a form on the sign in page.
// You can specify which fields should be submitted, by adding keys to the `credentials` object.
// e.g. domain, username, password, 2FA token, etc.
// You can pass any HTML attribute to the <input> tag through the object.
credentials: {
username: { label: "Email", type: "text", placeholder: "[email protected]" },
password: { label: "Password", type: "password" }
},
async authorize(credentials) {
// Create request to login api
const res = await fetch("http://localhost:3000/api/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
username: credentials?.username,
password: credentials?.password,
}),
});
// Get response from request
const user = await res.json();
if (res.ok && user) {
// If request returns an user, return the user object received.
return user
} else {
// If request does not return an user, return null.
return null
// You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter
}
}
})
],
callbacks: {
// Callback for when a jwt is created or updated.
async jwt({token, user}) {
return({...token,...user});
},
// Callback for when a session is checked.
async session({session, token}) {
// Add token to session.
session.user = token as any;
return session;
}
}
}
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST}
export { default } from 'next-auth/middleware'
export const config = {
matcher: "/settings"
}
// Imports
import { signJwtAccessToken } from "@/lib/jwt";
import prisma from "@/lib/prisma";
import * as bcrypt from 'bcryptjs';
// Interface
interface RequestBody {
username: string;
password: string;
}
// Return route.
export async function POST(request: Request) {
// Get requests body.
const body: RequestBody = await request.json();
// Create const with requested user.
const user = await prisma.user.findFirst({
where: {
email: body.username,
}
});
// If user exists check if password is correct and return the user.
if(user && ( await bcrypt.compare(body.password, user.password))) {
// Remove password from user object in the response.
const {password, ...userWithoutPass} = user
// Create jwt.
const accessToken = signJwtAccessToken(userWithoutPass);
// Combine user with jwt as result.
const result = {
...userWithoutPass,
accessToken,
}
// Return the result as JSON object.
return new Response(JSON.stringify(result));
}
// Return null as JSON object.
else return new Response(JSON.stringify(null));
}
// Imports
import jwt,{ JwtPayload } from "jsonwebtoken";
// Interfaces
interface SignOption {
expiresIn?: string | number,
}
// Default token expiration date.
const DEFAULT_SIGN_OPTION:SignOption={
expiresIn: "1h"
}
// Function to create jwt.
export function signJwtAccessToken(payload: JwtPayload, options: SignOption= DEFAULT_SIGN_OPTION) {
// Get secret key.
const secret_key = process.env.SECRET_KEY;
// Create token.
const token = jwt.sign(payload, secret_key!, options);
// Return the token.
return token;
}
// Function to verify jwt.
export function verifyJwt(token: string) {
try {
// Get secret key.
const secret_key = process.env.SECRET_KEY;
// Verify secret key.
const decoded = jwt.verify(token, secret_key!);
// Return if jwt is valid
return decoded as JwtPayload;
} catch (error) {
// If jwt is not valid, log the error.
console.log(error);
// And return null.
return null;
}
}
I tried the following with no success. They all resulted in the same problem.
Upvotes: 4
Views: 3749