QuiiBz / next-international

Type-safe internationalization (i18n) for Next.js
https://next-international.vercel.app
MIT License
1.19k stars 52 forks source link

Chain next-international with auth middleware #369

Open aakash19here opened 4 months ago

aakash19here commented 4 months ago

Describe the bug With the all new v5 of next-auth , how do we actually chain the createi18nMiddleware with auth middleware from it ?

About (please complete the following information):

import { NextResponse } from "next/server"
import { auth } from "./auth"
import { profileCreationRoute, signInRoutes } from "./routes"
import { createI18nMiddleware } from 'next-international/middleware'
import { NextRequest } from 'next/server'
const I18nMiddleware = createI18nMiddleware({
  locales: ['en', 'fr'],
  defaultLocale: 'en',
  urlMappingStrategy: 'rewriteDefault',
})
export function middleware(request: NextRequest) {
  return I18nMiddleware(request)
}
export default auth((req) => {
  const { nextUrl } = req
  const isLoggedIn = req.auth
  const isSignInPage = signInRoutes.includes(nextUrl.pathname)
  const isCreateProfilePage = nextUrl.pathname.startsWith(profileCreationRoute)
  if (isLoggedIn && isCreateProfilePage) {
    if (req.auth?.user?.isProfileCreated) {
      return NextResponse.redirect(new URL("/", nextUrl))
    }
    return NextResponse.next()
  }
  if (isSignInPage && isLoggedIn) {
    return NextResponse.redirect(new URL("/", nextUrl))
  }
  if (!isSignInPage) {
    if (isLoggedIn) {
      if (!req.auth?.user?.isProfileCreated) {
        return NextResponse.redirect(new URL("/create-profile", nextUrl))
      }
      return NextResponse.next()
    } else {
      return NextResponse.redirect(new URL("/sign-in", nextUrl))
    }
  }
  return NextResponse.next()
})
export const config = {
  matcher: ["/((?!.+\.[\w]+$|_next).)", "/", "/(api|trpc)(.)"],
}
iStorry commented 4 months ago

@aakash19here you can stack middleware in nextjs

// middleware-factory.ts

import { NextResponse, type NextMiddleware } from "next/server";

export type MiddlewareFactory = (middleware: NextMiddleware) => NextMiddleware;

export function stackMiddlewares(functions: MiddlewareFactory[] = [], index = 0): NextMiddleware {
  const current = functions[index];
  if (current) {
    const next = stackMiddlewares(functions, index + 1);
    return current(next);
  }
  return () => NextResponse.next();
}
// i18n.ts

const I18nMiddleware = createI18nMiddleware({
  locales: ["en", "ja"],
  defaultLocale: "en",
  urlMappingStrategy: "rewrite",
});

export const withI18n: MiddlewareFactory = (next: NextMiddleware) => {
  return async (request: NextRequest, _next: NextFetchEvent) => {
    await next(request, _next);
    return I18nMiddleware(request);
  };
};
// middleware.ts
const middlewares = [withI18n, withExample];

export default stackMiddlewares(middlewares);

export const config = {
  ....
};
aakash19here commented 4 months ago

Will try and revert back , Thanks for the help !

gustaveWPM commented 4 months ago

@aakash19here you can stack middleware in nextjs ...

Hello!

I would be happy to chain the Next International middleware exactly as I chain the NextAuth one here: https://github.com/Tirraa/dashboard_rtm/blob/main/src/middlewares/withProtectedRoutes.ts#L14

Calling next() before calling the i18n middleware for real breaks the order of the chain. Since we'll first call whatever is next, and then go back to the i18n middleware.

By doing so, writing: [withI18n, withAuth]

Would result in: withAuth -> withI18n

This bothers me: it's not the expected behavior. :/

I'd prefer to have something more naive and predictable: Screenshot from 2024-03-05 06-59-40

Also, I'd prefer to call the i18n middleware before the Auth middleware to make sure that the auth gets the request with the most up-to-date language flag. Otherwise, the Auth middleware might make a mistake and return the wrong language (i.e.: the second-to-last).