admin管理员组文章数量:1333210
I have next-auth set up (in my next.js 13 project with app router), it is working. Now I want to add internationalization to my app as described in the next.js docs. However, I don't know how to bine the two.
middleware working for next-auth
Every route in /user/ should be protected.
export { default } from "next-auth/middleware"
export const config = { matcher: ["/(user/.*)"] }
middleware for next-auth & i18n
This is what I've e up so far. I18n seems to be working, but the routes /user/... are not protected.
import { match } from '@formatjs/intl-localematcher'
import Negotiator from 'negotiator'
import { NextResponse } from 'next/server'
export { default } from "next-auth/middleware"
let locales = ['en', 'de']
let defaultLocale = 'en'
function getLocale(request) {
let headers = { 'accept-language': 'en' }
let languages = new Negotiator({ headers }).languages()
return match(languages, locales, defaultLocale) // -> 'en'
}
export function middleware(request) {
// Check if there is any supported locale in the pathname
const pathname = request.nextUrl.pathname
const pathnameIsMissingLocale = locales.every(
(locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
)
// Redirect if there is no locale
if (pathnameIsMissingLocale) {
const locale = getLocale(request)
// e.g. ining request is /products
// The new URL is now /en/products
return NextResponse.redirect(
new URL(`/${locale}/${pathname}`, request.url)
)
}
}
export const config = {
// '/(\w{2}/user/.*)' from nextauth (\w{2} because of /en/ or /de/); '/((?!_next).*)' from i18n
matcher: ['/(\w{2}/user/.*)', '/((?!_next).*)'],
}
How do I bine the two?
I have next-auth set up (in my next.js 13 project with app router), it is working. Now I want to add internationalization to my app as described in the next.js docs. However, I don't know how to bine the two.
middleware working for next-auth
Every route in /user/ should be protected.
export { default } from "next-auth/middleware"
export const config = { matcher: ["/(user/.*)"] }
middleware for next-auth & i18n
This is what I've e up so far. I18n seems to be working, but the routes /user/... are not protected.
import { match } from '@formatjs/intl-localematcher'
import Negotiator from 'negotiator'
import { NextResponse } from 'next/server'
export { default } from "next-auth/middleware"
let locales = ['en', 'de']
let defaultLocale = 'en'
function getLocale(request) {
let headers = { 'accept-language': 'en' }
let languages = new Negotiator({ headers }).languages()
return match(languages, locales, defaultLocale) // -> 'en'
}
export function middleware(request) {
// Check if there is any supported locale in the pathname
const pathname = request.nextUrl.pathname
const pathnameIsMissingLocale = locales.every(
(locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
)
// Redirect if there is no locale
if (pathnameIsMissingLocale) {
const locale = getLocale(request)
// e.g. ining request is /products
// The new URL is now /en/products
return NextResponse.redirect(
new URL(`/${locale}/${pathname}`, request.url)
)
}
}
export const config = {
// '/(\w{2}/user/.*)' from nextauth (\w{2} because of /en/ or /de/); '/((?!_next).*)' from i18n
matcher: ['/(\w{2}/user/.*)', '/((?!_next).*)'],
}
How do I bine the two?
Share Improve this question asked Sep 13, 2023 at 14:15 JJ77JJ77 4251 gold badge8 silver badges18 bronze badges 4- 1 were you able to resolve it? I'm facing the same problem right now – in43sh Commented Oct 15, 2023 at 6:40
- @in43sh No, sadly I was not yet able to fix that... Please let me know if you figure out a way to do so! I'll keep you posted about any success on my side as well. – JJ77 Commented Oct 15, 2023 at 7:34
- 1 thank you, I also have a question about it, it's almost identical – in43sh Commented Oct 15, 2023 at 7:37
- hey @in43sh, I just figured it out! Hope it helps you too. See below: stackoverflow./a/77524597/17760960 – JJ77 Commented Nov 21, 2023 at 16:55
3 Answers
Reset to default 2Here is how I did it:
middleware.js
export function middleware(request) {
let locales = ['en', 'fr'];
let defaultLocale = 'en';
// Check if there is any supported locale in the pathname
const { pathname } = request.nextUrl;
const isPathAllowed = ['/img', 'img', '/api'].some((allowedPath) =>
pathname.startsWith(`${allowedPath}`),
);
if (isPathAllowed) return;
const pathnameHasLocale = locales.some(
(locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`,
);
if (pathnameHasLocale) return;
// Redirect if there is no locale
const locale = defaultLocale;
request.nextUrl.pathname = `/${locale}${pathname}`;
// e.g. ining request is /products
// The new URL is now /en-US/products
return Response.redirect(request.nextUrl);
}
export const config = {
matcher: [
// Skip all internal paths (_next)
'/((?!_next).*)',
// Optional: only run on root (/) URL
// '/'
],
};
en.json
{
"Index": {
"title": "en title",
"description": "en description"
}
}
fr.json
{
"Index": {
"title": "french title",
"description": "french description"
}
}
messages.js
import en from './en.json';
import fr from './fr.json';
export const getSelectedLanguage = (lang) => {
switch (lang) {
case 'en':
return en;
case 'fr':
return fr;
default:
return null;
}
};
layout.js
'use client';
import '@/app/globals.css';
import { SessionProvider } from 'next-auth/react';
import { NextIntlClientProvider } from 'next-intl';
import { getSelectedLanguage } from '@/internationalization/messages/messages';
export default function RootLayout({ children, params: { lang } }) {
const messages = getSelectedLanguage(lang);
return (
<html lang={lang}>
<body>
<NextIntlClientProvider locale={lang} messages={messages}>
<SessionProvider>{children}</SessionProvider>
</NextIntlClientProvider>
</body>
</html>
);
}
page.jsx
'use client';
import Link from 'next/link';
import { useLocale, useTranslations } from 'next-intl';
const HomePage = () => {
const t = useTranslations('Index');
const locale = useLocale();
return (
<>
<main>
<div className="container">
<h1>{t('title')}</h1>
</div>
</main>
</>
);
}
I just figured it out thanks to the help of @mateen-kiani and phind.! This is how I bined the two middlewares:
import { NextResponse } from 'next/server'
import { match } from '@formatjs/intl-localematcher'
import Negotiator from 'negotiator'
import nextAuthMiddleware from "next-auth/middleware"
let locales = ['en', 'de']
let defaultLocale = 'en'
function getLocale(request) {
let headers = { 'accept-language': 'en' }
let languages = new Negotiator({ headers }).languages()
return match(languages, locales, defaultLocale) // -> 'en'
}
export function middleware(request) {
// cancel if exception
const pathname = request.nextUrl.pathname
const isException = ['/img', '/preview', '/icons', '/logo.svg', '/api', '/manifest.json', '/sw.js'].some((allowedPath) =>
pathname.startsWith(`${allowedPath}`),
);
if (isException) return;
// Check if there is any supported locale in the pathname
const pathnameIsMissingLocale = 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}`, request.url)
)
}
// check if auth is required
if (pathname.includes("/user")) {
// check & handle if logged in
const nextAuthResponse = nextAuthMiddleware(request)
if (nextAuthResponse) {
return nextAuthResponse
}
}
// Continue if no NextAuth middleware response
return NextResponse.next()
}
export const config = {
matcher: [
'/((?!_next).*)',
],
}
After a lot of moving things around I've finally found the better solution that works for me.
"next-auth": "5.0.0-beta.5",
"next-i18n-router": "^5.2.0",
Just use the NextAuth middleware configuration:
middleware.ts
import NextAuth from 'next-auth'
import { authConfig } from './auth.config'
export default NextAuth(authConfig).auth
export const config = {
matcher: '/((?!api|static|.*\\..*|_next).*)'
}
And then mix authorized handler and i18nRouter on the auth.config file:
auth.config.ts
import { i18nRouter } from 'next-i18n-router'
import type { NextAuthConfig } from 'next-auth'
import { i18nConfig } from '@/locale/config'
const getLocaleFromPath = (pathname: string) => {
const localeFromPathRegex = new RegExp(`^/(${i18nConfig.locales.join('|')})?`)
const localeFromPath = pathname.match(localeFromPathRegex)?.[1]
return { locale: localeFromPath, path: localeFromPath ? `/${localeFromPath}` : '' }
}
const checkCurrentRoute = (pathname: string, locale?: string) => {
const checkPathnameRegex = (pattern: string | RegExp) => {
const rootRegex = new RegExp(pattern)
return Boolean(pathname.match(rootRegex))
}
return {
root: checkPathnameRegex(`^/(${locale})?$`),
dashboard: checkPathnameRegex(`^(/${locale})?/dashboard.*`),
login: checkPathnameRegex(`^(/${locale})?/login.*`)
}
}
export const authConfig = {
pages: {
signIn: '/login'
},
callbacks: {
authorized({ auth, request }) {
const { nextUrl } = request
const locale = getLocaleFromPath(nextUrl.pathname)
const dashboardUrl = new URL(`${locale.path}/dashboard`, nextUrl)
const { root: isOnRoot, dashboard: isOnDashboard, login: isOnLogin } = checkCurrentRoute(nextUrl.pathname, locale.locale)
const isLoggedIn = !!auth?.user
if (isOnRoot || (isLoggedIn && !isOnDashboard)) {
// If on root or logged in but not on dashboard, redirect to dashboard
return Response.redirect(dashboardUrl)
}
if ((isOnLogin && !isLoggedIn) || (isOnDashboard && isLoggedIn)) {
// Not logged in but on login OR logged in and on dashboard => allow access
return i18nRouter(request, i18nConfig)
}
// Not logged in and not on login or dashboard => redirect to login page
return false
}
},
providers: []
} satisfies NextAuthConfig
@/locale/config.ts
export const i18nConfig = {
locales: ['en', 'es'],
defaultLocale: 'en'
}
In my case this successfully works. I've extracted the locale from pathname manually, and then reuse it in redirections. If no redirection for not authorized is needed, then return the i18nRouter so i18n is enabled.
For the rest of the config if followed this tutorial: NextAuth tutorial
本文标签: javascriptMiddleware for nextauth and i18n (nextjs 13 with app router)Stack Overflow
版权声明:本文标题:javascript - Middleware for next-auth and i18n (next.js 13 with app router) - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742285568a2446865.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论