Disclaimer: I am using the code in https://github.com/amannn/next-intl/tree/main/examples/example-next-13-next-auth as base point for my implementation.
I am failing to understand why composing next-intl and next-auth results in an infinite loop. I am basing my code on the above example (which I failed to run, tbh, but hope is solid). I had to make a couple of changes, due to how the project's business logic flow:
To do so, I have created the following folder structure:
This is my middleware:
import { NextRequestWithAuth, withAuth } from 'next-auth/middleware';
import createIntlMiddleware from 'next-intl/middleware';
import { NextRequest, NextResponse } from 'next/server';
const locales = ['bg', 'en'];
const intlMiddleware = createIntlMiddleware({
locales,
defaultLocale: 'bg',
});
const authMiddleware = withAuth(
(req: NextRequestWithAuth) => {
const now: any = new Date();
const timestamp = Math.round(now / 1000);
const { expiresAt }: any = req.nextauth.token;
if (timestamp > expiresAt) {
if (!req.nextUrl.pathname.startsWith('/auth')) {
const url = '/auth/signout';
return NextResponse.redirect(new URL(url, req.nextUrl));
}
}
// THIS IS WHERE THE MULTIPLE REDIRECTS HAPPEN
return intlMiddleware(req);
},
{
pages: {
signIn: '/auth/signin',
error: '/auth/error',
signOut: '/auth/signout',
},
},
);
export default function middleware(req: NextRequest) {
return (authMiddleware as any)(req);
}
export const config = {
matcher: ['/((?!api|_next|.*\\..*).*)'],
};
This is my app/layout:
'use client';
import React from 'react';
import { SessionProvider } from 'next-auth/react';
import { BodyStyled } from '@/global/styles';
type RootLayoutTypes = {
children: React.ReactNode,
params: any
}
export default function RootLayout({ children, params: { locale } }: RootLayoutTypes) {
return (
<html lang={locale}>
<BodyStyled>
<SessionProvider>
{children}
</SessionProvider>
</BodyStyled>
</html>
);
}
this is my app/[locale]/layout:
import React from 'react';
import { NextIntlClientProvider } from 'next-intl';
import bg from '@/public/locales/bg/common.json';
import en from '@/public/locales/en/common.json';
type RootLayoutTypes = {
children: React.ReactNode,
params: any
}
export function generateStaticParams() {
return [{ locale: 'bg' }, { locale: 'en' }];
}
export default function RootLayout({ children, params: { locale } }: RootLayoutTypes) {
return (
<NextIntlClientProvider locale={locale} messages={locale === 'bg' ? bg : en}>
{children}
</NextIntlClientProvider>
);
}
This is my app/api/auth/[...nextauth]/route code:
/* eslint-disable no-param-reassign */
import NextAuth, { NextAuthOptions } from 'next-auth';
import jwtDecode from 'jwt-decode';
const authOptions: NextAuthOptions = {
providers: [
{
wellKnown: `${process.env.NEXT_PUBLIC_AUTH_AUTHORITY}/.well-known/openid-configuration`,
authorization: {
params: { scope: 'openid profile' },
},
type: 'oauth',
id: 'customOauth',
clientId: process.env.NEXT_PUBLIC_AUTH_CLIENT_ID,
clientSecret: process.env.NEXT_PUBLIC_AUTH_CLIENT_SECRET,
name: '',
checks: ['pkce', 'state'],
profile(profile) {
return {
id: profile.sub,
permissions: profile.permissions,
};
},
},
],
secret: process.env.NEXTAUTH_SECRET,
session: {
strategy: 'jwt',
},
callbacks: {
async redirect() {
return '/dashboard';
},
async jwt({ token, account }) {
// Persist the OAuth access_token and or the user id to the token right after signin
if (account) {
const { exp }: any = jwtDecode(account.id_token || '');
token.accessToken = account.access_token;
token.idToken = account.id_token;
token.expiresAt = exp;
}
return token;
},
},
pages: {
signIn: '/auth/signin',
},
debug: true,
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST }
and this is the app/auth/signin page:
'use client';
import React, { useEffect } from 'react';
import { signIn, useSession } from 'next-auth/react';
import { useRouter } from 'next/navigation';
interface LoginProps {
}
const Login: React.FC<LoginProps> = () => {
const router = useRouter();
const { status } = useSession();
useEffect(() => {
if (status === 'unauthenticated') {
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
if (urlParams.get('error')) {
void router.push('/auth/error');
} else {
void signIn('customOauth', undefined, { prompt: 'select_account' });
}
} else if (status === 'authenticated') {
void router.push('/dashboard');
}
}, [status, router]);
return null;
};
export default Login;
What am I missing? Any help will be greatly appreciated. Been on this s**t for 3 weeks now, i would love to move on...
Turns out I had this configuration in next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
/** error: */
i18n: {
locales: ['bg', 'en'],
defaultLocale: 'bg',
},
/** end error */
async redirects() {
return [
{
source: '/',
destination: '/dashboard',
permanent: true,
},
];
},
};
module.exports = nextConfig;
You should NOT specify i18n when using next-intl. This gave the redirect error.