Next.js Discord

Discord Forum

unable to use next-auth with i18n

Unanswered
Naeem posted this in #help-forum
Open in Discord
Avatar
NaeemOP
I can't use next-auth and i18n together because of a middleware issue.
I've to use this for next-auth:
export { default } from "next-auth/middleware"

export const config = { matcher: ["/dashboard"] }

And this for i18n:
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

import { i18n } from '@/i18n.config'

import { match as matchLocale } from '@formatjs/intl-localematcher'
import Negotiator from 'negotiator'

function getLocale(request: NextRequest): string | undefined {
  const negotiatorHeaders: Record<string, string> = {}
  request.headers.forEach((value, key) => (negotiatorHeaders[key] = value))

  // @ts-ignore locales are readonly
  const locales: string[] = i18n.locales
  const languages = new Negotiator({ headers: negotiatorHeaders }).languages()

  const locale = matchLocale(languages, locales, i18n.defaultLocale)
  return locale
}

export function middleware(request: NextRequest) {
  const pathname = request.nextUrl.pathname
  const pathnameIsMissingLocale = i18n.locales.every(
    locale => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
  )

  // Redirect if there is no locale
  if (pathnameIsMissingLocale) {
    const locale = getLocale(request)
    return NextResponse.redirect(
      new URL(
        `/${locale}${pathname.startsWith('/') ? '' : '/'}${pathname}`,
        request.url
      )
    )
  }
}
export const config = {
  // Matcher ignoring `/_next/` and `/api/`
  matcher: ['/((?!api|_next/static|_next/image|images).*)']
}

when I combine both middleware next-auth protected routes aren't working, I can still go to /dashboard without being logged in.

8 Replies

Avatar
NaeemOP
Anyone...
Avatar
fuma thinks joulev is rational
Next Auth provides a withAuth function which allows you to wrap an existing middleware inside.
Hence, try the following:
import { withAuth } from "next-auth/middleware"
const middleware = withAuth((req) => ...)
export default middleware;
Next.js doesn't support multiple middlewares, you must combine them into a middleware in this case
Avatar
NaeemOP
Okay got it but what about Matcher???
I got negative loop regex for i18n and for next-auth just protected routes array...
import {withAuth} from "next-auth/middleware"
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

import { i18n } from '@/i18n.config'

import { match as matchLocale } from '@formatjs/intl-localematcher'
import Negotiator from 'negotiator'

function getLocale(request: NextRequest): string | undefined {
  const negotiatorHeaders: Record<string, string> = {}
  request.headers.forEach((value, key) => (negotiatorHeaders[key] = value))

  // @ts-ignore locales are readonly
  const locales: string[] = i18n.locales
  const languages = new Negotiator({ headers: negotiatorHeaders }).languages()

  const locale = matchLocale(languages, locales, i18n.defaultLocale)
  return locale
}

const middleware = withAuth((request: NextRequest)=> {
  const pathname = request.nextUrl.pathname
  const pathnameIsMissingLocale = i18n.locales.every(
    locale => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
  )

  // Redirect if there is no locale
  if (pathnameIsMissingLocale) {
    const locale = getLocale(request)
    return NextResponse.redirect(
      new URL(
        `/${locale}${pathname.startsWith('/') ? '' : '/'}${pathname}`,
        request.url
      )
    )
  }
})
export default middleware;

what about this
for i18n:
export const config = {
  // Matcher ignoring `/_next/` and `/api/`
  matcher: ['/((?!api|_next/static|_next/image|images).*)']
}
for next-auth:
export const config = { matcher: ["/dashboard"] }
Avatar
NaeemOP
@fuma thinks joulev is rational ?
Avatar
fuma thinks joulev is rational
You may combine the matcher if they both share the same routes, but seems in your case, you only wanna protect the dashboard page.

Actually withAuth also offers a simple method to achieve it, you can use withAuth in this way:

if (request.nextUrl.pathname === "/dashboard")
    return withAuth(request)


so that instead of wrapping your middleware inside of withAuth, you can use it as a function
Check its type definitions for other usages