QuiiBz / next-international

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

Usage together with next-auth #94

Closed cor-emble closed 1 year ago

cor-emble commented 1 year ago

Hi!

We're in a process of migrating an older Next.js app with the pages dir to the new App router and are also moving from i18next to next-international.

The app uses Next auth for the authentication layer and besides the login and register pages all other pages need authentication. So we're using the next auth middleware as follows:

import { withAuth } from "next-auth/middleware";

export const config = {
  matcher: ["/((?!api|user|static|favicon.ico).*)", "/"],
};
export default withAuth({
  pages: {
    signIn: "/user/login",
  },
});

Does somebody has an example on how to combine this middleware with the next-international middleware?

QuiiBz commented 1 year ago

I think you'll have to compose yourself the middleware, something like (not tested):

import { withAuth } from "next-auth/middleware";
import { createI18nMiddleware } from 'next-international/middleware'
import { NextRequest } from 'next/server'

const I18nMiddleware = createI18nMiddleware(['en', 'fr'] as const, 'fr')
const NextAuthMiddleware = withAuth({
  pages: {
    signIn: "/user/login",
  },
});

export const config = {
  matcher: ["/((?!api|user|static|favicon.ico).*)", "/"],
};

export default async function middleware(request) {
  const i18nResponse = I18nMiddleware(request);
  const nextAuthResponse = NextAuthMiddleware(request);

  nextAuthResponse.cookies.add(i18nResponse.cookies.get('Next-Locale'))
  nextAuthResponse.headers.set('X-Next-Locale', i18nResponse.headers.get('X-Next-Locale'))

  return nextAuthResponse;
}
diogomadeira-dev commented 1 year ago

i have the same problem... help?

// middleware.ts
import { withAuth } from 'next-auth/middleware'
import { createI18nMiddleware } from 'next-international/middleware'
import { NextRequest } from 'next/server'

const I18nMiddleware = createI18nMiddleware(['pt', 'en'] as const, 'en', {
  urlMappingStrategy: 'rewrite',
})

export const middleware = (request: NextRequest) => I18nMiddleware(request)

// eslint-disable-next-line @typescript-eslint/no-empty-function
export default withAuth(() => {}, {
  callbacks: {
    authorized: ({ req, token }: any) => {
      const path = req.nextUrl.pathname

      if (token?.error === 'RefreshAccessTokenError') return false

      if (path.startsWith('/admin')) {
        return token && token.roles && token.roles.includes('ADMIN')
      }

      return token !== null
    },
  },
})

export const config = {
  matcher: [
    '/((?!api|static|.*\\..*|_next|favicon.ico|robots.txt).*)',
    '/admin/:path*',
  ],
}
g3rardogo commented 1 year ago

Need some help with this too :/

QuiiBz commented 1 year ago

Can anyone provide a minimal reproduction? I'm not familiar with next-auth using the App Router but it should be possible.

zackrw commented 1 year ago

I don't use next-auth but I helped a friend using this code (not tested yet, reduced for minimalism)

import { withAuth } from "next-auth/middleware"
import { createI18nMiddleware } from 'next-international/middleware'

const I18nMiddleware = createI18nMiddleware({
    locales: ['en', 'fr'],
    defaultLocale: 'en',
})

export default withAuth(
    function middleware(req) {
        return I18nMiddleware(req)
    }
)

export const config = {
    matcher: ['/((?!api|static|.*\\..*|_next|favicon.ico|robots.txt).*)'],
}
Talent30 commented 1 year ago

Tested working code here, GL with the typing issue...

import { withAuth } from 'next-auth/middleware';
import { createI18nMiddleware } from 'next-international/middleware';
import type { NextRequest } from 'next/server';

const i18nMiddleware = createI18nMiddleware({
  locales: ['en'],
  defaultLocale: 'en',
});

const authMiddleware = withAuth((requestWithAuth) =>
  i18nMiddleware(requestWithAuth),
);
export default async function middleware(request: NextRequest) {
  return request.nextUrl.pathname.includes('/admin')
    ? // @ts-expect-error This is a typing issue that I have no idea how to resolve, but the code works...
      authMiddleware(request)
    : i18nMiddleware(request);
}

export const config = {
  matcher: ['/((?!api|static|.*\\..*|_next|favicon.ico|robots.txt).*)'],
};
RicardoAvans commented 1 year ago

In order to have everything (except the api routes and statics) protected, the following middleware works:

import type { NextFetchEvent, NextRequest } from "next/server";
import { createI18nMiddleware } from "next-international/middleware";
import { NextRequestWithAuth, withAuth } from "next-auth/middleware";

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

const authMiddleware = withAuth((requestWithAuth) =>
  I18nMiddleware(requestWithAuth),
);

export default async function middleware(
  request: NextRequest,
  event: NextFetchEvent,
) {
  return authMiddleware(request as NextRequestWithAuth, event);
}

export const config = {
  // Matcher ignoring `/_next/` and `/api/`
  matcher: ["/((?!api|_next/static|_next/image|favicon.ico|robots.txt).*)"],
};
cor-emble commented 1 year ago

Thnx all for the examples!

This is what i came up with and works in my case:

import { withAuth } from "next-auth/middleware";
import { createI18nMiddleware } from "next-international/middleware";

export const config = {
  matcher: ["/((?!api|admin|auth|404|assets|favicon.ico).*)", "/"],
};
const i18nMiddleware = createI18nMiddleware({
  locales: ["en", "nl"],
  defaultLocale: "nl",
  urlMappingStrategy: "rewrite",
});

export default withAuth(
  function middleware(req) {
    return i18nMiddleware(req);
  },
  {
    pages: {
      signIn: "/nl/user/login",
    },
  },
);
QuiiBz commented 1 year ago

Thanks everyone for sharing code examples! I think we can close the issue now, feel free to open a new one if needed.